1 // dear imgui, v1.50
2 // (main code and documentation)
3 
4 // See ImGui::ShowTestWindow() in imgui_demo.cpp for demo code.
5 // Newcomers, read 'Programmer guide' below for notes on how to setup ImGui in your codebase.
6 // Get latest version at https://github.com/ocornut/imgui
7 // Releases change-log at https://github.com/ocornut/imgui/releases
8 // Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/772
9 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
10 // This library is free but I need your support to sustain development and maintenance.
11 // If you work for a company, please consider financial support, e.g: https://www.patreon.com/imgui
12 
13 /*
14 
15  Index
16  - MISSION STATEMENT
17  - END-USER GUIDE
18  - PROGRAMMER GUIDE (read me!)
19  - API BREAKING CHANGES (read me when you update!)
20  - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
21    - How can I help?
22    - How do I update to a newer version of ImGui?
23    - What is ImTextureID and how do I display an image?
24    - I integrated ImGui in my engine and the text or lines are blurry..
25    - I integrated ImGui in my engine and some elements are clipping or disappearing when I move windows around..
26    - How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on the purpose of labels/IDs.
27    - How can I tell when ImGui wants my mouse/keyboard inputs and when I can pass them to my application?
28    - How can I load a different font than the default?
29    - How can I easily use icons in my application?
30    - How can I load multiple fonts?
31    - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
32    - How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
33  - ISSUES & TODO-LIST
34  - CODE
35 
36 
37  MISSION STATEMENT
38  =================
39 
40  - easy to use to create code-driven and data-driven tools
41  - easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools
42  - easy to hack and improve
43  - minimize screen real-estate usage
44  - minimize setup and maintenance
45  - minimize state storage on user side
46  - portable, minimize dependencies, run on target (consoles, phones, etc.)
47  - efficient runtime (NB- we do allocate when "growing" content - creating a window / opening a tree node for the first time, etc. - but a typical frame won't allocate anything)
48  - read about immediate-mode gui principles @ http://mollyrocket.com/861, http://mollyrocket.com/forums/index.html
49 
50  Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
51  - doesn't look fancy, doesn't animate
52  - limited layout features, intricate layouts are typically crafted in code
53  - occasionally uses statically sized buffers for string manipulations - won't crash, but some very long pieces of text may be clipped. functions like ImGui::TextUnformatted() don't have such restriction.
54 
55 
56  END-USER GUIDE
57  ==============
58 
59  - double-click title bar to collapse window
60  - click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin()
61  - click and drag on lower right corner to resize window
62  - click and drag on any empty space to move window
63  - double-click/double-tap on lower right corner grip to auto-fit to content
64  - TAB/SHIFT+TAB to cycle through keyboard editable fields
65  - use mouse wheel to scroll
66  - use CTRL+mouse wheel to zoom window contents (if IO.FontAllowScaling is true)
67  - CTRL+Click on a slider or drag box to input value as text
68  - text editor:
69    - Hold SHIFT or use mouse to select text.
70    - CTRL+Left/Right to word jump
71    - CTRL+Shift+Left/Right to select words
72    - CTRL+A our Double-Click to select all
73    - CTRL+X,CTRL+C,CTRL+V to use OS clipboard
74    - CTRL+Z,CTRL+Y to undo/redo
75    - ESCAPE to revert text to its original value
76    - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
77 
78 
79  PROGRAMMER GUIDE
80  ================
81 
82  - read the FAQ below this section!
83  - your code creates the UI, if your code doesn't run the UI is gone! == very dynamic UI, no construction/destructions steps, less data retention on your side, no state duplication, less sync, less bugs.
84  - call and read ImGui::ShowTestWindow() for demo code demonstrating most features.
85  - see examples/ folder for standalone sample applications. Prefer reading examples/opengl2_example/ first as it is the simplest.
86    you may be able to grab and copy a ready made imgui_impl_*** file from the examples/.
87  - customization: PushStyleColor()/PushStyleVar() or the style editor to tweak the look of the interface (e.g. if you want a more compact UI or a different color scheme).
88 
89  - getting started:
90    - init: call ImGui::GetIO() to retrieve the ImGuiIO structure and fill the fields marked 'Settings'.
91    - init: call io.Fonts->GetTexDataAsRGBA32(...) and load the font texture pixels into graphics memory.
92    - every frame:
93       1/ in your mainloop or right after you got your keyboard/mouse info, call ImGui::GetIO() and fill the fields marked 'Input'
94       2/ call ImGui::NewFrame() as early as you can!
95       3/ use any ImGui function you want between NewFrame() and Render()
96       4/ call ImGui::Render() as late as you can to end the frame and finalize render data. it will call your RenderDrawListFn handler that you set in the IO structure.
97          (if you don't need to render, you still need to call Render() and ignore the callback, or call EndFrame() instead. if you call neither some aspects of windows focusing/moving will appear broken.)
98    - all rendering information are stored into command-lists until ImGui::Render() is called.
99    - ImGui never touches or know about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide.
100    - effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases of your own application.
101    - refer to the examples applications in the examples/ folder for instruction on how to setup your code.
102    - a typical application skeleton may be:
103 
104         // Application init
105         ImGuiIO& io = ImGui::GetIO();
106         io.DisplaySize.x = 1920.0f;
107         io.DisplaySize.y = 1280.0f;
108         io.IniFilename = "imgui.ini";
109         io.RenderDrawListsFn = my_render_function;  // Setup a render function, or set to NULL and call GetDrawData() after Render() to access the render data.
110         // TODO: Fill others settings of the io structure
111 
112         // Load texture atlas
113         // There is a default font so you don't need to care about choosing a font yet
114         unsigned char* pixels;
115         int width, height;
116         io.Fonts->GetTexDataAsRGBA32(pixels, &width, &height);
117         // TODO: At this points you've got a texture pointed to by 'pixels' and you need to upload that your your graphic system
118         // TODO: Store your texture pointer/identifier (whatever your engine uses) in 'io.Fonts->TexID'
119 
120         // Application main loop
121         while (true)
122         {
123             // 1) get low-level inputs (e.g. on Win32, GetKeyboardState(), or poll your events, etc.)
124             // TODO: fill all fields of IO structure and call NewFrame
125             ImGuiIO& io = ImGui::GetIO();
126             io.DeltaTime = 1.0f/60.0f;
127             io.MousePos = mouse_pos;
128             io.MouseDown[0] = mouse_button_0;
129             io.MouseDown[1] = mouse_button_1;
130             io.KeysDown[i] = ...
131 
132             // 2) call NewFrame(), after this point you can use ImGui::* functions anytime
133             ImGui::NewFrame();
134 
135             // 3) most of your application code here
136             MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
137             MyGameRender(); // may use any ImGui functions
138 
139             // 4) render & swap video buffers
140             ImGui::Render();
141             SwapBuffers();
142         }
143 
144    - You can read back 'io.WantCaptureMouse', 'io.WantCaptureKeybord' etc. flags from the IO structure to tell how ImGui intends to use your
145      inputs and to know if you should share them or hide them from the rest of your application. Read the FAQ below for more information.
146 
147 
148  API BREAKING CHANGES
149  ====================
150 
151  Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix.
152  Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code.
153  Also read releases logs https://github.com/ocornut/imgui/releases for more details.
154 
155  - 2017/05/26 (1.50) - Removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
156  - 2017/05/01 (1.50) - Renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
157  - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetId() and use it instead of passing string to BeginChild().
158  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
159  - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc.
160  - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully breakage should be minimal.
161  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
162                        If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you.
163                        However if your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
164                        This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color.
165                            ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
166                            {
167                                float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
168                                return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a);
169                            }
170                        If this is confusing, pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color.
171  - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
172  - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
173  - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen).
174  - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDraw::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer.
175  - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref github issue #337).
176  - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337)
177  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
178  - 2016/03/02 (1.48) - InputText() completion/history/always callbacks: if you modify the text buffer manually (without using DeleteChars()/InsertChars() helper) you need to maintain the BufTextLen field. added an assert.
179  - 2016/01/23 (1.48) - fixed not honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. if you had manual pixel-perfect alignment in place it might affect you.
180  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
181  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
182  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
183                        GetCursorPos()/SetCursorPos() functions now include the scrolled amount. It shouldn't affect the majority of users, but take note that SetCursorPosX(100.0f) puts you at +100 from the starting x position which may include scrolling, not at +100 from the window left side.
184                        GetContentRegionMax()/GetWindowContentRegionMin()/GetWindowContentRegionMax() functions allow include the scrolled amount. Typically those were used in cases where no scrolling would happen so it may not be a problem, but watch out!
185  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
186  - 2015/08/05 (1.44) - split imgui.cpp into extra files: imgui_demo.cpp imgui_draw.cpp imgui_internal.h that you need to add to your project.
187  - 2015/07/18 (1.44) - fixed angles in ImDrawList::PathArcTo(), PathArcToFast() (introduced in 1.43) being off by an extra PI for no justifiable reason
188  - 2015/07/14 (1.43) - add new ImFontAtlas::AddFont() API. For the old AddFont***, moved the 'font_no' parameter of ImFontAtlas::AddFont** functions to the ImFontConfig structure.
189                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
190  - 2015/07/08 (1.43) - switched rendering data to use indexed rendering. this is saving a fair amount of CPU/GPU and enables us to get anti-aliasing for a marginal cost.
191                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
192                      - if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
193                      - the signature of the io.RenderDrawListsFn handler has changed!
194                             ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
195                        became:
196                             ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
197                               argument   'cmd_lists'        -> 'draw_data->CmdLists'
198                               argument   'cmd_lists_count'  -> 'draw_data->CmdListsCount'
199                               ImDrawList 'commands'         -> 'CmdBuffer'
200                               ImDrawList 'vtx_buffer'       -> 'VtxBuffer'
201                               ImDrawList  n/a               -> 'IdxBuffer' (new)
202                               ImDrawCmd  'vtx_count'        -> 'ElemCount'
203                               ImDrawCmd  'clip_rect'        -> 'ClipRect'
204                               ImDrawCmd  'user_callback'    -> 'UserCallback'
205                               ImDrawCmd  'texture_id'       -> 'TextureId'
206                      - each ImDrawList now contains both a vertex buffer and an index buffer. For each command, render ElemCount/3 triangles using indices from the index buffer.
207                      - if you REALLY cannot render indexed primitives, you can call the draw_data->DeIndexAllBuffers() method to de-index the buffers. This is slow and a waste of CPU/GPU. Prefer using indexed rendering!
208                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
209  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
210  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
211  - 2015/07/02 (1.42) - renamed GetScrollPosY() to GetScrollY(). Necessary to reduce confusion along with other scrolling functions, because positions (e.g. cursor position) are not equivalent to scrolling amount.
212  - 2015/06/14 (1.41) - changed ImageButton() default bg_col parameter from (0,0,0,1) (black) to (0,0,0,0) (transparent) - makes a difference when texture have transparence
213  - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry!
214  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
215  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
216  - 2015/05/27 (1.40) - removed the third 'repeat_if_held' parameter from Button() - sorry! it was rarely used and inconsistent. Use PushButtonRepeat(true) / PopButtonRepeat() to enable repeat on desired buttons.
217  - 2015/05/11 (1.40) - changed BeginPopup() API, takes a string identifier instead of a bool. ImGui needs to manage the open/closed state of popups. Call OpenPopup() to actually set the "open" state of a popup. BeginPopup() returns true if the popup is opened.
218  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
219  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
220  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
221  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
222  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
223  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
224  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
225  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
226  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
227  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
228  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
229  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
230  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
231  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
232  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
233  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
234  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
235               (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
236                        this sequence:
237                            const void* png_data;
238                            unsigned int png_size;
239                            ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size);
240                            // <Copy to GPU>
241                        became:
242                            unsigned char* pixels;
243                            int width, height;
244                            io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
245                            // <Copy to GPU>
246                            io.Fonts->TexID = (your_texture_identifier);
247                        you now have much more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
248                        it is now recommended that you sample the font texture with bilinear interpolation.
249               (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
250               (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
251               (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
252  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
253  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
254  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
255  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
256  - 2014/10/02 (1.14) - renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL and imgui_user.cpp to imgui_user.inl (more IDE friendly)
257  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
258  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
259  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
260  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
261  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
262  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
263 
264 
265  FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
266  ======================================
267 
268  Q: How can I help?
269  A: - If you are experienced enough with ImGui and with C/C++, look at the todo list and see how you want/can help!
270     - Become a Patron/donate. Convince your company to become a Patron or provide serious funding for development time.
271 
272  Q: How do I update to a newer version of ImGui?
273  A: Overwrite the following files:
274       imgui.cpp
275       imgui.h
276       imgui_demo.cpp
277       imgui_draw.cpp
278       imgui_internal.h
279       stb_rect_pack.h
280       stb_textedit.h
281       stb_truetype.h
282     Don't overwrite imconfig.h if you have made modification to your copy.
283     If you have a problem with a missing function/symbols, search for its name in the code, there will likely be a comment about it.
284     Check the "API BREAKING CHANGES" sections for a list of occasional API breaking changes.
285     Please report any issue to the GitHub page!
286 
287  Q: What is ImTextureID and how do I display an image?
288  A: ImTextureID is a void* used to pass renderer-agnostic texture references around until it hits your render function.
289     ImGui knows nothing about what those bits represent, it just passes them around. It is up to you to decide what you want the void* to carry!
290     It could be an identifier to your OpenGL texture (cast GLuint to void*), a pointer to your custom engine material (cast MyMaterial* to void*), etc.
291     At the end of the chain, your renderer takes this void* to cast it back into whatever it needs to select a current texture to render.
292     Refer to examples applications, where each renderer (in a imgui_impl_xxxx.cpp file) is treating ImTextureID as a different thing.
293     (c++ tip: OpenGL uses integers to identify textures. You can safely store an integer into a void*, just cast it to void*, don't take it's address!)
294     To display a custom image/texture within an ImGui window, you may use ImGui::Image(), ImGui::ImageButton(), ImDrawList::AddImage() functions.
295     ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use.
296     It is your responsibility to get textures uploaded to your GPU.
297 
298  Q: I integrated ImGui in my engine and the text or lines are blurry..
299  A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
300     Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
301 
302  Q: I integrated ImGui in my engine and some elements are clipping or disappearing when I move windows around..
303  A: Most likely you are mishandling the clipping rectangles in your render function. Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
304 
305  Q: Can I have multiple widgets with the same label? Can I have widget without a label? (Yes)
306  A: Yes. A primer on the use of labels/IDs in ImGui..
307 
308    - Elements that are not clickable, such as Text() items don't need an ID.
309 
310    - Interactive widgets require state to be carried over multiple frames (most typically ImGui often needs to remember what is the "active" widget).
311      to do so they need a unique ID. unique ID are typically derived from a string label, an integer index or a pointer.
312 
313        Button("OK");        // Label = "OK",     ID = hash of "OK"
314        Button("Cancel");    // Label = "Cancel", ID = hash of "Cancel"
315 
316    - ID are uniquely scoped within windows, tree nodes, etc. so no conflict can happen if you have two buttons called "OK" in two different windows
317      or in two different locations of a tree.
318 
319    - If you have a same ID twice in the same location, you'll have a conflict:
320 
321        Button("OK");
322        Button("OK");           // ID collision! Both buttons will be treated as the same.
323 
324      Fear not! this is easy to solve and there are many ways to solve it!
325 
326    - When passing a label you can optionally specify extra unique ID information within string itself. This helps solving the simpler collision cases.
327      use "##" to pass a complement to the ID that won't be visible to the end-user:
328 
329        Button("Play");         // Label = "Play",   ID = hash of "Play"
330        Button("Play##foo1");   // Label = "Play",   ID = hash of "Play##foo1" (different from above)
331        Button("Play##foo2");   // Label = "Play",   ID = hash of "Play##foo2" (different from above)
332 
333    - If you want to completely hide the label, but still need an ID:
334 
335        Checkbox("##On", &b);   // Label = "",       ID = hash of "##On" (no label!)
336 
337    - Occasionally/rarely you might want change a label while preserving a constant ID. This allows you to animate labels.
338      For example you may want to include varying information in a window title bar (and windows are uniquely identified by their ID.. obviously)
339      Use "###" to pass a label that isn't part of ID:
340 
341        Button("Hello###ID";   // Label = "Hello",  ID = hash of "ID"
342        Button("World###ID";   // Label = "World",  ID = hash of "ID" (same as above)
343 
344        sprintf(buf, "My game (%f FPS)###MyGame");
345        Begin(buf);            // Variable label,   ID = hash of "MyGame"
346 
347    - Use PushID() / PopID() to create scopes and avoid ID conflicts within the same Window.
348      This is the most convenient way of distinguishing ID if you are iterating and creating many UI elements.
349      You can push a pointer, a string or an integer value. Remember that ID are formed from the concatenation of everything in the ID stack!
350 
351        for (int i = 0; i < 100; i++)
352        {
353          PushID(i);
354          Button("Click");   // Label = "Click",  ID = hash of integer + "label" (unique)
355          PopID();
356        }
357 
358        for (int i = 0; i < 100; i++)
359        {
360          MyObject* obj = Objects[i];
361          PushID(obj);
362          Button("Click");   // Label = "Click",  ID = hash of pointer + "label" (unique)
363          PopID();
364        }
365 
366        for (int i = 0; i < 100; i++)
367        {
368          MyObject* obj = Objects[i];
369          PushID(obj->Name);
370          Button("Click");   // Label = "Click",  ID = hash of string + "label" (unique)
371          PopID();
372        }
373 
374    - More example showing that you can stack multiple prefixes into the ID stack:
375 
376        Button("Click");     // Label = "Click",  ID = hash of "Click"
377        PushID("node");
378        Button("Click");     // Label = "Click",  ID = hash of "node" + "Click"
379          PushID(my_ptr);
380            Button("Click"); // Label = "Click",  ID = hash of "node" + ptr + "Click"
381          PopID();
382        PopID();
383 
384    - Tree nodes implicitly creates a scope for you by calling PushID().
385 
386        Button("Click");     // Label = "Click",  ID = hash of "Click"
387        if (TreeNode("node"))
388        {
389          Button("Click");   // Label = "Click",  ID = hash of "node" + "Click"
390          TreePop();
391        }
392 
393    - When working with trees, ID are used to preserve the open/close state of each tree node.
394      Depending on your use cases you may want to use strings, indices or pointers as ID.
395       e.g. when displaying a single object that may change over time (1-1 relationship), using a static string as ID will preserve your node open/closed state when the targeted object change.
396       e.g. when displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state differently. experiment and see what makes more sense!
397 
398  Q: How can I tell when ImGui wants my mouse/keyboard inputs and when I can pass them to my application?
399  A: You can read the 'io.WantCaptureXXX' flags in the ImGuiIO structure. Preferably read them after calling ImGui::NewFrame() to avoid those flags lagging by one frame, but either should be fine.
400     When 'io.WantCaptureMouse' or 'io.WantCaptureKeyboard' flags are set you may want to discard/hide the inputs from the rest of your application.
401     When 'io.WantInputsCharacters' is set to may want to notify your OS to popup an on-screen keyboard, if available.
402     ImGui is tracking dragging and widget activity that may occur outside the boundary of a window, so 'io.WantCaptureMouse' is a more accurate and complete than testing for ImGui::IsMouseHoveringAnyWindow().
403     (Advanced note: text input releases focus on Return 'KeyDown', so the following Return 'KeyUp' event that your application receive will typically have 'io.WantcaptureKeyboard=false'.
404      Depending on your application logic it may or not be inconvenient. You might want to track which key-downs were for ImGui (e.g. with an array of bool) and filter out the corresponding key-ups.)
405 
406  Q: How can I load a different font than the default? (default is an embedded version of ProggyClean.ttf, rendered at size 13)
407  A: Use the font atlas to load the TTF file you want:
408 
409       ImGuiIO& io = ImGui::GetIO();
410       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
411       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
412 
413  Q: How can I easily use icons in my application?
414  A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you main font. Then you can refer to icons within your strings.
415     Read 'How can I load multiple fonts?' and the file 'extra_fonts/README.txt' for instructions.
416 
417  Q: How can I load multiple fonts?
418  A: Use the font atlas to pack them into a single texture:
419     (Read extra_fonts/README.txt and the code in ImFontAtlas for more details.)
420 
421       ImGuiIO& io = ImGui::GetIO();
422       ImFont* font0 = io.Fonts->AddFontDefault();
423       ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
424       ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
425       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
426       // the first loaded font gets used by default
427       // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
428 
429       // Options
430       ImFontConfig config;
431       config.OversampleH = 3;
432       config.OversampleV = 1;
433       config.GlyphExtraSpacing.x = 1.0f;
434       io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config);
435 
436       // Combine multiple fonts into one (e.g. for icon fonts)
437       ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
438       ImFontConfig config;
439       config.MergeMode = true;
440       io.Fonts->AddFontDefault();
441       io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font
442       io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs
443 
444  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
445  A: When loading a font, pass custom Unicode ranges to specify the glyphs to load.
446     All your strings needs to use UTF-8 encoding. Specifying literal in your source code using a local code page (such as CP-923 for Japanese or CP-1251 for Cyrillic) will not work.
447     In C++11 you can encode a string literal in UTF-8 by using the u8"hello" syntax. Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
448     You can also try to remap your local codepage characters to their Unicode codepoint using font->AddRemapChar(), but international users may have problems reading/editing your source code.
449 
450       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese());  // Load Japanese characters
451       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
452       io.ImeWindowHandle = MY_HWND;      // To input using Microsoft IME, give ImGui the hwnd of your application
453 
454     As for text input, depends on you passing the right character code to io.AddInputCharacter(). The example applications do that.
455 
456  Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
457  A: The easiest way is to create a dummy window. Call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flag, zero background alpha,
458     then retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
459 
460  - tip: the construct 'IMGUI_ONCE_UPON_A_FRAME { ... }' will run the block of code only once a frame. You can use it to quickly add custom UI in the middle of a deep nested inner loop in your code.
461  - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug"
462  - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window. this is also useful to set yourself in the context of another window (to get/set other settings)
463  - tip: you can call Render() multiple times (e.g for VR renders).
464  - tip: call and read the ShowTestWindow() code in imgui_demo.cpp for more example of how to use ImGui!
465 
466 
467  ISSUES & TODO-LIST
468  ==================
469  Issue numbers (#) refer to github issues listed at https://github.com/ocornut/imgui/issues
470  The list below consist mostly of ideas noted down before they are requested/discussed by users (at which point it usually moves to the github)
471 
472  - doc: add a proper documentation+regression testing system (#435)
473  - window: add a way for very transient windows (non-saved, temporary overlay over hundreds of objects) to "clean" up from the global window list. perhaps a lightweight explicit cleanup pass.
474  - window: calling SetNextWindowSize() every frame with <= 0 doesn't do anything, may be useful to allow (particularly when used for a single axis) (#690)
475  - window: auto-fit feedback loop when user relies on any dynamic layout (window width multiplier, column) appears weird to end-user. clarify.
476  - window: allow resizing of child windows (possibly given min/max for each axis?)
477  - window: background options for child windows, border option (disable rounding)
478  - window: add a way to clear an existing window instead of appending (e.g. for tooltip override using a consistent api rather than the deferred tooltip)
479  - window: resizing from any sides? + mouse cursor directives for app.
480 !- window: begin with *p_open == false should return false.
481  - window: get size/pos helpers given names (see discussion in #249)
482  - window: a collapsed window can be stuck behind the main menu bar?
483  - window: when window is small, prioritize resize button over close button.
484  - window: detect extra End() call that pop the "Debug" window out and assert at call site instead of later.
485  - window/tooltip: allow to set the width of a tooltip to allow TextWrapped() etc. while keeping the height automatic.
486  - window: increase minimum size of a window with menus or fix the menu rendering so that it doesn't look odd.
487  - draw-list: maintaining bounding box per command would allow to merge draw command when clipping isn't relied on (typical non-scrolling window or non-overflowing column would merge with previous command).
488 !- scrolling: allow immediately effective change of scroll if we haven't appended items yet
489  - splitter/separator: formalize the splitter idiom into an official api (we want to handle n-way split) (#319)
490  - widgets: display mode: widget-label, label-widget (aligned on column or using fixed size), label-newline-tab-widget etc.
491  - widgets: clean up widgets internal toward exposing everything.
492  - widgets: add disabled and read-only modes (#211)
493  - main: considering adding an Init() function? some constructs are awkward in the implementation because of the lack of them.
494 !- main: make it so that a frame with no window registered won't refocus every window on subsequent frames (~bump LastFrameActive of all windows).
495  - main: IsItemHovered() make it more consistent for various type of widgets, widgets with multiple components, etc. also effectively IsHovered() region sometimes differs from hot region, e.g tree nodes
496  - main: IsItemHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode?
497  - input text: clean up the mess caused by converting UTF-8 <> wchar. the code is rather inefficient right now and super fragile.
498  - input text: reorganize event handling, allow CharFilter to modify buffers, allow multiple events? (#541)
499  - input text: expose CursorPos in char filter event (#816)
500  - input text: flag to disable live update of the user buffer (also applies to float/int text input)
501  - input text: resize behavior - field could stretch when being edited? hover tooltip shows more text?
502  - input text: add ImGuiInputTextFlags_EnterToApply? (off #218)
503  - input text: add discard flag (e.g. ImGuiInputTextFlags_DiscardActiveBuffer) or make it easier to clear active focus for text replacement during edition (#725)
504  - input text multi-line: don't directly call AddText() which does an unnecessary vertex reserve for character count prior to clipping. and/or more line-based clipping to AddText(). and/or reorganize TextUnformatted/RenderText for more efficiency for large text (e.g TextUnformatted could clip and log separately, etc).
505  - input text multi-line: way to dynamically grow the buffer without forcing the user to initially allocate for worse case (follow up on #200)
506  - input text multi-line: line numbers? status bar? (follow up on #200)
507  - input text multi-line: behave better when user changes input buffer while editing is active (even though it is illegal behavior). namely, the change of buffer can create a scrollbar glitch (#725)
508  - input text: allow centering/positioning text so that ctrl+clicking Drag or Slider keeps the textual value at the same pixel position.
509  - input number: optional range min/max for Input*() functions
510  - input number: holding [-]/[+] buttons could increase the step speed non-linearly (or user-controlled)
511  - input number: use mouse wheel to step up/down
512  - input number: applying arithmetics ops (+,-,*,/) messes up with text edit undo stack.
513  - button: provide a button that looks framed.
514  - text: proper alignment options
515  - image/image button: misalignment on padded/bordered button?
516  - image/image button: parameters are confusing, image() has tint_col,border_col whereas imagebutton() has bg_col/tint_col. Even thou they are different parameters ordering could be more consistent. can we fix that?
517  - layout: horizontal layout helper (#97)
518  - layout: horizontal flow until no space left (#404)
519  - layout: more generic alignment state (left/right/centered) for single items?
520  - layout: clean up the InputFloatN/SliderFloatN/ColorEdit4 layout code. item width should include frame padding.
521  - layout: BeginGroup() needs a border option.
522  - columns: declare column set (each column: fixed size, %, fill, distribute default size among fills) (#513, #125)
523  - columns: add a conditional parameter to SetColumnOffset() (#513, #125)
524  - columns: separator function or parameter that works within the column (currently Separator() bypass all columns) (#125)
525  - columns: columns header to act as button (~sort op) and allow resize/reorder (#513, #125)
526  - columns: user specify columns size (#513, #125)
527  - columns: flag to add horizontal separator above/below?
528  - columns/layout: setup minimum line height (equivalent of automatically calling AlignFirstTextHeightToWidgets)
529  - combo: sparse combo boxes (via function call?) / iterators
530  - combo: contents should extends to fit label if combo widget is small
531  - combo/listbox: keyboard control. need InputText-like non-active focus + key handling. considering keyboard for custom listbox (pr #203)
532  - listbox: multiple selection
533  - listbox: user may want to initial scroll to focus on the one selected value?
534  - listbox: keyboard navigation.
535  - listbox: scrolling should track modified selection.
536 !- popups/menus: clarify usage of popups id, how MenuItem/Selectable closing parent popups affects the ID, etc. this is quite fishy needs improvement! (#331, #402)
537  - popups: add variant using global identifier similar to Begin/End (#402)
538  - popups: border options. richer api like BeginChild() perhaps? (#197)
539  - tooltip: tooltip that doesn't fit in entire screen seems to lose their "last preferred button" and may teleport when moving mouse
540  - menus: local shortcuts, global shortcuts (#456, #126)
541  - menus: icons
542  - menus: menubars: some sort of priority / effect of main menu-bar on desktop size?
543  - menus: calling BeginMenu() twice with a same name doesn't seem to append nicely
544  - statusbar: add a per-window status bar helper similar to what menubar does.
545  - tabs (#261, #351)
546  - separator: separator on the initial position of a window is not visible (cursorpos.y <= clippos.y)
547 !- color: the color helpers/typing is a mess and needs sorting out.
548  - color: add a better color picker (#346)
549  - node/graph editor (#306)
550  - pie menus patterns (#434)
551  - drag'n drop, dragging helpers (carry dragging info, visualize drag source before clicking, drop target, etc.) (#143, #479)
552  - plot: PlotLines() should use the polygon-stroke facilities (currently issues with averaging normals)
553  - plot: make it easier for user to draw extra stuff into the graph (e.g: draw basis, highlight certain points, 2d plots, multiple plots)
554  - plot: "smooth" automatic scale over time, user give an input 0.0(full user scale) 1.0(full derived from value)
555  - plot: add a helper e.g. Plot(char* label, float value, float time_span=2.0f) that stores values and Plot them for you - probably another function name. and/or automatically allow to plot ANY displayed value (more reliance on stable ID)
556  - slider: allow using the [-]/[+] buttons used by InputFloat()/InputInt()
557  - slider: initial absolute click is imprecise. change to relative movement slider (same as scrollbar).
558  - slider: add dragging-based widgets to edit values with mouse (on 2 axises), saving screen real-estate.
559  - slider: tint background based on value (e.g. v_min -> v_max, or use 0.0f either side of the sign)
560  - slider & drag: int data passing through a float
561  - drag float: up/down axis
562  - drag float: added leeway on edge (e.g. a few invisible steps past the clamp limits)
563  - tree node / optimization: avoid formatting when clipped.
564  - tree node: tree-node/header right-most side doesn't take account of horizontal scrolling.
565  - tree node: add treenode/treepush int variants? not there because (void*) cast from int warns on some platforms/settings?
566  - tree node: try to apply scrolling at time of TreePop() if node was just opened and end of node is past scrolling limits?
567  - tree node / selectable render mismatch which is visible if you use them both next to each other (e.g. cf. property viewer)
568  - tree node: tweak color scheme to distinguish headers from selected tree node (#581)
569  - textwrapped: figure out better way to use TextWrapped() in an always auto-resize context (tooltip, etc.) (#249)
570  - settings: write more decent code to allow saving/loading new fields
571  - settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file
572  - style: add window shadows.
573  - style/optimization: store rounded corners in texture to use 1 quad per corner (filled and wireframe) to lower the cost of rounding.
574  - style: color-box not always square?
575  - style: a concept of "compact style" that the end-user can easily rely on (e.g. PushStyleCompact()?) that maps to other settings? avoid implementing duplicate helpers such as SmallCheckbox(), etc.
576  - style: try to make PushStyleVar() more robust to incorrect parameters (to be more friendly to edit & continues situation).
577  - style: global scale setting.
578  - style: WindowPadding needs to be EVEN needs the 0.5 multiplier probably have a subtle effect on clip rectangle
579  - text: simple markup language for color change?
580  - font: dynamic font atlas to avoid baking huge ranges into bitmap and make scaling easier.
581  - font: small opt: for monospace font (like the defalt one) we can trim IndexXAdvance as long as trailing value is == FallbackXAdvance
582  - font: add support for kerning, probably optional. perhaps default to (32..128)^2 matrix ~ 36KB then hash fallback.
583  - font: add a simpler CalcTextSizeA() api? current one ok but not welcome if user needs to call it directly (without going through ImGui::CalcTextSize)
584  - font: fix AddRemapChar() to work before font has been built.
585  - log: LogButtons() options for specifying depth and/or hiding depth slider
586  - log: have more control over the log scope (e.g. stop logging when leaving current tree node scope)
587  - log: be able to log anything (e.g. right-click on a window/tree-node, shows context menu? log into tty/file/clipboard)
588  - log: let user copy any window content to clipboard easily (CTRL+C on windows? while moving it? context menu?). code is commented because it fails with multiple Begin/End pairs.
589  - filters: set a current filter that tree node can automatically query to hide themselves
590  - filters: handle wildcards (with implicit leading/trailing *), regexps
591  - shortcuts: add a shortcut api, e.g. parse "&Save" and/or "Save (CTRL+S)", pass in to widgets or provide simple ways to use (button=activate, input=focus)
592 !- keyboard: tooltip & combo boxes are messing up / not honoring keyboard tabbing
593  - keyboard: full keyboard navigation and focus. (#323)
594  - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622)
595  - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame)
596  - input: rework IO system to be able to pass actual ordered/timestamped events. (~#335, #71)
597  - input: allow to decide and pass explicit double-clicks (e.g. for windows by the CS_DBLCLKS style).
598  - input: support track pad style scrolling & slider edit.
599  - misc: provide a way to compile out the entire implementation while providing a dummy API (e.g. #define IMGUI_DUMMY_IMPL)
600  - misc: double-clicking on title bar to minimize isn't consistent, perhaps move to single-click on left-most collapse icon?
601  - misc: provide HoveredTime and ActivatedTime to ease the creation of animations.
602  - style editor: have a more global HSV setter (e.g. alter hue on all elements). consider replacing active/hovered by offset in HSV space? (#438)
603  - style editor: color child window height expressed in multiple of line height.
604  - remote: make a system like RemoteImGui first-class citizen/project (#75)
605  - drawlist: move Font, FontSize, FontTexUvWhitePixel inside ImDrawList and make it self-contained (apart from drawing settings?)
606  - drawlist: end-user probably can't call Clear() directly because we expect a texture to be pushed in the stack.
607  - examples: directx9: save/restore device state more thoroughly.
608  - examples: window minimize, maximize (#583)
609  - optimization: add a flag to disable most of rendering, for the case where the user expect to skip it (#335)
610  - optimization: use another hash function than crc32, e.g. FNV1a
611  - optimization/render: merge command-lists with same clip-rect into one even if they aren't sequential? (as long as in-between clip rectangle don't overlap)?
612  - optimization: turn some the various stack vectors into statically-sized arrays
613  - optimization: better clipping for multi-component widgets
614 */
615 
616 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
617 #define _CRT_SECURE_NO_WARNINGS
618 #endif
619 
620 #include "imgui.h"
621 #define IMGUI_DEFINE_MATH_OPERATORS
622 #define IMGUI_DEFINE_PLACEMENT_NEW
623 #include "imgui_internal.h"
624 
625 #include <ctype.h>      // toupper, isprint
626 #include <stdlib.h>     // NULL, malloc, free, qsort, atoi
627 #include <stdio.h>      // vsnprintf, sscanf, printf
628 #include <limits.h>     // INT_MIN, INT_MAX
629 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
630 #include <stddef.h>     // intptr_t
631 #else
632 #include <stdint.h>     // intptr_t
633 #endif
634 
635 #ifdef _MSC_VER
636 #pragma warning (disable: 4127) // condition expression is constant
637 #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
638 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
639 #endif
640 
641 // Clang warnings with -Weverything
642 #ifdef __clang__
643 #pragma clang diagnostic ignored "-Wold-style-cast"         // warning : use of old-style cast                              // yes, they are more terse.
644 #pragma clang diagnostic ignored "-Wfloat-equal"            // warning : comparing floating point with == or != is unsafe   // storing and comparing against same constants (typically 0.0f) is ok.
645 #pragma clang diagnostic ignored "-Wformat-nonliteral"      // warning : format string is not a string literal              // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
646 #pragma clang diagnostic ignored "-Wexit-time-destructors"  // warning : declaration requires an exit-time destructor       // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.
647 #pragma clang diagnostic ignored "-Wglobal-constructors"    // warning : declaration requires a global destructor           // similar to above, not sure what the exact difference it.
648 #pragma clang diagnostic ignored "-Wsign-conversion"        // warning : implicit conversion changes signedness             //
649 #pragma clang diagnostic ignored "-Wformat-pedantic"        // warning : format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic.
650 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' //
651 #elif defined(__GNUC__)
652 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
653 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
654 #pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
655 #pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
656 #pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
657 #pragma GCC diagnostic ignored "-Wcast-qual"                // warning: cast from type 'xxxx' to type 'xxxx' casts away qualifiers
658 #endif
659 
660 //-------------------------------------------------------------------------
661 // Forward Declarations
662 //-------------------------------------------------------------------------
663 
664 static void             LogRenderedText(const ImVec2& ref_pos, const char* text, const char* text_end = NULL);
665 
666 static void             PushMultiItemsWidths(int components, float w_full = 0.0f);
667 static float            GetDraggedColumnOffset(int column_index);
668 
669 static bool             IsKeyPressedMap(ImGuiKey key, bool repeat = true);
670 
671 static ImFont*          GetDefaultFont();
672 static void             SetCurrentFont(ImFont* font);
673 static void             SetCurrentWindow(ImGuiWindow* window);
674 static void             SetWindowScrollY(ImGuiWindow* window, float new_scroll_y);
675 static void             SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond);
676 static void             SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond);
677 static void             SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiSetCond cond);
678 static ImGuiWindow*     FindHoveredWindow(ImVec2 pos, bool excluding_childs);
679 static ImGuiWindow*     CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
680 static inline bool      IsWindowContentHoverable(ImGuiWindow* window);
681 static void             ClearSetNextWindowData();
682 static void             CheckStacksSize(ImGuiWindow* window, bool write);
683 static void             Scrollbar(ImGuiWindow* window, bool horizontal);
684 
685 static void             AddDrawListToRenderList(ImVector<ImDrawList*>& out_render_list, ImDrawList* draw_list);
686 static void             AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiWindow* window);
687 static void             AddWindowToSortedBuffer(ImVector<ImGuiWindow*>& out_sorted_windows, ImGuiWindow* window);
688 
689 static ImGuiIniData*    FindWindowSettings(const char* name);
690 static ImGuiIniData*    AddWindowSettings(const char* name);
691 static void             LoadIniSettingsFromDisk(const char* ini_filename);
692 static void             SaveIniSettingsToDisk(const char* ini_filename);
693 static void             MarkIniSettingsDirty();
694 
695 static void             PushColumnClipRect(int column_index = -1);
696 static ImRect           GetVisibleRect();
697 
698 static bool             BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags);
699 static void             CloseInactivePopups();
700 static void             ClosePopupToLevel(int remaining);
701 static void             ClosePopup(ImGuiID id);
702 static bool             IsPopupOpen(ImGuiID id);
703 static ImGuiWindow*     GetFrontMostModalRootWindow();
704 static ImVec2           FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& rect_to_avoid);
705 
706 static bool             InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data);
707 static int              InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
708 static ImVec2           InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false);
709 
710 static inline void      DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size);
711 static inline void      DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size);
712 static void             DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2);
713 static bool             DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format);
714 
715 //-----------------------------------------------------------------------------
716 // Platform dependent default implementations
717 //-----------------------------------------------------------------------------
718 
719 static const char*      GetClipboardTextFn_DefaultImpl(void* user_data);
720 static void             SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
721 static void             ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
722 
723 //-----------------------------------------------------------------------------
724 // Context
725 //-----------------------------------------------------------------------------
726 
727 // Default font atlas storage .
728 // New contexts always point by default to this font atlas. It can be changed by reassigning the GetIO().Fonts variable.
729 static ImFontAtlas      GImDefaultFontAtlas;
730 
731 // Default context storage + current context pointer.
732 // Implicitely used by all ImGui functions. Always assumed to be != NULL. Change to a different context by calling ImGui::SetCurrentContext()
733 // ImGui is currently not thread-safe because of this variable. If you want thread-safety to allow N threads to access N different contexts, you might work around it by:
734 // - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts)
735 // - or: Changing this variable to be TLS. You may #define GImGui in imconfig.h for further custom hackery. Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
736 #ifndef GImGui
737 static ImGuiContext     GImDefaultContext;
738 ImGuiContext*           GImGui = &GImDefaultContext;
739 #endif
740 
741 //-----------------------------------------------------------------------------
742 // User facing structures
743 //-----------------------------------------------------------------------------
744 
ImGuiStyle()745 ImGuiStyle::ImGuiStyle()
746 {
747     Alpha                   = 1.0f;             // Global alpha applies to everything in ImGui
748     WindowPadding           = ImVec2(8,8);      // Padding within a window
749     WindowMinSize           = ImVec2(32,32);    // Minimum window size
750     WindowRounding          = 9.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows
751     WindowTitleAlign        = ImVec2(0.0f,0.5f);// Alignment for title bar text
752     ChildWindowRounding     = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
753     FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
754     FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
755     ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
756     ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
757     TouchExtraPadding       = ImVec2(0,0);      // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!
758     IndentSpacing           = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
759     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns
760     ScrollbarSize           = 16.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
761     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar
762     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar
763     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
764     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
765     DisplayWindowPadding    = ImVec2(22,22);    // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows.
766     DisplaySafeAreaPadding  = ImVec2(4,4);      // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
767     AntiAliasedLines        = true;             // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
768     AntiAliasedShapes       = true;             // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
769     CurveTessellationTol    = 1.25f;            // Tessellation tolerance. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
770 
771     Colors[ImGuiCol_Text]                   = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
772     Colors[ImGuiCol_TextDisabled]           = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
773     Colors[ImGuiCol_WindowBg]               = ImVec4(0.00f, 0.00f, 0.00f, 0.70f);
774     Colors[ImGuiCol_ChildWindowBg]          = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
775     Colors[ImGuiCol_PopupBg]                = ImVec4(0.05f, 0.05f, 0.10f, 0.90f);
776     Colors[ImGuiCol_Border]                 = ImVec4(0.70f, 0.70f, 0.70f, 0.65f);
777     Colors[ImGuiCol_BorderShadow]           = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
778     Colors[ImGuiCol_FrameBg]                = ImVec4(0.80f, 0.80f, 0.80f, 0.30f);   // Background of checkbox, radio button, plot, slider, text input
779     Colors[ImGuiCol_FrameBgHovered]         = ImVec4(0.90f, 0.80f, 0.80f, 0.40f);
780     Colors[ImGuiCol_FrameBgActive]          = ImVec4(0.90f, 0.65f, 0.65f, 0.45f);
781     Colors[ImGuiCol_TitleBg]                = ImVec4(0.27f, 0.27f, 0.54f, 0.83f);
782     Colors[ImGuiCol_TitleBgCollapsed]       = ImVec4(0.40f, 0.40f, 0.80f, 0.20f);
783     Colors[ImGuiCol_TitleBgActive]          = ImVec4(0.32f, 0.32f, 0.63f, 0.87f);
784     Colors[ImGuiCol_MenuBarBg]              = ImVec4(0.40f, 0.40f, 0.55f, 0.80f);
785     Colors[ImGuiCol_ScrollbarBg]            = ImVec4(0.20f, 0.25f, 0.30f, 0.60f);
786     Colors[ImGuiCol_ScrollbarGrab]          = ImVec4(0.40f, 0.40f, 0.80f, 0.30f);
787     Colors[ImGuiCol_ScrollbarGrabHovered]   = ImVec4(0.40f, 0.40f, 0.80f, 0.40f);
788     Colors[ImGuiCol_ScrollbarGrabActive]    = ImVec4(0.80f, 0.50f, 0.50f, 0.40f);
789     Colors[ImGuiCol_ComboBg]                = ImVec4(0.20f, 0.20f, 0.20f, 0.99f);
790     Colors[ImGuiCol_CheckMark]              = ImVec4(0.90f, 0.90f, 0.90f, 0.50f);
791     Colors[ImGuiCol_SliderGrab]             = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
792     Colors[ImGuiCol_SliderGrabActive]       = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
793     Colors[ImGuiCol_Button]                 = ImVec4(0.67f, 0.40f, 0.40f, 0.60f);
794     Colors[ImGuiCol_ButtonHovered]          = ImVec4(0.67f, 0.40f, 0.40f, 1.00f);
795     Colors[ImGuiCol_ButtonActive]           = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
796     Colors[ImGuiCol_Header]                 = ImVec4(0.40f, 0.40f, 0.90f, 0.45f);
797     Colors[ImGuiCol_HeaderHovered]          = ImVec4(0.45f, 0.45f, 0.90f, 0.80f);
798     Colors[ImGuiCol_HeaderActive]           = ImVec4(0.53f, 0.53f, 0.87f, 0.80f);
799     Colors[ImGuiCol_Column]                 = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
800     Colors[ImGuiCol_ColumnHovered]          = ImVec4(0.70f, 0.60f, 0.60f, 1.00f);
801     Colors[ImGuiCol_ColumnActive]           = ImVec4(0.90f, 0.70f, 0.70f, 1.00f);
802     Colors[ImGuiCol_ResizeGrip]             = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
803     Colors[ImGuiCol_ResizeGripHovered]      = ImVec4(1.00f, 1.00f, 1.00f, 0.60f);
804     Colors[ImGuiCol_ResizeGripActive]       = ImVec4(1.00f, 1.00f, 1.00f, 0.90f);
805     Colors[ImGuiCol_CloseButton]            = ImVec4(0.50f, 0.50f, 0.90f, 0.50f);
806     Colors[ImGuiCol_CloseButtonHovered]     = ImVec4(0.70f, 0.70f, 0.90f, 0.60f);
807     Colors[ImGuiCol_CloseButtonActive]      = ImVec4(0.70f, 0.70f, 0.70f, 1.00f);
808     Colors[ImGuiCol_PlotLines]              = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
809     Colors[ImGuiCol_PlotLinesHovered]       = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
810     Colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
811     Colors[ImGuiCol_PlotHistogramHovered]   = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
812     Colors[ImGuiCol_TextSelectedBg]         = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
813     Colors[ImGuiCol_ModalWindowDarkening]   = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
814 }
815 
ImGuiIO()816 ImGuiIO::ImGuiIO()
817 {
818     // Most fields are initialized with zero
819     memset(this, 0, sizeof(*this));
820 
821     DisplaySize = ImVec2(-1.0f, -1.0f);
822     DeltaTime = 1.0f/60.0f;
823     IniSavingRate = 5.0f;
824     IniFilename = "imgui.ini";
825     LogFilename = "imgui_log.txt";
826     Fonts = &GImDefaultFontAtlas;
827     FontGlobalScale = 1.0f;
828     FontDefault = NULL;
829     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
830     MousePos = ImVec2(-1,-1);
831     MousePosPrev = ImVec2(-1,-1);
832     MouseDoubleClickTime = 0.30f;
833     MouseDoubleClickMaxDist = 6.0f;
834     MouseDragThreshold = 6.0f;
835     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++)
836         MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
837     for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++)
838         KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
839     for (int i = 0; i < ImGuiKey_COUNT; i++)
840         KeyMap[i] = -1;
841     KeyRepeatDelay = 0.250f;
842     KeyRepeatRate = 0.050f;
843     UserData = NULL;
844 
845     // User functions
846     RenderDrawListsFn = NULL;
847     MemAllocFn = malloc;
848     MemFreeFn = free;
849     GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;   // Platform dependent default implementations
850     SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
851     ClipboardUserData = NULL;
852     ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
853 
854     // Set OS X style defaults based on __APPLE__ compile time flag
855 #ifdef __APPLE__
856     OSXBehaviors = true;
857 #endif
858 }
859 
860 // Pass in translated ASCII characters for text input.
861 // - with glfw you can get those from the callback set in glfwSetCharCallback()
862 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(ImWchar c)863 void ImGuiIO::AddInputCharacter(ImWchar c)
864 {
865     const int n = ImStrlenW(InputCharacters);
866     if (n + 1 < IM_ARRAYSIZE(InputCharacters))
867     {
868         InputCharacters[n] = c;
869         InputCharacters[n+1] = '\0';
870     }
871 }
872 
AddInputCharactersUTF8(const char * utf8_chars)873 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
874 {
875     // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more
876     const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar);
877     ImWchar wchars[wchars_buf_len];
878     ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL);
879     for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++)
880         AddInputCharacter(wchars[i]);
881 }
882 
883 //-----------------------------------------------------------------------------
884 // HELPERS
885 //-----------------------------------------------------------------------------
886 
887 #define IM_F32_TO_INT8_UNBOUND(_VAL)    ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f)))   // Unsaturated, for display purpose
888 #define IM_F32_TO_INT8_SAT(_VAL)        ((int)(ImSaturate(_VAL) * 255.0f + 0.5f))               // Saturated, always output 0..255
889 
890 // Play it nice with Windows users. Notepad in 2015 still doesn't display text data with Unix-style \n.
891 #ifdef _WIN32
892 #define IM_NEWLINE "\r\n"
893 #else
894 #define IM_NEWLINE "\n"
895 #endif
896 
ImIsPointInTriangle(const ImVec2 & p,const ImVec2 & a,const ImVec2 & b,const ImVec2 & c)897 bool ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, const ImVec2& c)
898 {
899     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
900     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
901     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
902     return ((b1 == b2) && (b2 == b3));
903 }
904 
ImStricmp(const char * str1,const char * str2)905 int ImStricmp(const char* str1, const char* str2)
906 {
907     int d;
908     while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
909     return d;
910 }
911 
ImStrnicmp(const char * str1,const char * str2,int count)912 int ImStrnicmp(const char* str1, const char* str2, int count)
913 {
914     int d = 0;
915     while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
916     return d;
917 }
918 
ImStrncpy(char * dst,const char * src,int count)919 void ImStrncpy(char* dst, const char* src, int count)
920 {
921     if (count < 1) return;
922     strncpy(dst, src, (size_t)count);
923     dst[count-1] = 0;
924 }
925 
ImStrdup(const char * str)926 char* ImStrdup(const char *str)
927 {
928     size_t len = strlen(str) + 1;
929     void* buff = ImGui::MemAlloc(len);
930     return (char*)memcpy(buff, (const void*)str, len);
931 }
932 
ImStrlenW(const ImWchar * str)933 int ImStrlenW(const ImWchar* str)
934 {
935     int n = 0;
936     while (*str++) n++;
937     return n;
938 }
939 
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)940 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
941 {
942     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
943         buf_mid_line--;
944     return buf_mid_line;
945 }
946 
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)947 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
948 {
949     if (!needle_end)
950         needle_end = needle + strlen(needle);
951 
952     const char un0 = (char)toupper(*needle);
953     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
954     {
955         if (toupper(*haystack) == un0)
956         {
957             const char* b = needle + 1;
958             for (const char* a = haystack + 1; b < needle_end; a++, b++)
959                 if (toupper(*a) != toupper(*b))
960                     break;
961             if (b == needle_end)
962                 return haystack;
963         }
964         haystack++;
965     }
966     return NULL;
967 }
968 
969 
970 // MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
971 // Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm.
ImFormatString(char * buf,int buf_size,const char * fmt,...)972 int ImFormatString(char* buf, int buf_size, const char* fmt, ...)
973 {
974     IM_ASSERT(buf_size > 0);
975     va_list args;
976     va_start(args, fmt);
977     int w = vsnprintf(buf, buf_size, fmt, args);
978     va_end(args);
979     if (w == -1 || w >= buf_size)
980         w = buf_size - 1;
981     buf[w] = 0;
982     return w;
983 }
984 
ImFormatStringV(char * buf,int buf_size,const char * fmt,va_list args)985 int ImFormatStringV(char* buf, int buf_size, const char* fmt, va_list args)
986 {
987     IM_ASSERT(buf_size > 0);
988     int w = vsnprintf(buf, buf_size, fmt, args);
989     if (w == -1 || w >= buf_size)
990         w = buf_size - 1;
991     buf[w] = 0;
992     return w;
993 }
994 
995 // Pass data_size==0 for zero-terminated strings
996 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImHash(const void * data,int data_size,ImU32 seed)997 ImU32 ImHash(const void* data, int data_size, ImU32 seed)
998 {
999     static ImU32 crc32_lut[256] = { 0 };
1000     if (!crc32_lut[1])
1001     {
1002         const ImU32 polynomial = 0xEDB88320;
1003         for (ImU32 i = 0; i < 256; i++)
1004         {
1005             ImU32 crc = i;
1006             for (ImU32 j = 0; j < 8; j++)
1007                 crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial);
1008             crc32_lut[i] = crc;
1009         }
1010     }
1011 
1012     seed = ~seed;
1013     ImU32 crc = seed;
1014     const unsigned char* current = (const unsigned char*)data;
1015 
1016     if (data_size > 0)
1017     {
1018         // Known size
1019         while (data_size--)
1020             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++];
1021     }
1022     else
1023     {
1024         // Zero-terminated string
1025         while (unsigned char c = *current++)
1026         {
1027             // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1028             // Because this syntax is rarely used we are optimizing for the common case.
1029             // - If we reach ### in the string we discard the hash so far and reset to the seed.
1030             // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller.
1031             if (c == '#' && current[0] == '#' && current[1] == '#')
1032                 crc = seed;
1033             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1034         }
1035     }
1036     return ~crc;
1037 }
1038 
1039 //-----------------------------------------------------------------------------
1040 // ImText* helpers
1041 //-----------------------------------------------------------------------------
1042 
1043 // Convert UTF-8 to 32-bits character, process single character input.
1044 // Based on stb_from_utf8() from github.com/nothings/stb/
1045 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1046 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1047 {
1048     unsigned int c = (unsigned int)-1;
1049     const unsigned char* str = (const unsigned char*)in_text;
1050     if (!(*str & 0x80))
1051     {
1052         c = (unsigned int)(*str++);
1053         *out_char = c;
1054         return 1;
1055     }
1056     if ((*str & 0xe0) == 0xc0)
1057     {
1058         *out_char = 0xFFFD; // will be invalid but not end of string
1059         if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1060         if (*str < 0xc2) return 2;
1061         c = (unsigned int)((*str++ & 0x1f) << 6);
1062         if ((*str & 0xc0) != 0x80) return 2;
1063         c += (*str++ & 0x3f);
1064         *out_char = c;
1065         return 2;
1066     }
1067     if ((*str & 0xf0) == 0xe0)
1068     {
1069         *out_char = 0xFFFD; // will be invalid but not end of string
1070         if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1071         if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1072         if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1073         c = (unsigned int)((*str++ & 0x0f) << 12);
1074         if ((*str & 0xc0) != 0x80) return 3;
1075         c += (unsigned int)((*str++ & 0x3f) << 6);
1076         if ((*str & 0xc0) != 0x80) return 3;
1077         c += (*str++ & 0x3f);
1078         *out_char = c;
1079         return 3;
1080     }
1081     if ((*str & 0xf8) == 0xf0)
1082     {
1083         *out_char = 0xFFFD; // will be invalid but not end of string
1084         if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1085         if (*str > 0xf4) return 4;
1086         if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1087         if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1088         c = (unsigned int)((*str++ & 0x07) << 18);
1089         if ((*str & 0xc0) != 0x80) return 4;
1090         c += (unsigned int)((*str++ & 0x3f) << 12);
1091         if ((*str & 0xc0) != 0x80) return 4;
1092         c += (unsigned int)((*str++ & 0x3f) << 6);
1093         if ((*str & 0xc0) != 0x80) return 4;
1094         c += (*str++ & 0x3f);
1095         // utf-8 encodings of values used in surrogate pairs are invalid
1096         if ((c & 0xFFFFF800) == 0xD800) return 4;
1097         *out_char = c;
1098         return 4;
1099     }
1100     *out_char = 0;
1101     return 0;
1102 }
1103 
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1104 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1105 {
1106     ImWchar* buf_out = buf;
1107     ImWchar* buf_end = buf + buf_size;
1108     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1109     {
1110         unsigned int c;
1111         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1112         if (c == 0)
1113             break;
1114         if (c < 0x10000)    // FIXME: Losing characters that don't fit in 2 bytes
1115             *buf_out++ = (ImWchar)c;
1116     }
1117     *buf_out = 0;
1118     if (in_text_remaining)
1119         *in_text_remaining = in_text;
1120     return (int)(buf_out - buf);
1121 }
1122 
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1123 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1124 {
1125     int char_count = 0;
1126     while ((!in_text_end || in_text < in_text_end) && *in_text)
1127     {
1128         unsigned int c;
1129         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1130         if (c == 0)
1131             break;
1132         if (c < 0x10000)
1133             char_count++;
1134     }
1135     return char_count;
1136 }
1137 
1138 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1139 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1140 {
1141     if (c < 0x80)
1142     {
1143         buf[0] = (char)c;
1144         return 1;
1145     }
1146     if (c < 0x800)
1147     {
1148         if (buf_size < 2) return 0;
1149         buf[0] = (char)(0xc0 + (c >> 6));
1150         buf[1] = (char)(0x80 + (c & 0x3f));
1151         return 2;
1152     }
1153     if (c >= 0xdc00 && c < 0xe000)
1154     {
1155         return 0;
1156     }
1157     if (c >= 0xd800 && c < 0xdc00)
1158     {
1159         if (buf_size < 4) return 0;
1160         buf[0] = (char)(0xf0 + (c >> 18));
1161         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1162         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1163         buf[3] = (char)(0x80 + ((c ) & 0x3f));
1164         return 4;
1165     }
1166     //else if (c < 0x10000)
1167     {
1168         if (buf_size < 3) return 0;
1169         buf[0] = (char)(0xe0 + (c >> 12));
1170         buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1171         buf[2] = (char)(0x80 + ((c ) & 0x3f));
1172         return 3;
1173     }
1174 }
1175 
ImTextCountUtf8BytesFromChar(unsigned int c)1176 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1177 {
1178     if (c < 0x80) return 1;
1179     if (c < 0x800) return 2;
1180     if (c >= 0xdc00 && c < 0xe000) return 0;
1181     if (c >= 0xd800 && c < 0xdc00) return 4;
1182     return 3;
1183 }
1184 
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1185 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1186 {
1187     char* buf_out = buf;
1188     const char* buf_end = buf + buf_size;
1189     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1190     {
1191         unsigned int c = (unsigned int)(*in_text++);
1192         if (c < 0x80)
1193             *buf_out++ = (char)c;
1194         else
1195             buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1196     }
1197     *buf_out = 0;
1198     return (int)(buf_out - buf);
1199 }
1200 
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1201 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1202 {
1203     int bytes_count = 0;
1204     while ((!in_text_end || in_text < in_text_end) && *in_text)
1205     {
1206         unsigned int c = (unsigned int)(*in_text++);
1207         if (c < 0x80)
1208             bytes_count++;
1209         else
1210             bytes_count += ImTextCountUtf8BytesFromChar(c);
1211     }
1212     return bytes_count;
1213 }
1214 
ColorConvertU32ToFloat4(ImU32 in)1215 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1216 {
1217     float s = 1.0f/255.0f;
1218     return ImVec4(
1219         ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1220         ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1221         ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1222         ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1223 }
1224 
ColorConvertFloat4ToU32(const ImVec4 & in)1225 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1226 {
1227     ImU32 out;
1228     out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1229     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1230     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1231     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1232     return out;
1233 }
1234 
GetColorU32(ImGuiCol idx,float alpha_mul)1235 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1236 {
1237     ImVec4 c = GImGui->Style.Colors[idx];
1238     c.w *= GImGui->Style.Alpha * alpha_mul;
1239     return ColorConvertFloat4ToU32(c);
1240 }
1241 
GetColorU32(const ImVec4 & col)1242 ImU32 ImGui::GetColorU32(const ImVec4& col)
1243 {
1244     ImVec4 c = col;
1245     c.w *= GImGui->Style.Alpha;
1246     return ColorConvertFloat4ToU32(c);
1247 }
1248 
1249 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1250 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
ColorConvertRGBtoHSV(float r,float g,float b,float & out_h,float & out_s,float & out_v)1251 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1252 {
1253     float K = 0.f;
1254     if (g < b)
1255     {
1256         const float tmp = g; g = b; b = tmp;
1257         K = -1.f;
1258     }
1259     if (r < g)
1260     {
1261         const float tmp = r; r = g; g = tmp;
1262         K = -2.f / 6.f - K;
1263     }
1264 
1265     const float chroma = r - (g < b ? g : b);
1266     out_h = fabsf(K + (g - b) / (6.f * chroma + 1e-20f));
1267     out_s = chroma / (r + 1e-20f);
1268     out_v = r;
1269 }
1270 
1271 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1272 // also http://en.wikipedia.org/wiki/HSL_and_HSV
ColorConvertHSVtoRGB(float h,float s,float v,float & out_r,float & out_g,float & out_b)1273 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1274 {
1275     if (s == 0.0f)
1276     {
1277         // gray
1278         out_r = out_g = out_b = v;
1279         return;
1280     }
1281 
1282     h = fmodf(h, 1.0f) / (60.0f/360.0f);
1283     int   i = (int)h;
1284     float f = h - (float)i;
1285     float p = v * (1.0f - s);
1286     float q = v * (1.0f - s * f);
1287     float t = v * (1.0f - s * (1.0f - f));
1288 
1289     switch (i)
1290     {
1291     case 0: out_r = v; out_g = t; out_b = p; break;
1292     case 1: out_r = q; out_g = v; out_b = p; break;
1293     case 2: out_r = p; out_g = v; out_b = t; break;
1294     case 3: out_r = p; out_g = q; out_b = v; break;
1295     case 4: out_r = t; out_g = p; out_b = v; break;
1296     case 5: default: out_r = v; out_g = p; out_b = q; break;
1297     }
1298 }
1299 
ImFileOpen(const char * filename,const char * mode)1300 FILE* ImFileOpen(const char* filename, const char* mode)
1301 {
1302 #if defined(_WIN32) && !defined(__CYGWIN__)
1303     // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can)
1304     const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1305     const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1306     ImVector<ImWchar> buf;
1307     buf.resize(filename_wsize + mode_wsize);
1308     ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1309     ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1310     return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1311 #else
1312     return fopen(filename, mode);
1313 #endif
1314 }
1315 
1316 // Load file content into memory
1317 // Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree()
ImFileLoadToMemory(const char * filename,const char * file_open_mode,int * out_file_size,int padding_bytes)1318 void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, int* out_file_size, int padding_bytes)
1319 {
1320     IM_ASSERT(filename && file_open_mode);
1321     if (out_file_size)
1322         *out_file_size = 0;
1323 
1324     FILE* f;
1325     if ((f = ImFileOpen(filename, file_open_mode)) == NULL)
1326         return NULL;
1327 
1328     long file_size_signed;
1329     if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
1330     {
1331         fclose(f);
1332         return NULL;
1333     }
1334 
1335     int file_size = (int)file_size_signed;
1336     void* file_data = ImGui::MemAlloc(file_size + padding_bytes);
1337     if (file_data == NULL)
1338     {
1339         fclose(f);
1340         return NULL;
1341     }
1342     if (fread(file_data, 1, (size_t)file_size, f) != (size_t)file_size)
1343     {
1344         fclose(f);
1345         ImGui::MemFree(file_data);
1346         return NULL;
1347     }
1348     if (padding_bytes > 0)
1349         memset((void *)(((char*)file_data) + file_size), 0, padding_bytes);
1350 
1351     fclose(f);
1352     if (out_file_size)
1353         *out_file_size = file_size;
1354 
1355     return file_data;
1356 }
1357 
1358 //-----------------------------------------------------------------------------
1359 // ImGuiStorage
1360 //-----------------------------------------------------------------------------
1361 
1362 // Helper: Key->value storage
Clear()1363 void ImGuiStorage::Clear()
1364 {
1365     Data.clear();
1366 }
1367 
1368 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::Pair> & data,ImGuiID key)1369 static ImVector<ImGuiStorage::Pair>::iterator LowerBound(ImVector<ImGuiStorage::Pair>& data, ImGuiID key)
1370 {
1371     ImVector<ImGuiStorage::Pair>::iterator first = data.begin();
1372     ImVector<ImGuiStorage::Pair>::iterator last = data.end();
1373     int count = (int)(last - first);
1374     while (count > 0)
1375     {
1376         int count2 = count / 2;
1377         ImVector<ImGuiStorage::Pair>::iterator mid = first + count2;
1378         if (mid->key < key)
1379         {
1380             first = ++mid;
1381             count -= count2 + 1;
1382         }
1383         else
1384         {
1385             count = count2;
1386         }
1387     }
1388     return first;
1389 }
1390 
GetInt(ImGuiID key,int default_val) const1391 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1392 {
1393     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1394     if (it == Data.end() || it->key != key)
1395         return default_val;
1396     return it->val_i;
1397 }
1398 
GetBool(ImGuiID key,bool default_val) const1399 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1400 {
1401     return GetInt(key, default_val ? 1 : 0) != 0;
1402 }
1403 
GetFloat(ImGuiID key,float default_val) const1404 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1405 {
1406     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1407     if (it == Data.end() || it->key != key)
1408         return default_val;
1409     return it->val_f;
1410 }
1411 
GetVoidPtr(ImGuiID key) const1412 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1413 {
1414     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1415     if (it == Data.end() || it->key != key)
1416         return NULL;
1417     return it->val_p;
1418 }
1419 
1420 // References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer.
GetIntRef(ImGuiID key,int default_val)1421 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1422 {
1423     ImVector<Pair>::iterator it = LowerBound(Data, key);
1424     if (it == Data.end() || it->key != key)
1425         it = Data.insert(it, Pair(key, default_val));
1426     return &it->val_i;
1427 }
1428 
GetBoolRef(ImGuiID key,bool default_val)1429 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1430 {
1431     return (bool*)GetIntRef(key, default_val ? 1 : 0);
1432 }
1433 
GetFloatRef(ImGuiID key,float default_val)1434 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1435 {
1436     ImVector<Pair>::iterator it = LowerBound(Data, key);
1437     if (it == Data.end() || it->key != key)
1438         it = Data.insert(it, Pair(key, default_val));
1439     return &it->val_f;
1440 }
1441 
GetVoidPtrRef(ImGuiID key,void * default_val)1442 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1443 {
1444     ImVector<Pair>::iterator it = LowerBound(Data, key);
1445     if (it == Data.end() || it->key != key)
1446         it = Data.insert(it, Pair(key, default_val));
1447     return &it->val_p;
1448 }
1449 
1450 // FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame)
SetInt(ImGuiID key,int val)1451 void ImGuiStorage::SetInt(ImGuiID key, int val)
1452 {
1453     ImVector<Pair>::iterator it = LowerBound(Data, key);
1454     if (it == Data.end() || it->key != key)
1455     {
1456         Data.insert(it, Pair(key, val));
1457         return;
1458     }
1459     it->val_i = val;
1460 }
1461 
SetBool(ImGuiID key,bool val)1462 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1463 {
1464     SetInt(key, val ? 1 : 0);
1465 }
1466 
SetFloat(ImGuiID key,float val)1467 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1468 {
1469     ImVector<Pair>::iterator it = LowerBound(Data, key);
1470     if (it == Data.end() || it->key != key)
1471     {
1472         Data.insert(it, Pair(key, val));
1473         return;
1474     }
1475     it->val_f = val;
1476 }
1477 
SetVoidPtr(ImGuiID key,void * val)1478 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1479 {
1480     ImVector<Pair>::iterator it = LowerBound(Data, key);
1481     if (it == Data.end() || it->key != key)
1482     {
1483         Data.insert(it, Pair(key, val));
1484         return;
1485     }
1486     it->val_p = val;
1487 }
1488 
SetAllInt(int v)1489 void ImGuiStorage::SetAllInt(int v)
1490 {
1491     for (int i = 0; i < Data.Size; i++)
1492         Data[i].val_i = v;
1493 }
1494 
1495 //-----------------------------------------------------------------------------
1496 // ImGuiTextFilter
1497 //-----------------------------------------------------------------------------
1498 
1499 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1500 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1501 {
1502     if (default_filter)
1503     {
1504         ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1505         Build();
1506     }
1507     else
1508     {
1509         InputBuf[0] = 0;
1510         CountGrep = 0;
1511     }
1512 }
1513 
Draw(const char * label,float width)1514 bool ImGuiTextFilter::Draw(const char* label, float width)
1515 {
1516     if (width != 0.0f)
1517         ImGui::PushItemWidth(width);
1518     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1519     if (width != 0.0f)
1520         ImGui::PopItemWidth();
1521     if (value_changed)
1522         Build();
1523     return value_changed;
1524 }
1525 
split(char separator,ImVector<TextRange> & out)1526 void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>& out)
1527 {
1528     out.resize(0);
1529     const char* wb = b;
1530     const char* we = wb;
1531     while (we < e)
1532     {
1533         if (*we == separator)
1534         {
1535             out.push_back(TextRange(wb, we));
1536             wb = we + 1;
1537         }
1538         we++;
1539     }
1540     if (wb != we)
1541         out.push_back(TextRange(wb, we));
1542 }
1543 
Build()1544 void ImGuiTextFilter::Build()
1545 {
1546     Filters.resize(0);
1547     TextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
1548     input_range.split(',', Filters);
1549 
1550     CountGrep = 0;
1551     for (int i = 0; i != Filters.Size; i++)
1552     {
1553         Filters[i].trim_blanks();
1554         if (Filters[i].empty())
1555             continue;
1556         if (Filters[i].front() != '-')
1557             CountGrep += 1;
1558     }
1559 }
1560 
PassFilter(const char * text,const char * text_end) const1561 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
1562 {
1563     if (Filters.empty())
1564         return true;
1565 
1566     if (text == NULL)
1567         text = "";
1568 
1569     for (int i = 0; i != Filters.Size; i++)
1570     {
1571         const TextRange& f = Filters[i];
1572         if (f.empty())
1573             continue;
1574         if (f.front() == '-')
1575         {
1576             // Subtract
1577             if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL)
1578                 return false;
1579         }
1580         else
1581         {
1582             // Grep
1583             if (ImStristr(text, text_end, f.begin(), f.end()) != NULL)
1584                 return true;
1585         }
1586     }
1587 
1588     // Implicit * grep
1589     if (CountGrep == 0)
1590         return true;
1591 
1592     return false;
1593 }
1594 
1595 //-----------------------------------------------------------------------------
1596 // ImGuiTextBuffer
1597 //-----------------------------------------------------------------------------
1598 
1599 // On some platform vsnprintf() takes va_list by reference and modifies it.
1600 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1601 #ifndef va_copy
1602 #define va_copy(dest, src) (dest = src)
1603 #endif
1604 
1605 // Helper: Text buffer for logging/accumulating text
appendv(const char * fmt,va_list args)1606 void ImGuiTextBuffer::appendv(const char* fmt, va_list args)
1607 {
1608     va_list args_copy;
1609     va_copy(args_copy, args);
1610 
1611     int len = vsnprintf(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
1612     if (len <= 0)
1613         return;
1614 
1615     const int write_off = Buf.Size;
1616     const int needed_sz = write_off + len;
1617     if (write_off + len >= Buf.Capacity)
1618     {
1619         int double_capacity = Buf.Capacity * 2;
1620         Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity);
1621     }
1622 
1623     Buf.resize(needed_sz);
1624     ImFormatStringV(&Buf[write_off] - 1, len+1, fmt, args_copy);
1625 }
1626 
append(const char * fmt,...)1627 void ImGuiTextBuffer::append(const char* fmt, ...)
1628 {
1629     va_list args;
1630     va_start(args, fmt);
1631     appendv(fmt, args);
1632     va_end(args);
1633 }
1634 
1635 //-----------------------------------------------------------------------------
1636 // ImGuiSimpleColumns
1637 //-----------------------------------------------------------------------------
1638 
ImGuiSimpleColumns()1639 ImGuiSimpleColumns::ImGuiSimpleColumns()
1640 {
1641     Count = 0;
1642     Spacing = Width = NextWidth = 0.0f;
1643     memset(Pos, 0, sizeof(Pos));
1644     memset(NextWidths, 0, sizeof(NextWidths));
1645 }
1646 
Update(int count,float spacing,bool clear)1647 void ImGuiSimpleColumns::Update(int count, float spacing, bool clear)
1648 {
1649     IM_ASSERT(Count <= IM_ARRAYSIZE(Pos));
1650     Count = count;
1651     Width = NextWidth = 0.0f;
1652     Spacing = spacing;
1653     if (clear) memset(NextWidths, 0, sizeof(NextWidths));
1654     for (int i = 0; i < Count; i++)
1655     {
1656         if (i > 0 && NextWidths[i] > 0.0f)
1657             Width += Spacing;
1658         Pos[i] = (float)(int)Width;
1659         Width += NextWidths[i];
1660         NextWidths[i] = 0.0f;
1661     }
1662 }
1663 
DeclColumns(float w0,float w1,float w2)1664 float ImGuiSimpleColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double
1665 {
1666     NextWidth = 0.0f;
1667     NextWidths[0] = ImMax(NextWidths[0], w0);
1668     NextWidths[1] = ImMax(NextWidths[1], w1);
1669     NextWidths[2] = ImMax(NextWidths[2], w2);
1670     for (int i = 0; i < 3; i++)
1671         NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f);
1672     return ImMax(Width, NextWidth);
1673 }
1674 
CalcExtraSpace(float avail_w)1675 float ImGuiSimpleColumns::CalcExtraSpace(float avail_w)
1676 {
1677     return ImMax(0.0f, avail_w - Width);
1678 }
1679 
1680 //-----------------------------------------------------------------------------
1681 // ImGuiListClipper
1682 //-----------------------------------------------------------------------------
1683 
SetCursorPosYAndSetupDummyPrevLine(float pos_y,float line_height)1684 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
1685 {
1686     // Set cursor position and a few other things so that SetScrollHere() and Columns() can work when seeking cursor.
1687     // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. Consider moving within SetCursorXXX functions?
1688     ImGui::SetCursorPosY(pos_y);
1689     ImGuiWindow* window = ImGui::GetCurrentWindow();
1690     window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height;      // Setting those fields so that SetScrollHere() can properly function after the end of our clipper usage.
1691     window->DC.PrevLineHeight = (line_height - GImGui->Style.ItemSpacing.y);    // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list.
1692     if (window->DC.ColumnsCount > 1)
1693         window->DC.ColumnsCellMinY = window->DC.CursorPos.y;                    // Setting this so that cell Y position are set properly
1694 }
1695 
1696 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
1697 // Use case B: Begin() called from constructor with items_height>0
1698 // FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style.
Begin(int count,float items_height)1699 void ImGuiListClipper::Begin(int count, float items_height)
1700 {
1701     StartPosY = ImGui::GetCursorPosY();
1702     ItemsHeight = items_height;
1703     ItemsCount = count;
1704     StepNo = 0;
1705     DisplayEnd = DisplayStart = -1;
1706     if (ItemsHeight > 0.0f)
1707     {
1708         ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
1709         if (DisplayStart > 0)
1710             SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
1711         StepNo = 2;
1712     }
1713 }
1714 
End()1715 void ImGuiListClipper::End()
1716 {
1717     if (ItemsCount < 0)
1718         return;
1719     // In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user.
1720     if (ItemsCount < INT_MAX)
1721         SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
1722     ItemsCount = -1;
1723     StepNo = 3;
1724 }
1725 
Step()1726 bool ImGuiListClipper::Step()
1727 {
1728     if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems)
1729     {
1730         ItemsCount = -1;
1731         return false;
1732     }
1733     if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height.
1734     {
1735         DisplayStart = 0;
1736         DisplayEnd = 1;
1737         StartPosY = ImGui::GetCursorPosY();
1738         StepNo = 1;
1739         return true;
1740     }
1741     if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element.
1742     {
1743         if (ItemsCount == 1) { ItemsCount = -1; return false; }
1744         float items_height = ImGui::GetCursorPosY() - StartPosY;
1745         IM_ASSERT(items_height > 0.0f);   // If this triggers, it means Item 0 hasn't moved the cursor vertically
1746         Begin(ItemsCount-1, items_height);
1747         DisplayStart++;
1748         DisplayEnd++;
1749         StepNo = 3;
1750         return true;
1751     }
1752     if (StepNo == 2) // Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3.
1753     {
1754         IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
1755         StepNo = 3;
1756         return true;
1757     }
1758     if (StepNo == 3) // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop.
1759         End();
1760     return false;
1761 }
1762 
1763 //-----------------------------------------------------------------------------
1764 // ImGuiWindow
1765 //-----------------------------------------------------------------------------
1766 
ImGuiWindow(const char * name)1767 ImGuiWindow::ImGuiWindow(const char* name)
1768 {
1769     Name = ImStrdup(name);
1770     ID = ImHash(name, 0);
1771     IDStack.push_back(ID);
1772     MoveId = GetID("#MOVE");
1773 
1774     Flags = 0;
1775     IndexWithinParent = 0;
1776     PosFloat = Pos = ImVec2(0.0f, 0.0f);
1777     Size = SizeFull = ImVec2(0.0f, 0.0f);
1778     SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
1779     WindowPadding = ImVec2(0.0f, 0.0f);
1780     Scroll = ImVec2(0.0f, 0.0f);
1781     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
1782     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
1783     ScrollbarX = ScrollbarY = false;
1784     ScrollbarSizes = ImVec2(0.0f, 0.0f);
1785     BorderSize = 0.0f;
1786     Active = WasActive = false;
1787     Accessed = false;
1788     Collapsed = false;
1789     SkipItems = false;
1790     BeginCount = 0;
1791     PopupId = 0;
1792     AutoFitFramesX = AutoFitFramesY = -1;
1793     AutoFitOnlyGrows = false;
1794     AutoPosLastDirection = -1;
1795     HiddenFrames = 0;
1796     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiSetCond_Always | ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing;
1797     SetWindowPosCenterWanted = false;
1798 
1799     LastFrameActive = -1;
1800     ItemWidthDefault = 0.0f;
1801     FontWindowScale = 1.0f;
1802 
1803     DrawList = (ImDrawList*)ImGui::MemAlloc(sizeof(ImDrawList));
1804     IM_PLACEMENT_NEW(DrawList) ImDrawList();
1805     DrawList->_OwnerName = Name;
1806     RootWindow = NULL;
1807     RootNonPopupWindow = NULL;
1808     ParentWindow = NULL;
1809 
1810     FocusIdxAllCounter = FocusIdxTabCounter = -1;
1811     FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX;
1812     FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX;
1813 }
1814 
~ImGuiWindow()1815 ImGuiWindow::~ImGuiWindow()
1816 {
1817     DrawList->~ImDrawList();
1818     ImGui::MemFree(DrawList);
1819     DrawList = NULL;
1820     ImGui::MemFree(Name);
1821     Name = NULL;
1822 }
1823 
GetID(const char * str,const char * str_end)1824 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
1825 {
1826     ImGuiID seed = IDStack.back();
1827     ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
1828     ImGui::KeepAliveID(id);
1829     return id;
1830 }
1831 
GetID(const void * ptr)1832 ImGuiID ImGuiWindow::GetID(const void* ptr)
1833 {
1834     ImGuiID seed = IDStack.back();
1835     ImGuiID id = ImHash(&ptr, sizeof(void*), seed);
1836     ImGui::KeepAliveID(id);
1837     return id;
1838 }
1839 
GetIDNoKeepAlive(const char * str,const char * str_end)1840 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
1841 {
1842     ImGuiID seed = IDStack.back();
1843     return ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
1844 }
1845 
1846 //-----------------------------------------------------------------------------
1847 // Internal API exposed in imgui_internal.h
1848 //-----------------------------------------------------------------------------
1849 
SetCurrentWindow(ImGuiWindow * window)1850 static void SetCurrentWindow(ImGuiWindow* window)
1851 {
1852     ImGuiContext& g = *GImGui;
1853     g.CurrentWindow = window;
1854     if (window)
1855         g.FontSize = window->CalcFontSize();
1856 }
1857 
GetParentWindow()1858 ImGuiWindow* ImGui::GetParentWindow()
1859 {
1860     ImGuiContext& g = *GImGui;
1861     IM_ASSERT(g.CurrentWindowStack.Size >= 2);
1862     return g.CurrentWindowStack[(unsigned int)g.CurrentWindowStack.Size - 2];
1863 }
1864 
SetActiveID(ImGuiID id,ImGuiWindow * window)1865 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
1866 {
1867     ImGuiContext& g = *GImGui;
1868     g.ActiveId = id;
1869     g.ActiveIdAllowOverlap = false;
1870     g.ActiveIdIsJustActivated = true;
1871     if (id)
1872         g.ActiveIdIsAlive = true;
1873     g.ActiveIdWindow = window;
1874 }
1875 
ClearActiveID()1876 void ImGui::ClearActiveID()
1877 {
1878     SetActiveID(0, NULL);
1879 }
1880 
SetHoveredID(ImGuiID id)1881 void ImGui::SetHoveredID(ImGuiID id)
1882 {
1883     ImGuiContext& g = *GImGui;
1884     g.HoveredId = id;
1885     g.HoveredIdAllowOverlap = false;
1886 }
1887 
KeepAliveID(ImGuiID id)1888 void ImGui::KeepAliveID(ImGuiID id)
1889 {
1890     ImGuiContext& g = *GImGui;
1891     if (g.ActiveId == id)
1892         g.ActiveIdIsAlive = true;
1893 }
1894 
1895 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_offset_y)1896 void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
1897 {
1898     ImGuiWindow* window = GetCurrentWindow();
1899     if (window->SkipItems)
1900         return;
1901 
1902     // Always align ourselves on pixel boundaries
1903     ImGuiContext& g = *GImGui;
1904     const float line_height = ImMax(window->DC.CurrentLineHeight, size.y);
1905     const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y);
1906     window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
1907     window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y));
1908     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
1909     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
1910 
1911     //window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // Debug
1912 
1913     window->DC.PrevLineHeight = line_height;
1914     window->DC.PrevLineTextBaseOffset = text_base_offset;
1915     window->DC.CurrentLineHeight = window->DC.CurrentLineTextBaseOffset = 0.0f;
1916 }
1917 
ItemSize(const ImRect & bb,float text_offset_y)1918 void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
1919 {
1920     ItemSize(bb.GetSize(), text_offset_y);
1921 }
1922 
1923 // Declare item bounding box for clipping and interaction.
1924 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
1925 // declares their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd().
ItemAdd(const ImRect & bb,const ImGuiID * id)1926 bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id)
1927 {
1928     ImGuiWindow* window = GetCurrentWindow();
1929     window->DC.LastItemId = id ? *id : 0;
1930     window->DC.LastItemRect = bb;
1931     window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = false;
1932     if (IsClippedEx(bb, id, false))
1933         return false;
1934 
1935     // This is a sensible default, but widgets are free to override it after calling ItemAdd()
1936     ImGuiContext& g = *GImGui;
1937     if (IsMouseHoveringRect(bb.Min, bb.Max))
1938     {
1939         // Matching the behavior of IsHovered() but allow if ActiveId==window->MoveID (we clicked on the window background)
1940         // So that clicking on items with no active id such as Text() still returns true with IsItemHovered()
1941         window->DC.LastItemHoveredRect = true;
1942         if (g.HoveredRootWindow == window->RootWindow)
1943             if (g.ActiveId == 0 || (id && g.ActiveId == *id) || g.ActiveIdAllowOverlap || (g.ActiveId == window->MoveId))
1944                 if (IsWindowContentHoverable(window))
1945                     window->DC.LastItemHoveredAndUsable = true;
1946     }
1947 
1948     return true;
1949 }
1950 
IsClippedEx(const ImRect & bb,const ImGuiID * id,bool clip_even_when_logged)1951 bool ImGui::IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged)
1952 {
1953     ImGuiContext& g = *GImGui;
1954     ImGuiWindow* window = GetCurrentWindowRead();
1955 
1956     if (!bb.Overlaps(window->ClipRect))
1957         if (!id || *id != GImGui->ActiveId)
1958             if (clip_even_when_logged || !g.LogEnabled)
1959                 return true;
1960     return false;
1961 }
1962 
1963 // NB: This is an internal helper. The user-facing IsItemHovered() is using data emitted from ItemAdd(), with a slightly different logic.
IsHovered(const ImRect & bb,ImGuiID id,bool flatten_childs)1964 bool ImGui::IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs)
1965 {
1966     ImGuiContext& g = *GImGui;
1967     if (g.HoveredId == 0 || g.HoveredId == id || g.HoveredIdAllowOverlap)
1968     {
1969         ImGuiWindow* window = GetCurrentWindowRead();
1970         if (g.HoveredWindow == window || (flatten_childs && g.HoveredRootWindow == window->RootWindow))
1971             if ((g.ActiveId == 0 || g.ActiveId == id || g.ActiveIdAllowOverlap) && IsMouseHoveringRect(bb.Min, bb.Max))
1972                 if (IsWindowContentHoverable(g.HoveredRootWindow))
1973                     return true;
1974     }
1975     return false;
1976 }
1977 
FocusableItemRegister(ImGuiWindow * window,bool is_active,bool tab_stop)1978 bool ImGui::FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_stop)
1979 {
1980     ImGuiContext& g = *GImGui;
1981 
1982     const bool allow_keyboard_focus = window->DC.AllowKeyboardFocus;
1983     window->FocusIdxAllCounter++;
1984     if (allow_keyboard_focus)
1985         window->FocusIdxTabCounter++;
1986 
1987     // Process keyboard input at this point: TAB, Shift-TAB switch focus
1988     // We can always TAB out of a widget that doesn't allow tabbing in.
1989     if (tab_stop && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && is_active && IsKeyPressedMap(ImGuiKey_Tab))
1990     {
1991         // Modulo on index will be applied at the end of frame once we've got the total counter of items.
1992         window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1);
1993     }
1994 
1995     if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
1996         return true;
1997 
1998     if (allow_keyboard_focus)
1999         if (window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent)
2000             return true;
2001 
2002     return false;
2003 }
2004 
FocusableItemUnregister(ImGuiWindow * window)2005 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
2006 {
2007     window->FocusIdxAllCounter--;
2008     window->FocusIdxTabCounter--;
2009 }
2010 
CalcItemSize(ImVec2 size,float default_x,float default_y)2011 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y)
2012 {
2013     ImGuiContext& g = *GImGui;
2014     ImVec2 content_max;
2015     if (size.x < 0.0f || size.y < 0.0f)
2016         content_max = g.CurrentWindow->Pos + GetContentRegionMax();
2017     if (size.x <= 0.0f)
2018         size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x;
2019     if (size.y <= 0.0f)
2020         size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y;
2021     return size;
2022 }
2023 
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)2024 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
2025 {
2026     if (wrap_pos_x < 0.0f)
2027         return 0.0f;
2028 
2029     ImGuiWindow* window = GetCurrentWindowRead();
2030     if (wrap_pos_x == 0.0f)
2031         wrap_pos_x = GetContentRegionMax().x + window->Pos.x;
2032     else if (wrap_pos_x > 0.0f)
2033         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
2034 
2035     return ImMax(wrap_pos_x - pos.x, 1.0f);
2036 }
2037 
2038 //-----------------------------------------------------------------------------
2039 
MemAlloc(size_t sz)2040 void* ImGui::MemAlloc(size_t sz)
2041 {
2042     GImGui->IO.MetricsAllocs++;
2043     return GImGui->IO.MemAllocFn(sz);
2044 }
2045 
MemFree(void * ptr)2046 void ImGui::MemFree(void* ptr)
2047 {
2048     if (ptr) GImGui->IO.MetricsAllocs--;
2049     return GImGui->IO.MemFreeFn(ptr);
2050 }
2051 
GetClipboardText()2052 const char* ImGui::GetClipboardText()
2053 {
2054     return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : "";
2055 }
2056 
SetClipboardText(const char * text)2057 void ImGui::SetClipboardText(const char* text)
2058 {
2059     if (GImGui->IO.SetClipboardTextFn)
2060         GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text);
2061 }
2062 
GetVersion()2063 const char* ImGui::GetVersion()
2064 {
2065     return IMGUI_VERSION;
2066 }
2067 
2068 // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
2069 // Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module
GetCurrentContext()2070 ImGuiContext* ImGui::GetCurrentContext()
2071 {
2072     return GImGui;
2073 }
2074 
SetCurrentContext(ImGuiContext * ctx)2075 void ImGui::SetCurrentContext(ImGuiContext* ctx)
2076 {
2077 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
2078     IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
2079 #else
2080     GImGui = ctx;
2081 #endif
2082 }
2083 
CreateContext(void * (* malloc_fn)(size_t),void (* free_fn)(void *))2084 ImGuiContext* ImGui::CreateContext(void* (*malloc_fn)(size_t), void (*free_fn)(void*))
2085 {
2086     if (!malloc_fn) malloc_fn = malloc;
2087     ImGuiContext* ctx = (ImGuiContext*)malloc_fn(sizeof(ImGuiContext));
2088     IM_PLACEMENT_NEW(ctx) ImGuiContext();
2089     ctx->IO.MemAllocFn = malloc_fn;
2090     ctx->IO.MemFreeFn = free_fn ? free_fn : free;
2091     return ctx;
2092 }
2093 
DestroyContext(ImGuiContext * ctx)2094 void ImGui::DestroyContext(ImGuiContext* ctx)
2095 {
2096     void (*free_fn)(void*) = ctx->IO.MemFreeFn;
2097     ctx->~ImGuiContext();
2098     free_fn(ctx);
2099     if (GImGui == ctx)
2100         SetCurrentContext(NULL);
2101 }
2102 
GetIO()2103 ImGuiIO& ImGui::GetIO()
2104 {
2105     return GImGui->IO;
2106 }
2107 
GetStyle()2108 ImGuiStyle& ImGui::GetStyle()
2109 {
2110     return GImGui->Style;
2111 }
2112 
2113 // Same value as passed to your RenderDrawListsFn() function. valid after Render() and until the next call to NewFrame()
GetDrawData()2114 ImDrawData* ImGui::GetDrawData()
2115 {
2116     return GImGui->RenderDrawData.Valid ? &GImGui->RenderDrawData : NULL;
2117 }
2118 
GetTime()2119 float ImGui::GetTime()
2120 {
2121     return GImGui->Time;
2122 }
2123 
GetFrameCount()2124 int ImGui::GetFrameCount()
2125 {
2126     return GImGui->FrameCount;
2127 }
2128 
NewFrame()2129 void ImGui::NewFrame()
2130 {
2131     ImGuiContext& g = *GImGui;
2132 
2133     // Check user data
2134     IM_ASSERT(g.IO.DeltaTime >= 0.0f);               // Need a positive DeltaTime (zero is tolerated but will cause some timing issues)
2135     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f);
2136     IM_ASSERT(g.IO.Fonts->Fonts.Size > 0);           // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
2137     IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded());     // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
2138     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f);  // Invalid style setting
2139 
2140     if (!g.Initialized)
2141     {
2142         // Initialize on first frame
2143         g.LogClipboard = (ImGuiTextBuffer*)ImGui::MemAlloc(sizeof(ImGuiTextBuffer));
2144         IM_PLACEMENT_NEW(g.LogClipboard) ImGuiTextBuffer();
2145 
2146         IM_ASSERT(g.Settings.empty());
2147         LoadIniSettingsFromDisk(g.IO.IniFilename);
2148         g.Initialized = true;
2149     }
2150 
2151     SetCurrentFont(GetDefaultFont());
2152     IM_ASSERT(g.Font->IsLoaded());
2153 
2154     g.Time += g.IO.DeltaTime;
2155     g.FrameCount += 1;
2156     g.Tooltip[0] = '\0';
2157     g.OverlayDrawList.Clear();
2158     g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID);
2159     g.OverlayDrawList.PushClipRectFullScreen();
2160 
2161     // Mark rendering data as invalid to prevent user who may have a handle on it to use it
2162     g.RenderDrawData.Valid = false;
2163     g.RenderDrawData.CmdLists = NULL;
2164     g.RenderDrawData.CmdListsCount = g.RenderDrawData.TotalVtxCount = g.RenderDrawData.TotalIdxCount = 0;
2165 
2166     // Update inputs state
2167     if (g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0)
2168         g.IO.MousePos = ImVec2(-9999.0f, -9999.0f);
2169     if ((g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0) || (g.IO.MousePosPrev.x < 0 && g.IO.MousePosPrev.y < 0))   // if mouse just appeared or disappeared (negative coordinate) we cancel out movement in MouseDelta
2170         g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
2171     else
2172         g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
2173     g.IO.MousePosPrev = g.IO.MousePos;
2174     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
2175     {
2176         g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
2177         g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
2178         g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
2179         g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f;
2180         g.IO.MouseDoubleClicked[i] = false;
2181         if (g.IO.MouseClicked[i])
2182         {
2183             if (g.Time - g.IO.MouseClickedTime[i] < g.IO.MouseDoubleClickTime)
2184             {
2185                 if (ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
2186                     g.IO.MouseDoubleClicked[i] = true;
2187                 g.IO.MouseClickedTime[i] = -FLT_MAX;    // so the third click isn't turned into a double-click
2188             }
2189             else
2190             {
2191                 g.IO.MouseClickedTime[i] = g.Time;
2192             }
2193             g.IO.MouseClickedPos[i] = g.IO.MousePos;
2194             g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
2195         }
2196         else if (g.IO.MouseDown[i])
2197         {
2198             g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]));
2199         }
2200     }
2201     memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
2202     for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
2203         g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f;
2204 
2205     // Calculate frame-rate for the user, as a purely luxurious feature
2206     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
2207     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
2208     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
2209     g.IO.Framerate = 1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame));
2210 
2211     // Clear reference to active widget if the widget isn't alive anymore
2212     g.HoveredIdPreviousFrame = g.HoveredId;
2213     g.HoveredId = 0;
2214     g.HoveredIdAllowOverlap = false;
2215     if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
2216         ClearActiveID();
2217     g.ActiveIdPreviousFrame = g.ActiveId;
2218     g.ActiveIdIsAlive = false;
2219     g.ActiveIdIsJustActivated = false;
2220 
2221     // Handle user moving window (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows.
2222     if (g.MovedWindowMoveId && g.MovedWindowMoveId == g.ActiveId)
2223     {
2224         KeepAliveID(g.MovedWindowMoveId);
2225         IM_ASSERT(g.MovedWindow && g.MovedWindow->RootWindow);
2226         IM_ASSERT(g.MovedWindow->RootWindow->MoveId == g.MovedWindowMoveId);
2227         if (g.IO.MouseDown[0])
2228         {
2229             if (!(g.MovedWindow->Flags & ImGuiWindowFlags_NoMove))
2230             {
2231                 g.MovedWindow->PosFloat += g.IO.MouseDelta;
2232                 if (!(g.MovedWindow->Flags & ImGuiWindowFlags_NoSavedSettings) && (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f))
2233                     MarkIniSettingsDirty();
2234             }
2235             FocusWindow(g.MovedWindow);
2236         }
2237         else
2238         {
2239             ClearActiveID();
2240             g.MovedWindow = NULL;
2241             g.MovedWindowMoveId = 0;
2242         }
2243     }
2244     else
2245     {
2246         g.MovedWindow = NULL;
2247         g.MovedWindowMoveId = 0;
2248     }
2249 
2250     // Delay saving settings so we don't spam disk too much
2251     if (g.SettingsDirtyTimer > 0.0f)
2252     {
2253         g.SettingsDirtyTimer -= g.IO.DeltaTime;
2254         if (g.SettingsDirtyTimer <= 0.0f)
2255             SaveIniSettingsToDisk(g.IO.IniFilename);
2256     }
2257 
2258     // Find the window we are hovering. Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow
2259     g.HoveredWindow = g.MovedWindow ? g.MovedWindow : FindHoveredWindow(g.IO.MousePos, false);
2260     if (g.HoveredWindow && (g.HoveredWindow->Flags & ImGuiWindowFlags_ChildWindow))
2261         g.HoveredRootWindow = g.HoveredWindow->RootWindow;
2262     else
2263         g.HoveredRootWindow = g.MovedWindow ? g.MovedWindow->RootWindow : FindHoveredWindow(g.IO.MousePos, true);
2264 
2265     if (ImGuiWindow* modal_window = GetFrontMostModalRootWindow())
2266     {
2267         g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f);
2268         ImGuiWindow* window = g.HoveredRootWindow;
2269         while (window && window != modal_window)
2270             window = window->ParentWindow;
2271         if (!window)
2272             g.HoveredRootWindow = g.HoveredWindow = NULL;
2273     }
2274     else
2275     {
2276         g.ModalWindowDarkeningRatio = 0.0f;
2277     }
2278 
2279     // Are we using inputs? Tell user so they can capture/discard the inputs away from the rest of their application.
2280     // When clicking outside of a window we assume the click is owned by the application and won't request capture. We need to track click ownership.
2281     int mouse_earliest_button_down = -1;
2282     bool mouse_any_down = false;
2283     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
2284     {
2285         if (g.IO.MouseClicked[i])
2286             g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
2287         mouse_any_down |= g.IO.MouseDown[i];
2288         if (g.IO.MouseDown[i])
2289             if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[mouse_earliest_button_down] > g.IO.MouseClickedTime[i])
2290                 mouse_earliest_button_down = i;
2291     }
2292     bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
2293     if (g.CaptureMouseNextFrame != -1)
2294         g.IO.WantCaptureMouse = (g.CaptureMouseNextFrame != 0);
2295     else
2296         g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.ActiveId != 0) || (!g.OpenPopupStack.empty());
2297     g.IO.WantCaptureKeyboard = (g.CaptureKeyboardNextFrame != -1) ? (g.CaptureKeyboardNextFrame != 0) : (g.ActiveId != 0);
2298     g.IO.WantTextInput = (g.ActiveId != 0 && g.InputTextState.Id == g.ActiveId);
2299     g.MouseCursor = ImGuiMouseCursor_Arrow;
2300     g.CaptureMouseNextFrame = g.CaptureKeyboardNextFrame = -1;
2301     g.OsImePosRequest = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
2302 
2303     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
2304     if (!mouse_avail_to_imgui)
2305         g.HoveredWindow = g.HoveredRootWindow = NULL;
2306 
2307     // Scale & Scrolling
2308     if (g.HoveredWindow && g.IO.MouseWheel != 0.0f && !g.HoveredWindow->Collapsed)
2309     {
2310         ImGuiWindow* window = g.HoveredWindow;
2311         if (g.IO.KeyCtrl)
2312         {
2313             if (g.IO.FontAllowUserScaling)
2314             {
2315                 // Zoom / Scale window
2316                 float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
2317                 float scale = new_font_scale / window->FontWindowScale;
2318                 window->FontWindowScale = new_font_scale;
2319 
2320                 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
2321                 window->Pos += offset;
2322                 window->PosFloat += offset;
2323                 window->Size *= scale;
2324                 window->SizeFull *= scale;
2325             }
2326         }
2327         else if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse))
2328         {
2329             // Scroll
2330             const int scroll_lines = (window->Flags & ImGuiWindowFlags_ComboBox) ? 3 : 5;
2331             SetWindowScrollY(window, window->Scroll.y - g.IO.MouseWheel * window->CalcFontSize() * scroll_lines);
2332         }
2333     }
2334 
2335     // Pressing TAB activate widget focus
2336     // NB: Don't discard FocusedWindow if it isn't active, so that a window that go on/off programatically won't lose its keyboard focus.
2337     if (g.ActiveId == 0 && g.FocusedWindow != NULL && g.FocusedWindow->Active && IsKeyPressedMap(ImGuiKey_Tab, false))
2338         g.FocusedWindow->FocusIdxTabRequestNext = 0;
2339 
2340     // Mark all windows as not visible
2341     for (int i = 0; i != g.Windows.Size; i++)
2342     {
2343         ImGuiWindow* window = g.Windows[i];
2344         window->WasActive = window->Active;
2345         window->Active = false;
2346         window->Accessed = false;
2347     }
2348 
2349     // Closing the focused window restore focus to the first active root window in descending z-order
2350     if (g.FocusedWindow && !g.FocusedWindow->WasActive)
2351         for (int i = g.Windows.Size-1; i >= 0; i--)
2352             if (g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow))
2353             {
2354                 FocusWindow(g.Windows[i]);
2355                 break;
2356             }
2357 
2358     // No window should be open at the beginning of the frame.
2359     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
2360     g.CurrentWindowStack.resize(0);
2361     g.CurrentPopupStack.resize(0);
2362     CloseInactivePopups();
2363 
2364     // Create implicit window - we will only render it if the user has added something to it.
2365     ImGui::SetNextWindowSize(ImVec2(400,400), ImGuiSetCond_FirstUseEver);
2366     ImGui::Begin("Debug");
2367 }
2368 
2369 // NB: behavior of ImGui after Shutdown() is not tested/guaranteed at the moment. This function is merely here to free heap allocations.
Shutdown()2370 void ImGui::Shutdown()
2371 {
2372     ImGuiContext& g = *GImGui;
2373 
2374     // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame)
2375     if (g.IO.Fonts) // Testing for NULL to allow user to NULLify in case of running Shutdown() on multiple contexts. Bit hacky.
2376         g.IO.Fonts->Clear();
2377 
2378     // Cleanup of other data are conditional on actually having used ImGui.
2379     if (!g.Initialized)
2380         return;
2381 
2382     SaveIniSettingsToDisk(g.IO.IniFilename);
2383 
2384     for (int i = 0; i < g.Windows.Size; i++)
2385     {
2386         g.Windows[i]->~ImGuiWindow();
2387         ImGui::MemFree(g.Windows[i]);
2388     }
2389     g.Windows.clear();
2390     g.WindowsSortBuffer.clear();
2391     g.CurrentWindow = NULL;
2392     g.CurrentWindowStack.clear();
2393     g.FocusedWindow = NULL;
2394     g.HoveredWindow = NULL;
2395     g.HoveredRootWindow = NULL;
2396     g.ActiveIdWindow = NULL;
2397     g.MovedWindow = NULL;
2398     for (int i = 0; i < g.Settings.Size; i++)
2399         ImGui::MemFree(g.Settings[i].Name);
2400     g.Settings.clear();
2401     g.ColorModifiers.clear();
2402     g.StyleModifiers.clear();
2403     g.FontStack.clear();
2404     g.OpenPopupStack.clear();
2405     g.CurrentPopupStack.clear();
2406     g.SetNextWindowSizeConstraintCallback = NULL;
2407     g.SetNextWindowSizeConstraintCallbackUserData = NULL;
2408     for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2409         g.RenderDrawLists[i].clear();
2410     g.OverlayDrawList.ClearFreeMemory();
2411     g.ColorEditModeStorage.Clear();
2412     if (g.PrivateClipboard)
2413     {
2414         ImGui::MemFree(g.PrivateClipboard);
2415         g.PrivateClipboard = NULL;
2416     }
2417     g.InputTextState.Text.clear();
2418     g.InputTextState.InitialText.clear();
2419     g.InputTextState.TempTextBuffer.clear();
2420 
2421     if (g.LogFile && g.LogFile != stdout)
2422     {
2423         fclose(g.LogFile);
2424         g.LogFile = NULL;
2425     }
2426     if (g.LogClipboard)
2427     {
2428         g.LogClipboard->~ImGuiTextBuffer();
2429         ImGui::MemFree(g.LogClipboard);
2430     }
2431 
2432     g.Initialized = false;
2433 }
2434 
FindWindowSettings(const char * name)2435 static ImGuiIniData* FindWindowSettings(const char* name)
2436 {
2437     ImGuiContext& g = *GImGui;
2438     ImGuiID id = ImHash(name, 0);
2439     for (int i = 0; i != g.Settings.Size; i++)
2440     {
2441         ImGuiIniData* ini = &g.Settings[i];
2442         if (ini->Id == id)
2443             return ini;
2444     }
2445     return NULL;
2446 }
2447 
AddWindowSettings(const char * name)2448 static ImGuiIniData* AddWindowSettings(const char* name)
2449 {
2450     GImGui->Settings.resize(GImGui->Settings.Size + 1);
2451     ImGuiIniData* ini = &GImGui->Settings.back();
2452     ini->Name = ImStrdup(name);
2453     ini->Id = ImHash(name, 0);
2454     ini->Collapsed = false;
2455     ini->Pos = ImVec2(FLT_MAX,FLT_MAX);
2456     ini->Size = ImVec2(0,0);
2457     return ini;
2458 }
2459 
2460 // Zero-tolerance, poor-man .ini parsing
2461 // FIXME: Write something less rubbish
LoadIniSettingsFromDisk(const char * ini_filename)2462 static void LoadIniSettingsFromDisk(const char* ini_filename)
2463 {
2464     ImGuiContext& g = *GImGui;
2465     if (!ini_filename)
2466         return;
2467 
2468     int file_size;
2469     char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_size, 1);
2470     if (!file_data)
2471         return;
2472 
2473     ImGuiIniData* settings = NULL;
2474     const char* buf_end = file_data + file_size;
2475     for (const char* line_start = file_data; line_start < buf_end; )
2476     {
2477         const char* line_end = line_start;
2478         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
2479             line_end++;
2480 
2481         if (line_start[0] == '[' && line_end > line_start && line_end[-1] == ']')
2482         {
2483             char name[64];
2484             ImFormatString(name, IM_ARRAYSIZE(name), "%.*s", (int)(line_end-line_start-2), line_start+1);
2485             settings = FindWindowSettings(name);
2486             if (!settings)
2487                 settings = AddWindowSettings(name);
2488         }
2489         else if (settings)
2490         {
2491             float x, y;
2492             int i;
2493             if (sscanf(line_start, "Pos=%f,%f", &x, &y) == 2)
2494                 settings->Pos = ImVec2(x, y);
2495             else if (sscanf(line_start, "Size=%f,%f", &x, &y) == 2)
2496                 settings->Size = ImMax(ImVec2(x, y), g.Style.WindowMinSize);
2497             else if (sscanf(line_start, "Collapsed=%d", &i) == 1)
2498                 settings->Collapsed = (i != 0);
2499         }
2500 
2501         line_start = line_end+1;
2502     }
2503 
2504     ImGui::MemFree(file_data);
2505 }
2506 
SaveIniSettingsToDisk(const char * ini_filename)2507 static void SaveIniSettingsToDisk(const char* ini_filename)
2508 {
2509     ImGuiContext& g = *GImGui;
2510     g.SettingsDirtyTimer = 0.0f;
2511     if (!ini_filename)
2512         return;
2513 
2514     // Gather data from windows that were active during this session
2515     for (int i = 0; i != g.Windows.Size; i++)
2516     {
2517         ImGuiWindow* window = g.Windows[i];
2518         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
2519             continue;
2520         ImGuiIniData* settings = FindWindowSettings(window->Name);
2521         settings->Pos = window->Pos;
2522         settings->Size = window->SizeFull;
2523         settings->Collapsed = window->Collapsed;
2524     }
2525 
2526     // Write .ini file
2527     // If a window wasn't opened in this session we preserve its settings
2528     FILE* f = ImFileOpen(ini_filename, "wt");
2529     if (!f)
2530         return;
2531     for (int i = 0; i != g.Settings.Size; i++)
2532     {
2533         const ImGuiIniData* settings = &g.Settings[i];
2534         if (settings->Pos.x == FLT_MAX)
2535             continue;
2536         const char* name = settings->Name;
2537         if (const char* p = strstr(name, "###"))  // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
2538             name = p;
2539         fprintf(f, "[%s]\n", name);
2540         fprintf(f, "Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
2541         fprintf(f, "Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
2542         fprintf(f, "Collapsed=%d\n", settings->Collapsed);
2543         fprintf(f, "\n");
2544     }
2545 
2546     fclose(f);
2547 }
2548 
MarkIniSettingsDirty()2549 static void MarkIniSettingsDirty()
2550 {
2551     ImGuiContext& g = *GImGui;
2552     if (g.SettingsDirtyTimer <= 0.0f)
2553         g.SettingsDirtyTimer = g.IO.IniSavingRate;
2554 }
2555 
2556 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)2557 static int ChildWindowComparer(const void* lhs, const void* rhs)
2558 {
2559     const ImGuiWindow* a = *(const ImGuiWindow**)lhs;
2560     const ImGuiWindow* b = *(const ImGuiWindow**)rhs;
2561     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
2562         return d;
2563     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
2564         return d;
2565     if (int d = (a->Flags & ImGuiWindowFlags_ComboBox) - (b->Flags & ImGuiWindowFlags_ComboBox))
2566         return d;
2567     return (a->IndexWithinParent - b->IndexWithinParent);
2568 }
2569 
AddWindowToSortedBuffer(ImVector<ImGuiWindow * > & out_sorted_windows,ImGuiWindow * window)2570 static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>& out_sorted_windows, ImGuiWindow* window)
2571 {
2572     out_sorted_windows.push_back(window);
2573     if (window->Active)
2574     {
2575         int count = window->DC.ChildWindows.Size;
2576         if (count > 1)
2577             qsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
2578         for (int i = 0; i < count; i++)
2579         {
2580             ImGuiWindow* child = window->DC.ChildWindows[i];
2581             if (child->Active)
2582                 AddWindowToSortedBuffer(out_sorted_windows, child);
2583         }
2584     }
2585 }
2586 
AddDrawListToRenderList(ImVector<ImDrawList * > & out_render_list,ImDrawList * draw_list)2587 static void AddDrawListToRenderList(ImVector<ImDrawList*>& out_render_list, ImDrawList* draw_list)
2588 {
2589     if (draw_list->CmdBuffer.empty())
2590         return;
2591 
2592     // Remove trailing command if unused
2593     ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
2594     if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
2595     {
2596         draw_list->CmdBuffer.pop_back();
2597         if (draw_list->CmdBuffer.empty())
2598             return;
2599     }
2600 
2601     // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
2602     IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
2603     IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
2604     IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
2605 
2606     // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = 2 bytes = 64K vertices)
2607     // If this assert triggers because you are drawing lots of stuff manually, A) workaround by calling BeginChild()/EndChild() to put your draw commands in multiple draw lists, B) #define ImDrawIdx to a 'unsigned int' in imconfig.h and render accordingly.
2608     IM_ASSERT((int64_t)draw_list->_VtxCurrentIdx <= ((int64_t)1L << (sizeof(ImDrawIdx)*8)));  // Too many vertices in same ImDrawList. See comment above.
2609 
2610     out_render_list.push_back(draw_list);
2611     GImGui->IO.MetricsRenderVertices += draw_list->VtxBuffer.Size;
2612     GImGui->IO.MetricsRenderIndices += draw_list->IdxBuffer.Size;
2613 }
2614 
AddWindowToRenderList(ImVector<ImDrawList * > & out_render_list,ImGuiWindow * window)2615 static void AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiWindow* window)
2616 {
2617     AddDrawListToRenderList(out_render_list, window->DrawList);
2618     for (int i = 0; i < window->DC.ChildWindows.Size; i++)
2619     {
2620         ImGuiWindow* child = window->DC.ChildWindows[i];
2621         if (!child->Active) // clipped children may have been marked not active
2622             continue;
2623         if ((child->Flags & ImGuiWindowFlags_Popup) && child->HiddenFrames > 0)
2624             continue;
2625         AddWindowToRenderList(out_render_list, child);
2626     }
2627 }
2628 
2629 // When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result.
PushClipRect(const ImVec2 & clip_rect_min,const ImVec2 & clip_rect_max,bool intersect_with_current_clip_rect)2630 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
2631 {
2632     ImGuiWindow* window = GetCurrentWindow();
2633     window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
2634     window->ClipRect = window->DrawList->_ClipRectStack.back();
2635 }
2636 
PopClipRect()2637 void ImGui::PopClipRect()
2638 {
2639     ImGuiWindow* window = GetCurrentWindow();
2640     window->DrawList->PopClipRect();
2641     window->ClipRect = window->DrawList->_ClipRectStack.back();
2642 }
2643 
2644 // This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal.
EndFrame()2645 void ImGui::EndFrame()
2646 {
2647     ImGuiContext& g = *GImGui;
2648     IM_ASSERT(g.Initialized);                       // Forgot to call ImGui::NewFrame()
2649     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // ImGui::EndFrame() called multiple times, or forgot to call ImGui::NewFrame() again
2650 
2651     // Render tooltip
2652     if (g.Tooltip[0])
2653     {
2654         ImGui::BeginTooltip();
2655         ImGui::TextUnformatted(g.Tooltip);
2656         ImGui::EndTooltip();
2657     }
2658 
2659     // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
2660     if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.OsImePosRequest - g.OsImePosSet) > 0.0001f)
2661     {
2662         g.IO.ImeSetInputScreenPosFn((int)g.OsImePosRequest.x, (int)g.OsImePosRequest.y);
2663         g.OsImePosSet = g.OsImePosRequest;
2664     }
2665 
2666     // Hide implicit "Debug" window if it hasn't been used
2667     IM_ASSERT(g.CurrentWindowStack.Size == 1);    // Mismatched Begin()/End() calls
2668     if (g.CurrentWindow && !g.CurrentWindow->Accessed)
2669         g.CurrentWindow->Active = false;
2670     ImGui::End();
2671 
2672     // Click to focus window and start moving (after we're done with all our widgets)
2673     if (g.ActiveId == 0 && g.HoveredId == 0 && g.IO.MouseClicked[0])
2674     {
2675         if (!(g.FocusedWindow && !g.FocusedWindow->WasActive && g.FocusedWindow->Active)) // Unless we just made a popup appear
2676         {
2677             if (g.HoveredRootWindow != NULL)
2678             {
2679                 FocusWindow(g.HoveredWindow);
2680                 if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove))
2681                 {
2682                     g.MovedWindow = g.HoveredWindow;
2683                     g.MovedWindowMoveId = g.HoveredRootWindow->MoveId;
2684                     SetActiveID(g.MovedWindowMoveId, g.HoveredRootWindow);
2685                 }
2686             }
2687             else if (g.FocusedWindow != NULL && GetFrontMostModalRootWindow() == NULL)
2688             {
2689                 // Clicking on void disable focus
2690                 FocusWindow(NULL);
2691             }
2692         }
2693     }
2694 
2695     // Sort the window list so that all child windows are after their parent
2696     // We cannot do that on FocusWindow() because childs may not exist yet
2697     g.WindowsSortBuffer.resize(0);
2698     g.WindowsSortBuffer.reserve(g.Windows.Size);
2699     for (int i = 0; i != g.Windows.Size; i++)
2700     {
2701         ImGuiWindow* window = g.Windows[i];
2702         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
2703             continue;
2704         AddWindowToSortedBuffer(g.WindowsSortBuffer, window);
2705     }
2706     IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size);  // we done something wrong
2707     g.Windows.swap(g.WindowsSortBuffer);
2708 
2709     // Clear Input data for next frame
2710     g.IO.MouseWheel = 0.0f;
2711     memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
2712 
2713     g.FrameCountEnded = g.FrameCount;
2714 }
2715 
Render()2716 void ImGui::Render()
2717 {
2718     ImGuiContext& g = *GImGui;
2719     IM_ASSERT(g.Initialized);   // Forgot to call ImGui::NewFrame()
2720 
2721     if (g.FrameCountEnded != g.FrameCount)
2722         ImGui::EndFrame();
2723     g.FrameCountRendered = g.FrameCount;
2724 
2725     // Skip render altogether if alpha is 0.0
2726     // Note that vertex buffers have been created and are wasted, so it is best practice that you don't create windows in the first place, or consistently respond to Begin() returning false.
2727     if (g.Style.Alpha > 0.0f)
2728     {
2729         // Gather windows to render
2730         g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0;
2731         for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2732             g.RenderDrawLists[i].resize(0);
2733         for (int i = 0; i != g.Windows.Size; i++)
2734         {
2735             ImGuiWindow* window = g.Windows[i];
2736             if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0)
2737             {
2738                 // FIXME: Generalize this with a proper layering system so e.g. user can draw in specific layers, below text, ..
2739                 g.IO.MetricsActiveWindows++;
2740                 if (window->Flags & ImGuiWindowFlags_Popup)
2741                     AddWindowToRenderList(g.RenderDrawLists[1], window);
2742                 else if (window->Flags & ImGuiWindowFlags_Tooltip)
2743                     AddWindowToRenderList(g.RenderDrawLists[2], window);
2744                 else
2745                     AddWindowToRenderList(g.RenderDrawLists[0], window);
2746             }
2747         }
2748 
2749         // Flatten layers
2750         int n = g.RenderDrawLists[0].Size;
2751         int flattened_size = n;
2752         for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2753             flattened_size += g.RenderDrawLists[i].Size;
2754         g.RenderDrawLists[0].resize(flattened_size);
2755         for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2756         {
2757             ImVector<ImDrawList*>& layer = g.RenderDrawLists[i];
2758             if (layer.empty())
2759                 continue;
2760             memcpy(&g.RenderDrawLists[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
2761             n += layer.Size;
2762         }
2763 
2764         // Draw software mouse cursor if requested
2765         if (g.IO.MouseDrawCursor)
2766         {
2767             const ImGuiMouseCursorData& cursor_data = g.MouseCursorData[g.MouseCursor];
2768             const ImVec2 pos = g.IO.MousePos - cursor_data.HotOffset;
2769             const ImVec2 size = cursor_data.Size;
2770             const ImTextureID tex_id = g.IO.Fonts->TexID;
2771             g.OverlayDrawList.PushTextureID(tex_id);
2772             g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(1,0), pos+ImVec2(1,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,48));        // Shadow
2773             g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(2,0), pos+ImVec2(2,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,48));        // Shadow
2774             g.OverlayDrawList.AddImage(tex_id, pos,             pos + size,             cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,255));       // Black border
2775             g.OverlayDrawList.AddImage(tex_id, pos,             pos + size,             cursor_data.TexUvMin[0], cursor_data.TexUvMax[0], IM_COL32(255,255,255,255)); // White fill
2776             g.OverlayDrawList.PopTextureID();
2777         }
2778         if (!g.OverlayDrawList.VtxBuffer.empty())
2779             AddDrawListToRenderList(g.RenderDrawLists[0], &g.OverlayDrawList);
2780 
2781         // Setup draw data
2782         g.RenderDrawData.Valid = true;
2783         g.RenderDrawData.CmdLists = (g.RenderDrawLists[0].Size > 0) ? &g.RenderDrawLists[0][0] : NULL;
2784         g.RenderDrawData.CmdListsCount = g.RenderDrawLists[0].Size;
2785         g.RenderDrawData.TotalVtxCount = g.IO.MetricsRenderVertices;
2786         g.RenderDrawData.TotalIdxCount = g.IO.MetricsRenderIndices;
2787 
2788         // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData()
2789         if (g.RenderDrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
2790             g.IO.RenderDrawListsFn(&g.RenderDrawData);
2791     }
2792 }
2793 
FindRenderedTextEnd(const char * text,const char * text_end)2794 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2795 {
2796     const char* text_display_end = text;
2797     if (!text_end)
2798         text_end = (const char*)-1;
2799 
2800     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2801         text_display_end++;
2802     return text_display_end;
2803 }
2804 
2805 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)2806 void ImGui::LogText(const char* fmt, ...)
2807 {
2808     ImGuiContext& g = *GImGui;
2809     if (!g.LogEnabled)
2810         return;
2811 
2812     va_list args;
2813     va_start(args, fmt);
2814     if (g.LogFile)
2815     {
2816         vfprintf(g.LogFile, fmt, args);
2817     }
2818     else
2819     {
2820         g.LogClipboard->appendv(fmt, args);
2821     }
2822     va_end(args);
2823 }
2824 
2825 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
2826 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 & ref_pos,const char * text,const char * text_end)2827 static void LogRenderedText(const ImVec2& ref_pos, const char* text, const char* text_end)
2828 {
2829     ImGuiContext& g = *GImGui;
2830     ImGuiWindow* window = ImGui::GetCurrentWindowRead();
2831 
2832     if (!text_end)
2833         text_end = ImGui::FindRenderedTextEnd(text, text_end);
2834 
2835     const bool log_new_line = ref_pos.y > window->DC.LogLinePosY+1;
2836     window->DC.LogLinePosY = ref_pos.y;
2837 
2838     const char* text_remaining = text;
2839     if (g.LogStartDepth > window->DC.TreeDepth)  // Re-adjust padding if we have popped out of our starting depth
2840         g.LogStartDepth = window->DC.TreeDepth;
2841     const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth);
2842     for (;;)
2843     {
2844         // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
2845         const char* line_end = text_remaining;
2846         while (line_end < text_end)
2847             if (*line_end == '\n')
2848                 break;
2849             else
2850                 line_end++;
2851         if (line_end >= text_end)
2852             line_end = NULL;
2853 
2854         const bool is_first_line = (text == text_remaining);
2855         bool is_last_line = false;
2856         if (line_end == NULL)
2857         {
2858             is_last_line = true;
2859             line_end = text_end;
2860         }
2861         if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0))
2862         {
2863             const int char_count = (int)(line_end - text_remaining);
2864             if (log_new_line || !is_first_line)
2865                 ImGui::LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining);
2866             else
2867                 ImGui::LogText(" %.*s", char_count, text_remaining);
2868         }
2869 
2870         if (is_last_line)
2871             break;
2872         text_remaining = line_end + 1;
2873     }
2874 }
2875 
2876 // Internal ImGui functions to render text
2877 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2878 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2879 {
2880     ImGuiContext& g = *GImGui;
2881     ImGuiWindow* window = GetCurrentWindow();
2882 
2883     // Hide anything after a '##' string
2884     const char* text_display_end;
2885     if (hide_text_after_hash)
2886     {
2887         text_display_end = FindRenderedTextEnd(text, text_end);
2888     }
2889     else
2890     {
2891         if (!text_end)
2892             text_end = text + strlen(text); // FIXME-OPT
2893         text_display_end = text_end;
2894     }
2895 
2896     const int text_len = (int)(text_display_end - text);
2897     if (text_len > 0)
2898     {
2899         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2900         if (g.LogEnabled)
2901             LogRenderedText(pos, text, text_display_end);
2902     }
2903 }
2904 
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2905 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2906 {
2907     ImGuiContext& g = *GImGui;
2908     ImGuiWindow* window = GetCurrentWindow();
2909 
2910     if (!text_end)
2911         text_end = text + strlen(text); // FIXME-OPT
2912 
2913     const int text_len = (int)(text_end - text);
2914     if (text_len > 0)
2915     {
2916         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2917         if (g.LogEnabled)
2918             LogRenderedText(pos, text, text_end);
2919     }
2920 }
2921 
2922 // Default clip_rect uses (pos_min,pos_max)
2923 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
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)2924 void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
2925 {
2926     // Hide anything after a '##' string
2927     const char* text_display_end = FindRenderedTextEnd(text, text_end);
2928     const int text_len = (int)(text_display_end - text);
2929     if (text_len == 0)
2930         return;
2931 
2932     ImGuiContext& g = *GImGui;
2933     ImGuiWindow* window = GetCurrentWindow();
2934 
2935     // Perform CPU side clipping for single clipped element to avoid using scissor state
2936     ImVec2 pos = pos_min;
2937     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2938 
2939     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2940     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2941     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2942     if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2943         need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2944 
2945     // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2946     if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2947     if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2948 
2949     // Render
2950     if (need_clipping)
2951     {
2952         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2953         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2954     }
2955     else
2956     {
2957         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2958     }
2959     if (g.LogEnabled)
2960         LogRenderedText(pos, text, text_display_end);
2961 }
2962 
2963 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2964 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2965 {
2966     ImGuiWindow* window = GetCurrentWindow();
2967 
2968     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2969     if (border && (window->Flags & ImGuiWindowFlags_ShowBorders))
2970     {
2971         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding);
2972         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding);
2973     }
2974 }
2975 
2976 // Render a triangle to denote expanded/collapsed state
RenderCollapseTriangle(ImVec2 p_min,bool is_open,float scale)2977 void ImGui::RenderCollapseTriangle(ImVec2 p_min, bool is_open, float scale)
2978 {
2979     ImGuiContext& g = *GImGui;
2980     ImGuiWindow* window = GetCurrentWindow();
2981 
2982     const float h = g.FontSize * 1.00f;
2983     const float r = h * 0.40f * scale;
2984     ImVec2 center = p_min + ImVec2(h*0.50f, h*0.50f*scale);
2985 
2986     ImVec2 a, b, c;
2987     if (is_open)
2988     {
2989         center.y -= r*0.25f;
2990         a = center + ImVec2(0,1)*r;
2991         b = center + ImVec2(-0.866f,-0.5f)*r;
2992         c = center + ImVec2(0.866f,-0.5f)*r;
2993     }
2994     else
2995     {
2996         a = center + ImVec2(1,0)*r;
2997         b = center + ImVec2(-0.500f,0.866f)*r;
2998         c = center + ImVec2(-0.500f,-0.866f)*r;
2999     }
3000 
3001     window->DrawList->AddTriangleFilled(a, b, c, GetColorU32(ImGuiCol_Text));
3002 }
3003 
RenderBullet(ImVec2 pos)3004 void ImGui::RenderBullet(ImVec2 pos)
3005 {
3006     ImGuiWindow* window = GetCurrentWindow();
3007     window->DrawList->AddCircleFilled(pos, GImGui->FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8);
3008 }
3009 
RenderCheckMark(ImVec2 pos,ImU32 col)3010 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col)
3011 {
3012     ImGuiContext& g = *GImGui;
3013     ImGuiWindow* window = GetCurrentWindow();
3014 
3015     ImVec2 a, b, c;
3016     float start_x = (float)(int)(g.FontSize * 0.307f + 0.5f);
3017     float rem_third = (float)(int)((g.FontSize - start_x) / 3.0f);
3018     a.x = pos.x + 0.5f + start_x;
3019     b.x = a.x + rem_third;
3020     c.x = a.x + rem_third * 3.0f;
3021     b.y = pos.y - 1.0f + (float)(int)(g.Font->Ascent * (g.FontSize / g.Font->FontSize) + 0.5f) + (float)(int)(g.Font->DisplayOffset.y);
3022     a.y = b.y - rem_third;
3023     c.y = b.y - rem_third * 2.0f;
3024 
3025     window->DrawList->PathLineTo(a);
3026     window->DrawList->PathLineTo(b);
3027     window->DrawList->PathLineTo(c);
3028     window->DrawList->PathStroke(col, false);
3029 }
3030 
3031 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
3032 // CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize)
CalcTextSize(const char * text,const char * text_end,bool hide_text_after_double_hash,float wrap_width)3033 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
3034 {
3035     ImGuiContext& g = *GImGui;
3036 
3037     const char* text_display_end;
3038     if (hide_text_after_double_hash)
3039         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
3040     else
3041         text_display_end = text_end;
3042 
3043     ImFont* font = g.Font;
3044     const float font_size = g.FontSize;
3045     if (text == text_display_end)
3046         return ImVec2(0.0f, font_size);
3047     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
3048 
3049     // Cancel out character spacing for the last character of a line (it is baked into glyph->XAdvance field)
3050     const float font_scale = font_size / font->FontSize;
3051     const float character_spacing_x = 1.0f * font_scale;
3052     if (text_size.x > 0.0f)
3053         text_size.x -= character_spacing_x;
3054     text_size.x = (float)(int)(text_size.x + 0.95f);
3055 
3056     return text_size;
3057 }
3058 
3059 // Helper to calculate coarse clipping of large list of evenly sized items.
3060 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
3061 // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX
CalcListClipping(int items_count,float items_height,int * out_items_display_start,int * out_items_display_end)3062 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
3063 {
3064     ImGuiContext& g = *GImGui;
3065     ImGuiWindow* window = GetCurrentWindowRead();
3066     if (g.LogEnabled)
3067     {
3068         // If logging is active, do not perform any clipping
3069         *out_items_display_start = 0;
3070         *out_items_display_end = items_count;
3071         return;
3072     }
3073     if (window->SkipItems)
3074     {
3075         *out_items_display_start = *out_items_display_end = 0;
3076         return;
3077     }
3078 
3079     const ImVec2 pos = window->DC.CursorPos;
3080     int start = (int)((window->ClipRect.Min.y - pos.y) / items_height);
3081     int end = (int)((window->ClipRect.Max.y - pos.y) / items_height);
3082     start = ImClamp(start, 0, items_count);
3083     end = ImClamp(end + 1, start, items_count);
3084     *out_items_display_start = start;
3085     *out_items_display_end = end;
3086 }
3087 
3088 // Find window given position, search front-to-back
3089 // FIXME: Note that we have a lag here because WindowRectClipped is updated in Begin() so windows moved by user via SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is called, aka before the next Begin(). Moving window thankfully isn't affected.
FindHoveredWindow(ImVec2 pos,bool excluding_childs)3090 static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs)
3091 {
3092     ImGuiContext& g = *GImGui;
3093     for (int i = g.Windows.Size-1; i >= 0; i--)
3094     {
3095         ImGuiWindow* window = g.Windows[i];
3096         if (!window->Active)
3097             continue;
3098         if (window->Flags & ImGuiWindowFlags_NoInputs)
3099             continue;
3100         if (excluding_childs && (window->Flags & ImGuiWindowFlags_ChildWindow) != 0)
3101             continue;
3102 
3103         // Using the clipped AABB so a child window will typically be clipped by its parent.
3104         ImRect bb(window->WindowRectClipped.Min - g.Style.TouchExtraPadding, window->WindowRectClipped.Max + g.Style.TouchExtraPadding);
3105         if (bb.Contains(pos))
3106             return window;
3107     }
3108     return NULL;
3109 }
3110 
3111 // Test if mouse cursor is hovering given rectangle
3112 // NB- Rectangle is clipped by our current clip setting
3113 // NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
IsMouseHoveringRect(const ImVec2 & r_min,const ImVec2 & r_max,bool clip)3114 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
3115 {
3116     ImGuiContext& g = *GImGui;
3117     ImGuiWindow* window = GetCurrentWindowRead();
3118 
3119     // Clip
3120     ImRect rect_clipped(r_min, r_max);
3121     if (clip)
3122         rect_clipped.Clip(window->ClipRect);
3123 
3124     // Expand for touch input
3125     const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
3126     return rect_for_touch.Contains(g.IO.MousePos);
3127 }
3128 
IsMouseHoveringWindow()3129 bool ImGui::IsMouseHoveringWindow()
3130 {
3131     ImGuiContext& g = *GImGui;
3132     return g.HoveredWindow == g.CurrentWindow;
3133 }
3134 
IsMouseHoveringAnyWindow()3135 bool ImGui::IsMouseHoveringAnyWindow()
3136 {
3137     ImGuiContext& g = *GImGui;
3138     return g.HoveredWindow != NULL;
3139 }
3140 
IsPosHoveringAnyWindow(const ImVec2 & pos)3141 bool ImGui::IsPosHoveringAnyWindow(const ImVec2& pos)
3142 {
3143     return FindHoveredWindow(pos, false) != NULL;
3144 }
3145 
IsKeyPressedMap(ImGuiKey key,bool repeat)3146 static bool IsKeyPressedMap(ImGuiKey key, bool repeat)
3147 {
3148     const int key_index = GImGui->IO.KeyMap[key];
3149     return ImGui::IsKeyPressed(key_index, repeat);
3150 }
3151 
GetKeyIndex(ImGuiKey imgui_key)3152 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
3153 {
3154     IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
3155     return GImGui->IO.KeyMap[imgui_key];
3156 }
3157 
3158 // Note that imgui doesn't know the semantic of each entry of io.KeyDown[]. Use your own indices/enums according to how your backend/engine stored them into KeyDown[]!
IsKeyDown(int user_key_index)3159 bool ImGui::IsKeyDown(int user_key_index)
3160 {
3161     if (user_key_index < 0) return false;
3162     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown));
3163     return GImGui->IO.KeysDown[user_key_index];
3164 }
3165 
IsKeyPressed(int user_key_index,bool repeat)3166 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
3167 {
3168     ImGuiContext& g = *GImGui;
3169     if (user_key_index < 0) return false;
3170     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3171     const float t = g.IO.KeysDownDuration[user_key_index];
3172     if (t == 0.0f)
3173         return true;
3174 
3175     if (repeat && t > g.IO.KeyRepeatDelay)
3176     {
3177         float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
3178         if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
3179             return true;
3180     }
3181     return false;
3182 }
3183 
IsKeyReleased(int user_key_index)3184 bool ImGui::IsKeyReleased(int user_key_index)
3185 {
3186     ImGuiContext& g = *GImGui;
3187     if (user_key_index < 0) return false;
3188     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3189     if (g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index])
3190         return true;
3191     return false;
3192 }
3193 
IsMouseDown(int button)3194 bool ImGui::IsMouseDown(int button)
3195 {
3196     ImGuiContext& g = *GImGui;
3197     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3198     return g.IO.MouseDown[button];
3199 }
3200 
IsMouseClicked(int button,bool repeat)3201 bool ImGui::IsMouseClicked(int button, bool repeat)
3202 {
3203     ImGuiContext& g = *GImGui;
3204     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3205     const float t = g.IO.MouseDownDuration[button];
3206     if (t == 0.0f)
3207         return true;
3208 
3209     if (repeat && t > g.IO.KeyRepeatDelay)
3210     {
3211         float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
3212         if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
3213             return true;
3214     }
3215 
3216     return false;
3217 }
3218 
IsMouseReleased(int button)3219 bool ImGui::IsMouseReleased(int button)
3220 {
3221     ImGuiContext& g = *GImGui;
3222     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3223     return g.IO.MouseReleased[button];
3224 }
3225 
IsMouseDoubleClicked(int button)3226 bool ImGui::IsMouseDoubleClicked(int button)
3227 {
3228     ImGuiContext& g = *GImGui;
3229     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3230     return g.IO.MouseDoubleClicked[button];
3231 }
3232 
IsMouseDragging(int button,float lock_threshold)3233 bool ImGui::IsMouseDragging(int button, float lock_threshold)
3234 {
3235     ImGuiContext& g = *GImGui;
3236     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3237     if (!g.IO.MouseDown[button])
3238         return false;
3239     if (lock_threshold < 0.0f)
3240         lock_threshold = g.IO.MouseDragThreshold;
3241     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
3242 }
3243 
GetMousePos()3244 ImVec2 ImGui::GetMousePos()
3245 {
3246     return GImGui->IO.MousePos;
3247 }
3248 
3249 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()3250 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
3251 {
3252     ImGuiContext& g = *GImGui;
3253     if (g.CurrentPopupStack.Size > 0)
3254         return g.OpenPopupStack[g.CurrentPopupStack.Size-1].MousePosOnOpen;
3255     return g.IO.MousePos;
3256 }
3257 
GetMouseDragDelta(int button,float lock_threshold)3258 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
3259 {
3260     ImGuiContext& g = *GImGui;
3261     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3262     if (lock_threshold < 0.0f)
3263         lock_threshold = g.IO.MouseDragThreshold;
3264     if (g.IO.MouseDown[button])
3265         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
3266             return g.IO.MousePos - g.IO.MouseClickedPos[button];     // Assume we can only get active with left-mouse button (at the moment).
3267     return ImVec2(0.0f, 0.0f);
3268 }
3269 
ResetMouseDragDelta(int button)3270 void ImGui::ResetMouseDragDelta(int button)
3271 {
3272     ImGuiContext& g = *GImGui;
3273     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3274     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
3275     g.IO.MouseClickedPos[button] = g.IO.MousePos;
3276 }
3277 
GetMouseCursor()3278 ImGuiMouseCursor ImGui::GetMouseCursor()
3279 {
3280     return GImGui->MouseCursor;
3281 }
3282 
SetMouseCursor(ImGuiMouseCursor cursor_type)3283 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
3284 {
3285     GImGui->MouseCursor = cursor_type;
3286 }
3287 
CaptureKeyboardFromApp(bool capture)3288 void ImGui::CaptureKeyboardFromApp(bool capture)
3289 {
3290     GImGui->CaptureKeyboardNextFrame = capture ? 1 : 0;
3291 }
3292 
CaptureMouseFromApp(bool capture)3293 void ImGui::CaptureMouseFromApp(bool capture)
3294 {
3295     GImGui->CaptureMouseNextFrame = capture ? 1 : 0;
3296 }
3297 
IsItemHovered()3298 bool ImGui::IsItemHovered()
3299 {
3300     ImGuiWindow* window = GetCurrentWindowRead();
3301     return window->DC.LastItemHoveredAndUsable;
3302 }
3303 
IsItemHoveredRect()3304 bool ImGui::IsItemHoveredRect()
3305 {
3306     ImGuiWindow* window = GetCurrentWindowRead();
3307     return window->DC.LastItemHoveredRect;
3308 }
3309 
IsItemActive()3310 bool ImGui::IsItemActive()
3311 {
3312     ImGuiContext& g = *GImGui;
3313     if (g.ActiveId)
3314     {
3315         ImGuiWindow* window = GetCurrentWindowRead();
3316         return g.ActiveId == window->DC.LastItemId;
3317     }
3318     return false;
3319 }
3320 
IsItemClicked(int mouse_button)3321 bool ImGui::IsItemClicked(int mouse_button)
3322 {
3323     return IsMouseClicked(mouse_button) && IsItemHovered();
3324 }
3325 
IsAnyItemHovered()3326 bool ImGui::IsAnyItemHovered()
3327 {
3328     return GImGui->HoveredId != 0 || GImGui->HoveredIdPreviousFrame != 0;
3329 }
3330 
IsAnyItemActive()3331 bool ImGui::IsAnyItemActive()
3332 {
3333     return GImGui->ActiveId != 0;
3334 }
3335 
IsItemVisible()3336 bool ImGui::IsItemVisible()
3337 {
3338     ImGuiWindow* window = GetCurrentWindowRead();
3339     ImRect r(window->ClipRect);
3340     return r.Overlaps(window->DC.LastItemRect);
3341 }
3342 
3343 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
SetItemAllowOverlap()3344 void ImGui::SetItemAllowOverlap()
3345 {
3346     ImGuiContext& g = *GImGui;
3347     if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
3348         g.HoveredIdAllowOverlap = true;
3349     if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
3350         g.ActiveIdAllowOverlap = true;
3351 }
3352 
GetItemRectMin()3353 ImVec2 ImGui::GetItemRectMin()
3354 {
3355     ImGuiWindow* window = GetCurrentWindowRead();
3356     return window->DC.LastItemRect.Min;
3357 }
3358 
GetItemRectMax()3359 ImVec2 ImGui::GetItemRectMax()
3360 {
3361     ImGuiWindow* window = GetCurrentWindowRead();
3362     return window->DC.LastItemRect.Max;
3363 }
3364 
GetItemRectSize()3365 ImVec2 ImGui::GetItemRectSize()
3366 {
3367     ImGuiWindow* window = GetCurrentWindowRead();
3368     return window->DC.LastItemRect.GetSize();
3369 }
3370 
CalcItemRectClosestPoint(const ImVec2 & pos,bool on_edge,float outward)3371 ImVec2 ImGui::CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge, float outward)
3372 {
3373     ImGuiWindow* window = GetCurrentWindowRead();
3374     ImRect rect = window->DC.LastItemRect;
3375     rect.Expand(outward);
3376     return rect.GetClosestPoint(pos, on_edge);
3377 }
3378 
3379 // Tooltip is stored and turned into a BeginTooltip()/EndTooltip() sequence at the end of the frame. Each call override previous value.
SetTooltipV(const char * fmt,va_list args)3380 void ImGui::SetTooltipV(const char* fmt, va_list args)
3381 {
3382     ImGuiContext& g = *GImGui;
3383     ImFormatStringV(g.Tooltip, IM_ARRAYSIZE(g.Tooltip), fmt, args);
3384 }
3385 
SetTooltip(const char * fmt,...)3386 void ImGui::SetTooltip(const char* fmt, ...)
3387 {
3388     va_list args;
3389     va_start(args, fmt);
3390     SetTooltipV(fmt, args);
3391     va_end(args);
3392 }
3393 
GetVisibleRect()3394 static ImRect GetVisibleRect()
3395 {
3396     ImGuiContext& g = *GImGui;
3397     if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y)
3398         return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax);
3399     return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3400 }
3401 
BeginTooltip()3402 void ImGui::BeginTooltip()
3403 {
3404     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
3405     ImGui::Begin("##Tooltip", NULL, flags);
3406 }
3407 
EndTooltip()3408 void ImGui::EndTooltip()
3409 {
3410     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
3411     ImGui::End();
3412 }
3413 
IsPopupOpen(ImGuiID id)3414 static bool IsPopupOpen(ImGuiID id)
3415 {
3416     ImGuiContext& g = *GImGui;
3417     return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id;
3418 }
3419 
3420 // Mark popup as open (toggle toward open state).
3421 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
3422 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
3423 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(const char * str_id,bool reopen_existing)3424 void ImGui::OpenPopupEx(const char* str_id, bool reopen_existing)
3425 {
3426     ImGuiContext& g = *GImGui;
3427     ImGuiWindow* window = g.CurrentWindow;
3428     ImGuiID id = window->GetID(str_id);
3429     int current_stack_size = g.CurrentPopupStack.Size;
3430     ImGuiPopupRef popup_ref = ImGuiPopupRef(id, window, window->GetID("##menus"), g.IO.MousePos); // Tagged as new ref because constructor sets Window to NULL (we are passing the ParentWindow info here)
3431     if (g.OpenPopupStack.Size < current_stack_size + 1)
3432         g.OpenPopupStack.push_back(popup_ref);
3433     else if (reopen_existing || g.OpenPopupStack[current_stack_size].PopupId != id)
3434     {
3435         g.OpenPopupStack.resize(current_stack_size+1);
3436         g.OpenPopupStack[current_stack_size] = popup_ref;
3437     }
3438 }
3439 
OpenPopup(const char * str_id)3440 void ImGui::OpenPopup(const char* str_id)
3441 {
3442     ImGui::OpenPopupEx(str_id, false);
3443 }
3444 
CloseInactivePopups()3445 static void CloseInactivePopups()
3446 {
3447     ImGuiContext& g = *GImGui;
3448     if (g.OpenPopupStack.empty())
3449         return;
3450 
3451     // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
3452     // Don't close our own child popup windows
3453     int n = 0;
3454     if (g.FocusedWindow)
3455     {
3456         for (n = 0; n < g.OpenPopupStack.Size; n++)
3457         {
3458             ImGuiPopupRef& popup = g.OpenPopupStack[n];
3459             if (!popup.Window)
3460                 continue;
3461             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
3462             if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
3463                 continue;
3464 
3465             bool has_focus = false;
3466             for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++)
3467                 has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == g.FocusedWindow->RootWindow);
3468             if (!has_focus)
3469                 break;
3470         }
3471     }
3472     if (n < g.OpenPopupStack.Size)   // This test is not required but it allows to set a useful breakpoint on the line below
3473         g.OpenPopupStack.resize(n);
3474 }
3475 
GetFrontMostModalRootWindow()3476 static ImGuiWindow* GetFrontMostModalRootWindow()
3477 {
3478     ImGuiContext& g = *GImGui;
3479     for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
3480         if (ImGuiWindow* front_most_popup = g.OpenPopupStack.Data[n].Window)
3481             if (front_most_popup->Flags & ImGuiWindowFlags_Modal)
3482                 return front_most_popup;
3483     return NULL;
3484 }
3485 
ClosePopupToLevel(int remaining)3486 static void ClosePopupToLevel(int remaining)
3487 {
3488     ImGuiContext& g = *GImGui;
3489     if (remaining > 0)
3490         ImGui::FocusWindow(g.OpenPopupStack[remaining-1].Window);
3491     else
3492         ImGui::FocusWindow(g.OpenPopupStack[0].ParentWindow);
3493     g.OpenPopupStack.resize(remaining);
3494 }
3495 
ClosePopup(ImGuiID id)3496 static void ClosePopup(ImGuiID id)
3497 {
3498     if (!IsPopupOpen(id))
3499         return;
3500     ImGuiContext& g = *GImGui;
3501     ClosePopupToLevel(g.OpenPopupStack.Size - 1);
3502 }
3503 
3504 // Close the popup we have begin-ed into.
CloseCurrentPopup()3505 void ImGui::CloseCurrentPopup()
3506 {
3507     ImGuiContext& g = *GImGui;
3508     int popup_idx = g.CurrentPopupStack.Size - 1;
3509     if (popup_idx < 0 || popup_idx > g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
3510         return;
3511     while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu))
3512         popup_idx--;
3513     ClosePopupToLevel(popup_idx);
3514 }
3515 
ClearSetNextWindowData()3516 static inline void ClearSetNextWindowData()
3517 {
3518     ImGuiContext& g = *GImGui;
3519     g.SetNextWindowPosCond = g.SetNextWindowSizeCond = g.SetNextWindowContentSizeCond = g.SetNextWindowCollapsedCond = 0;
3520     g.SetNextWindowSizeConstraint = g.SetNextWindowFocus = false;
3521 }
3522 
BeginPopupEx(const char * str_id,ImGuiWindowFlags extra_flags)3523 static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags)
3524 {
3525     ImGuiContext& g = *GImGui;
3526     ImGuiWindow* window = g.CurrentWindow;
3527     const ImGuiID id = window->GetID(str_id);
3528     if (!IsPopupOpen(id))
3529     {
3530         ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3531         return false;
3532     }
3533 
3534     ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
3535     ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
3536 
3537     char name[20];
3538     if (flags & ImGuiWindowFlags_ChildMenu)
3539         ImFormatString(name, IM_ARRAYSIZE(name), "##menu_%d", g.CurrentPopupStack.Size);    // Recycle windows based on depth
3540     else
3541         ImFormatString(name, IM_ARRAYSIZE(name), "##popup_%08x", id); // Not recycling, so we can close/open during the same frame
3542 
3543     bool is_open = ImGui::Begin(name, NULL, flags);
3544     if (!(window->Flags & ImGuiWindowFlags_ShowBorders))
3545         g.CurrentWindow->Flags &= ~ImGuiWindowFlags_ShowBorders;
3546     if (!is_open) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
3547         ImGui::EndPopup();
3548 
3549     return is_open;
3550 }
3551 
BeginPopup(const char * str_id)3552 bool ImGui::BeginPopup(const char* str_id)
3553 {
3554     if (GImGui->OpenPopupStack.Size <= GImGui->CurrentPopupStack.Size)	// Early out for performance
3555     {
3556         ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3557         return false;
3558     }
3559     return BeginPopupEx(str_id, ImGuiWindowFlags_ShowBorders);
3560 }
3561 
BeginPopupModal(const char * name,bool * p_open,ImGuiWindowFlags extra_flags)3562 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags extra_flags)
3563 {
3564     ImGuiContext& g = *GImGui;
3565     ImGuiWindow* window = g.CurrentWindow;
3566     const ImGuiID id = window->GetID(name);
3567     if (!IsPopupOpen(id))
3568     {
3569         ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3570         return false;
3571     }
3572 
3573     ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoSavedSettings;
3574     bool is_open = ImGui::Begin(name, p_open, flags);
3575     if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
3576     {
3577         ImGui::EndPopup();
3578         if (is_open)
3579             ClosePopup(id);
3580         return false;
3581     }
3582 
3583     return is_open;
3584 }
3585 
EndPopup()3586 void ImGui::EndPopup()
3587 {
3588     ImGuiWindow* window = GetCurrentWindow();
3589     IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
3590     IM_ASSERT(GImGui->CurrentPopupStack.Size > 0);
3591     ImGui::End();
3592     if (!(window->Flags & ImGuiWindowFlags_Modal))
3593         ImGui::PopStyleVar();
3594 }
3595 
3596 // This is a helper to handle the most simple case of associating one named popup to one given widget.
3597 // 1. If you have many possible popups (for different "instances" of a same widget, or for wholly different widgets), you may be better off handling
3598 //    this yourself so you can store data relative to the widget that opened the popup instead of choosing different popup identifiers.
3599 // 2. If you want right-clicking on the same item to reopen the popup at new location, use the same code replacing IsItemHovered() with IsItemHoveredRect()
3600 //    and passing true to the OpenPopupEx().
3601 //    Because: hovering an item in a window below the popup won't normally trigger is hovering behavior/coloring. The pattern of ignoring the fact that
3602 //    the item isn't interactable (because it is blocked by the active popup) may useful in some situation when e.g. large canvas as one item, content of menu
3603 //    driven by click position.
BeginPopupContextItem(const char * str_id,int mouse_button)3604 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
3605 {
3606     if (IsItemHovered() && IsMouseClicked(mouse_button))
3607         OpenPopupEx(str_id, false);
3608     return BeginPopup(str_id);
3609 }
3610 
BeginPopupContextWindow(bool also_over_items,const char * str_id,int mouse_button)3611 bool ImGui::BeginPopupContextWindow(bool also_over_items, const char* str_id, int mouse_button)
3612 {
3613     if (!str_id) str_id = "window_context_menu";
3614     if (IsMouseHoveringWindow() && IsMouseClicked(mouse_button))
3615         if (also_over_items || !IsAnyItemHovered())
3616             OpenPopupEx(str_id, true);
3617     return BeginPopup(str_id);
3618 }
3619 
BeginPopupContextVoid(const char * str_id,int mouse_button)3620 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
3621 {
3622     if (!str_id) str_id = "void_context_menu";
3623     if (!IsMouseHoveringAnyWindow() && IsMouseClicked(mouse_button))
3624         OpenPopupEx(str_id, true);
3625     return BeginPopup(str_id);
3626 }
3627 
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)3628 static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
3629 {
3630     ImGuiWindow* window = ImGui::GetCurrentWindow();
3631     ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
3632 
3633     const ImVec2 content_avail = ImGui::GetContentRegionAvail();
3634     ImVec2 size = ImFloor(size_arg);
3635     if (size.x <= 0.0f)
3636     {
3637         if (size.x == 0.0f)
3638             flags |= ImGuiWindowFlags_ChildWindowAutoFitX;
3639         size.x = ImMax(content_avail.x, 4.0f) - fabsf(size.x); // Arbitrary minimum zero-ish child size of 4.0f (0.0f causing too much issues)
3640     }
3641     if (size.y <= 0.0f)
3642     {
3643         if (size.y == 0.0f)
3644             flags |= ImGuiWindowFlags_ChildWindowAutoFitY;
3645         size.y = ImMax(content_avail.y, 4.0f) - fabsf(size.y);
3646     }
3647     if (border)
3648         flags |= ImGuiWindowFlags_ShowBorders;
3649     flags |= extra_flags;
3650 
3651     char title[256];
3652     if (name)
3653         ImFormatString(title, IM_ARRAYSIZE(title), "%s.%s.%08X", window->Name, name, id);
3654     else
3655         ImFormatString(title, IM_ARRAYSIZE(title), "%s.%08X", window->Name, id);
3656 
3657     bool ret = ImGui::Begin(title, NULL, size, -1.0f, flags);
3658 
3659     if (!(window->Flags & ImGuiWindowFlags_ShowBorders))
3660         ImGui::GetCurrentWindow()->Flags &= ~ImGuiWindowFlags_ShowBorders;
3661 
3662     return ret;
3663 }
3664 
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)3665 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
3666 {
3667     ImGuiWindow* window = GetCurrentWindow();
3668     return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
3669 }
3670 
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)3671 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
3672 {
3673     return BeginChildEx(NULL, id, size_arg, border, extra_flags);
3674 }
3675 
EndChild()3676 void ImGui::EndChild()
3677 {
3678     ImGuiWindow* window = GetCurrentWindow();
3679 
3680     IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() callss
3681     if ((window->Flags & ImGuiWindowFlags_ComboBox) || window->BeginCount > 1)
3682     {
3683         ImGui::End();
3684     }
3685     else
3686     {
3687         // When using auto-filling child window, we don't provide full width/height to ItemSize so that it doesn't feed back into automatic size-fitting.
3688         ImVec2 sz = GetWindowSize();
3689         if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitX) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
3690             sz.x = ImMax(4.0f, sz.x);
3691         if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitY)
3692             sz.y = ImMax(4.0f, sz.y);
3693 
3694         ImGui::End();
3695 
3696         window = GetCurrentWindow();
3697         ImRect bb(window->DC.CursorPos, window->DC.CursorPos + sz);
3698         ItemSize(sz);
3699         ItemAdd(bb, NULL);
3700     }
3701 }
3702 
3703 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)3704 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
3705 {
3706     ImGuiContext& g = *GImGui;
3707     const ImGuiStyle& style = g.Style;
3708     ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, style.Colors[ImGuiCol_FrameBg]);
3709     ImGui::PushStyleVar(ImGuiStyleVar_ChildWindowRounding, style.FrameRounding);
3710     ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
3711     return ImGui::BeginChild(id, size, (g.CurrentWindow->Flags & ImGuiWindowFlags_ShowBorders) ? true : false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
3712 }
3713 
EndChildFrame()3714 void ImGui::EndChildFrame()
3715 {
3716     ImGui::EndChild();
3717     ImGui::PopStyleVar(2);
3718     ImGui::PopStyleColor();
3719 }
3720 
3721 // Save and compare stack sizes on Begin()/End() to detect usage errors
CheckStacksSize(ImGuiWindow * window,bool write)3722 static void CheckStacksSize(ImGuiWindow* window, bool write)
3723 {
3724     // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
3725     ImGuiContext& g = *GImGui;
3726     int* p_backup = &window->DC.StackSizesBackup[0];
3727     { int current = window->IDStack.Size;       if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!");   p_backup++; }    // Too few or too many PopID()/TreePop()
3728     { int current = window->DC.GroupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!");                p_backup++; }    // Too few or too many EndGroup()
3729     { int current = g.CurrentPopupStack.Size;   if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup()
3730     { int current = g.ColorModifiers.Size;      if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleColor/PopStyleColor Mismatch!");       p_backup++; }    // Too few or too many PopStyleColor()
3731     { int current = g.StyleModifiers.Size;      if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleVar/PopStyleVar Mismatch!");           p_backup++; }    // Too few or too many PopStyleVar()
3732     { int current = g.FontStack.Size;           if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushFont/PopFont Mismatch!");                   p_backup++; }    // Too few or too many PopFont()
3733     IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
3734 }
3735 
FindBestPopupWindowPos(const ImVec2 & base_pos,const ImVec2 & size,int * last_dir,const ImRect & r_inner)3736 static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& r_inner)
3737 {
3738     const ImGuiStyle& style = GImGui->Style;
3739 
3740     // Clamp into visible area while not overlapping the cursor. Safety padding is optional if our popup size won't fit without it.
3741     ImVec2 safe_padding = style.DisplaySafeAreaPadding;
3742     ImRect r_outer(GetVisibleRect());
3743     r_outer.Reduce(ImVec2((size.x - r_outer.GetWidth() > safe_padding.x*2) ? safe_padding.x : 0.0f, (size.y - r_outer.GetHeight() > safe_padding.y*2) ? safe_padding.y : 0.0f));
3744     ImVec2 base_pos_clamped = ImClamp(base_pos, r_outer.Min, r_outer.Max - size);
3745 
3746     for (int n = (*last_dir != -1) ? -1 : 0; n < 4; n++)   // Last, Right, down, up, left. (Favor last used direction).
3747     {
3748         const int dir = (n == -1) ? *last_dir : n;
3749         ImRect rect(dir == 0 ? r_inner.Max.x : r_outer.Min.x, dir == 1 ? r_inner.Max.y : r_outer.Min.y, dir == 3 ? r_inner.Min.x : r_outer.Max.x, dir == 2 ? r_inner.Min.y : r_outer.Max.y);
3750         if (rect.GetWidth() < size.x || rect.GetHeight() < size.y)
3751             continue;
3752         *last_dir = dir;
3753         return ImVec2(dir == 0 ? r_inner.Max.x : dir == 3 ? r_inner.Min.x - size.x : base_pos_clamped.x, dir == 1 ? r_inner.Max.y : dir == 2 ? r_inner.Min.y - size.y : base_pos_clamped.y);
3754     }
3755 
3756     // Fallback, try to keep within display
3757     *last_dir = -1;
3758     ImVec2 pos = base_pos;
3759     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
3760     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
3761     return pos;
3762 }
3763 
FindWindowByName(const char * name)3764 ImGuiWindow* ImGui::FindWindowByName(const char* name)
3765 {
3766     // FIXME-OPT: Store sorted hashes -> pointers so we can do a bissection in a contiguous block
3767     ImGuiContext& g = *GImGui;
3768     ImGuiID id = ImHash(name, 0);
3769     for (int i = 0; i < g.Windows.Size; i++)
3770         if (g.Windows[i]->ID == id)
3771             return g.Windows[i];
3772     return NULL;
3773 }
3774 
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)3775 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
3776 {
3777     ImGuiContext& g = *GImGui;
3778 
3779     // Create window the first time
3780     ImGuiWindow* window = (ImGuiWindow*)ImGui::MemAlloc(sizeof(ImGuiWindow));
3781     IM_PLACEMENT_NEW(window) ImGuiWindow(name);
3782     window->Flags = flags;
3783 
3784     if (flags & ImGuiWindowFlags_NoSavedSettings)
3785     {
3786         // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
3787         window->Size = window->SizeFull = size;
3788     }
3789     else
3790     {
3791         // Retrieve settings from .ini file
3792         // Use SetWindowPos() or SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
3793         window->PosFloat = ImVec2(60, 60);
3794         window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
3795 
3796         ImGuiIniData* settings = FindWindowSettings(name);
3797         if (!settings)
3798         {
3799             settings = AddWindowSettings(name);
3800         }
3801         else
3802         {
3803             window->SetWindowPosAllowFlags &= ~ImGuiSetCond_FirstUseEver;
3804             window->SetWindowSizeAllowFlags &= ~ImGuiSetCond_FirstUseEver;
3805             window->SetWindowCollapsedAllowFlags &= ~ImGuiSetCond_FirstUseEver;
3806         }
3807 
3808         if (settings->Pos.x != FLT_MAX)
3809         {
3810             window->PosFloat = settings->Pos;
3811             window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
3812             window->Collapsed = settings->Collapsed;
3813         }
3814 
3815         if (ImLengthSqr(settings->Size) > 0.00001f && !(flags & ImGuiWindowFlags_NoResize))
3816             size = settings->Size;
3817         window->Size = window->SizeFull = size;
3818     }
3819 
3820     if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
3821     {
3822         window->AutoFitFramesX = window->AutoFitFramesY = 2;
3823         window->AutoFitOnlyGrows = false;
3824     }
3825     else
3826     {
3827         if (window->Size.x <= 0.0f)
3828             window->AutoFitFramesX = 2;
3829         if (window->Size.y <= 0.0f)
3830             window->AutoFitFramesY = 2;
3831         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
3832     }
3833 
3834     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
3835         g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once
3836     else
3837         g.Windows.push_back(window);
3838     return window;
3839 }
3840 
ApplySizeFullWithConstraint(ImGuiWindow * window,ImVec2 new_size)3841 static void ApplySizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size)
3842 {
3843     ImGuiContext& g = *GImGui;
3844     if (g.SetNextWindowSizeConstraint)
3845     {
3846         // Using -1,-1 on either X/Y axis to preserve the current size.
3847         ImRect cr = g.SetNextWindowSizeConstraintRect;
3848         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
3849         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
3850         if (g.SetNextWindowSizeConstraintCallback)
3851         {
3852             ImGuiSizeConstraintCallbackData data;
3853             data.UserData = g.SetNextWindowSizeConstraintCallbackUserData;
3854             data.Pos = window->Pos;
3855             data.CurrentSize = window->SizeFull;
3856             data.DesiredSize = new_size;
3857             g.SetNextWindowSizeConstraintCallback(&data);
3858             new_size = data.DesiredSize;
3859         }
3860     }
3861     if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
3862         new_size = ImMax(new_size, g.Style.WindowMinSize);
3863     window->SizeFull = new_size;
3864 }
3865 
3866 // Push a new ImGui window to add widgets to.
3867 // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair.
3868 // - Begin/End can be called multiple times during the frame with the same window name to append content.
3869 // - 'size_on_first_use' for a regular window denote the initial size for first-time creation (no saved data) and isn't that useful. Use SetNextWindowSize() prior to calling Begin() for more flexible window manipulation.
3870 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
3871 //   You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file.
3872 // - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned.
3873 // - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed.
3874 // - Passing non-zero 'size' is roughly equivalent to calling SetNextWindowSize(size, ImGuiSetCond_FirstUseEver) prior to calling Begin().
Begin(const char * name,bool * p_open,ImGuiWindowFlags flags)3875 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
3876 {
3877     return ImGui::Begin(name, p_open, ImVec2(0.f, 0.f), -1.0f, flags);
3878 }
3879 
Begin(const char * name,bool * p_open,const ImVec2 & size_on_first_use,float bg_alpha,ImGuiWindowFlags flags)3880 bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha, ImGuiWindowFlags flags)
3881 {
3882     ImGuiContext& g = *GImGui;
3883     const ImGuiStyle& style = g.Style;
3884     IM_ASSERT(name != NULL);                        // Window name required
3885     IM_ASSERT(g.Initialized);                       // Forgot to call ImGui::NewFrame()
3886     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
3887 
3888     if (flags & ImGuiWindowFlags_NoInputs)
3889         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
3890 
3891     // Find or create
3892     bool window_is_new = false;
3893     ImGuiWindow* window = FindWindowByName(name);
3894     if (!window)
3895     {
3896         window = CreateNewWindow(name, size_on_first_use, flags);
3897         window_is_new = true;
3898     }
3899 
3900     const int current_frame = ImGui::GetFrameCount();
3901     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
3902     if (first_begin_of_the_frame)
3903         window->Flags = (ImGuiWindowFlags)flags;
3904     else
3905         flags = window->Flags;
3906 
3907     // Add to stack
3908     ImGuiWindow* parent_window = !g.CurrentWindowStack.empty() ? g.CurrentWindowStack.back() : NULL;
3909     g.CurrentWindowStack.push_back(window);
3910     SetCurrentWindow(window);
3911     CheckStacksSize(window, true);
3912     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
3913 
3914     bool window_was_active = (window->LastFrameActive == current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
3915     if (flags & ImGuiWindowFlags_Popup)
3916     {
3917         ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
3918         window_was_active &= (window->PopupId == popup_ref.PopupId);
3919         window_was_active &= (window == popup_ref.Window);
3920         popup_ref.Window = window;
3921         g.CurrentPopupStack.push_back(popup_ref);
3922         window->PopupId = popup_ref.PopupId;
3923     }
3924 
3925     const bool window_appearing_after_being_hidden = (window->HiddenFrames == 1);
3926 
3927     // Process SetNextWindow***() calls
3928     bool window_pos_set_by_api = false, window_size_set_by_api = false;
3929     if (g.SetNextWindowPosCond)
3930     {
3931         const ImVec2 backup_cursor_pos = window->DC.CursorPos;                  // FIXME: not sure of the exact reason of this saving/restore anymore :( need to look into that.
3932         if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowPosAllowFlags |= ImGuiSetCond_Appearing;
3933         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.SetNextWindowPosCond) != 0;
3934         if (window_pos_set_by_api && ImLengthSqr(g.SetNextWindowPosVal - ImVec2(-FLT_MAX,-FLT_MAX)) < 0.001f)
3935         {
3936             window->SetWindowPosCenterWanted = true;                            // May be processed on the next frame if this is our first frame and we are measuring size
3937             window->SetWindowPosAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
3938         }
3939         else
3940         {
3941             SetWindowPos(window, g.SetNextWindowPosVal, g.SetNextWindowPosCond);
3942         }
3943         window->DC.CursorPos = backup_cursor_pos;
3944         g.SetNextWindowPosCond = 0;
3945     }
3946     if (g.SetNextWindowSizeCond)
3947     {
3948         if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowSizeAllowFlags |= ImGuiSetCond_Appearing;
3949         window_size_set_by_api = (window->SetWindowSizeAllowFlags & g.SetNextWindowSizeCond) != 0;
3950         SetWindowSize(window, g.SetNextWindowSizeVal, g.SetNextWindowSizeCond);
3951         g.SetNextWindowSizeCond = 0;
3952     }
3953     if (g.SetNextWindowContentSizeCond)
3954     {
3955         window->SizeContentsExplicit = g.SetNextWindowContentSizeVal;
3956         g.SetNextWindowContentSizeCond = 0;
3957     }
3958     else if (first_begin_of_the_frame)
3959     {
3960         window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
3961     }
3962     if (g.SetNextWindowCollapsedCond)
3963     {
3964         if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowCollapsedAllowFlags |= ImGuiSetCond_Appearing;
3965         SetWindowCollapsed(window, g.SetNextWindowCollapsedVal, g.SetNextWindowCollapsedCond);
3966         g.SetNextWindowCollapsedCond = 0;
3967     }
3968     if (g.SetNextWindowFocus)
3969     {
3970         ImGui::SetWindowFocus();
3971         g.SetNextWindowFocus = false;
3972     }
3973 
3974     // Update known root window (if we are a child window, otherwise window == window->RootWindow)
3975     int root_idx, root_non_popup_idx;
3976     for (root_idx = g.CurrentWindowStack.Size - 1; root_idx > 0; root_idx--)
3977         if (!(g.CurrentWindowStack[root_idx]->Flags & ImGuiWindowFlags_ChildWindow))
3978             break;
3979     for (root_non_popup_idx = root_idx; root_non_popup_idx > 0; root_non_popup_idx--)
3980         if (!(g.CurrentWindowStack[root_non_popup_idx]->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
3981             break;
3982     window->ParentWindow = parent_window;
3983     window->RootWindow = g.CurrentWindowStack[root_idx];
3984     window->RootNonPopupWindow = g.CurrentWindowStack[root_non_popup_idx];      // This is merely for displaying the TitleBgActive color.
3985 
3986     // When reusing window again multiple times a frame, just append content (don't need to setup again)
3987     if (first_begin_of_the_frame)
3988     {
3989         window->Active = true;
3990         window->IndexWithinParent = 0;
3991         window->BeginCount = 0;
3992         window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
3993         window->LastFrameActive = current_frame;
3994         window->IDStack.resize(1);
3995 
3996         // Clear draw list, setup texture, outer clipping rectangle
3997         window->DrawList->Clear();
3998         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
3999         ImRect fullscreen_rect(GetVisibleRect());
4000         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_ComboBox|ImGuiWindowFlags_Popup)))
4001             PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
4002         else
4003             PushClipRect(fullscreen_rect.Min, fullscreen_rect.Max, true);
4004 
4005         if (!window_was_active)
4006         {
4007             // Popup first latch mouse position, will position itself when it appears next frame
4008             window->AutoPosLastDirection = -1;
4009             if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
4010                 window->PosFloat = g.IO.MousePos;
4011         }
4012 
4013         // Collapse window by double-clicking on title bar
4014         // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing
4015         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
4016         {
4017             ImRect title_bar_rect = window->TitleBarRect();
4018             if (g.HoveredWindow == window && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
4019             {
4020                 window->Collapsed = !window->Collapsed;
4021                 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4022                     MarkIniSettingsDirty();
4023                 FocusWindow(window);
4024             }
4025         }
4026         else
4027         {
4028             window->Collapsed = false;
4029         }
4030 
4031         // SIZE
4032 
4033         // Save contents size from last frame for auto-fitting (unless explicitly specified)
4034         window->SizeContents.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.x - window->Pos.x) + window->Scroll.x));
4035         window->SizeContents.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.y - window->Pos.y) + window->Scroll.y));
4036 
4037         // Hide popup/tooltip window when first appearing while we measure size (because we recycle them)
4038         if (window->HiddenFrames > 0)
4039             window->HiddenFrames--;
4040         if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && !window_was_active)
4041         {
4042             window->HiddenFrames = 1;
4043             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
4044             {
4045                 if (!window_size_set_by_api)
4046                     window->Size = window->SizeFull = ImVec2(0.f, 0.f);
4047                 window->SizeContents = ImVec2(0.f, 0.f);
4048             }
4049         }
4050 
4051         // Lock window padding so that altering the ShowBorders flag for children doesn't have side-effects.
4052         window->WindowPadding = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_ComboBox | ImGuiWindowFlags_Popup))) ? ImVec2(0,0) : style.WindowPadding;
4053 
4054         // Calculate auto-fit size
4055         ImVec2 size_auto_fit;
4056         if ((flags & ImGuiWindowFlags_Tooltip) != 0)
4057         {
4058             // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose.
4059             size_auto_fit = window->SizeContents + window->WindowPadding - ImVec2(0.0f, style.ItemSpacing.y);
4060         }
4061         else
4062         {
4063             size_auto_fit = ImClamp(window->SizeContents + window->WindowPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding));
4064 
4065             // Handling case of auto fit window not fitting in screen on one axis, we are growing auto fit size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
4066             if (size_auto_fit.x < window->SizeContents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar))
4067                 size_auto_fit.y += style.ScrollbarSize;
4068             if (size_auto_fit.y < window->SizeContents.y && !(flags & ImGuiWindowFlags_NoScrollbar))
4069                 size_auto_fit.x += style.ScrollbarSize;
4070             size_auto_fit.y = ImMax(size_auto_fit.y - style.ItemSpacing.y, 0.0f);
4071         }
4072 
4073         // Handle automatic resize
4074         if (window->Collapsed)
4075         {
4076             // We still process initial auto-fit on collapsed windows to get a window width,
4077             // But otherwise we don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
4078             if (window->AutoFitFramesX > 0)
4079                 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
4080             if (window->AutoFitFramesY > 0)
4081                 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
4082         }
4083         else
4084         {
4085             if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window_size_set_by_api)
4086             {
4087                 window->SizeFull = size_auto_fit;
4088             }
4089             else if ((window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) && !window_size_set_by_api)
4090             {
4091                 // Auto-fit only grows during the first few frames
4092                 if (window->AutoFitFramesX > 0)
4093                     window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
4094                 if (window->AutoFitFramesY > 0)
4095                     window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
4096                 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4097                     MarkIniSettingsDirty();
4098             }
4099         }
4100 
4101         // Apply minimum/maximum window size constraints and final size
4102         ApplySizeFullWithConstraint(window, window->SizeFull);
4103         window->Size = window->Collapsed ? window->TitleBarRect().GetSize() : window->SizeFull;
4104 
4105         // POSITION
4106 
4107         // Position child window
4108         if (flags & ImGuiWindowFlags_ChildWindow)
4109         {
4110             window->IndexWithinParent = parent_window->DC.ChildWindows.Size;
4111             parent_window->DC.ChildWindows.push_back(window);
4112         }
4113         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup))
4114         {
4115             window->Pos = window->PosFloat = parent_window->DC.CursorPos;
4116             window->Size = window->SizeFull = size_on_first_use; // NB: argument name 'size_on_first_use' misleading here, it's really just 'size' as provided by user passed via BeginChild()->Begin().
4117         }
4118 
4119         bool window_pos_center = false;
4120         window_pos_center |= (window->SetWindowPosCenterWanted && window->HiddenFrames == 0);
4121         window_pos_center |= ((flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api && window_appearing_after_being_hidden);
4122         if (window_pos_center)
4123         {
4124             // Center (any sort of window)
4125             SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, fullscreen_rect.GetCenter() - window->SizeFull * 0.5f), 0);
4126         }
4127         else if (flags & ImGuiWindowFlags_ChildMenu)
4128         {
4129             // Child menus typically request _any_ position within the parent menu item, and then our FindBestPopupWindowPos() function will move the new menu outside the parent bounds.
4130             // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
4131             IM_ASSERT(window_pos_set_by_api);
4132             float horizontal_overlap = style.ItemSpacing.x; // We want some overlap to convey the relative depth of each popup (currently the amount of overlap it is hard-coded to style.ItemSpacing.x, may need to introduce another style value).
4133             ImRect rect_to_avoid;
4134             if (parent_window->DC.MenuBarAppending)
4135                 rect_to_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
4136             else
4137                 rect_to_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX);
4138             window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
4139         }
4140         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_appearing_after_being_hidden)
4141         {
4142             ImRect rect_to_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1);
4143             window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
4144         }
4145 
4146         // Position tooltip (always follows mouse)
4147         if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api)
4148         {
4149             ImRect rect_to_avoid(g.IO.MousePos.x - 16, g.IO.MousePos.y - 8, g.IO.MousePos.x + 24, g.IO.MousePos.y + 24); // FIXME: Completely hard-coded. Perhaps center on cursor hit-point instead?
4150             window->PosFloat = FindBestPopupWindowPos(g.IO.MousePos, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
4151             if (window->AutoPosLastDirection == -1)
4152                 window->PosFloat = g.IO.MousePos + ImVec2(2,2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
4153         }
4154 
4155         // Clamp position so it stays visible
4156         if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
4157         {
4158             if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
4159             {
4160                 ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
4161                 window->PosFloat = ImMax(window->PosFloat + window->Size, padding) - window->Size;
4162                 window->PosFloat = ImMin(window->PosFloat, g.IO.DisplaySize - padding);
4163             }
4164         }
4165         window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
4166 
4167         // Default item width. Make it proportional to window size if window manually resizes
4168         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
4169             window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
4170         else
4171             window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
4172 
4173         // Prepare for focus requests
4174         window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1);
4175         window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1);
4176         window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1;
4177         window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX;
4178 
4179         // Apply scrolling
4180         if (window->ScrollTarget.x < FLT_MAX)
4181         {
4182             window->Scroll.x = window->ScrollTarget.x;
4183             window->ScrollTarget.x = FLT_MAX;
4184         }
4185         if (window->ScrollTarget.y < FLT_MAX)
4186         {
4187             float center_ratio = window->ScrollTargetCenterRatio.y;
4188             window->Scroll.y = window->ScrollTarget.y - ((1.0f - center_ratio) * (window->TitleBarHeight() + window->MenuBarHeight())) - (center_ratio * window->SizeFull.y);
4189             window->ScrollTarget.y = FLT_MAX;
4190         }
4191         window->Scroll = ImMax(window->Scroll, ImVec2(0.0f, 0.0f));
4192         if (!window->Collapsed && !window->SkipItems)
4193             window->Scroll = ImMin(window->Scroll, ImMax(ImVec2(0.0f, 0.0f), window->SizeContents - window->SizeFull + window->ScrollbarSizes));
4194 
4195         // Modal window darkens what is behind them
4196         if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow())
4197             window->DrawList->AddRectFilled(fullscreen_rect.Min, fullscreen_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio));
4198 
4199         // Draw window + handle manual resize
4200         ImRect title_bar_rect = window->TitleBarRect();
4201         const float window_rounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding;
4202         if (window->Collapsed)
4203         {
4204             // Draw title bar only
4205             RenderFrame(title_bar_rect.GetTL(), title_bar_rect.GetBR(),  GetColorU32(ImGuiCol_TitleBgCollapsed), true, window_rounding);
4206         }
4207         else
4208         {
4209             ImU32 resize_col = 0;
4210             const float resize_corner_size = ImMax(g.FontSize * 1.35f, window_rounding + 1.0f + g.FontSize * 0.2f);
4211             if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && !(flags & ImGuiWindowFlags_NoResize))
4212             {
4213                 // Manual resize
4214                 const ImVec2 br = window->Rect().GetBR();
4215                 const ImRect resize_rect(br - ImVec2(resize_corner_size * 0.75f, resize_corner_size * 0.75f), br);
4216                 const ImGuiID resize_id = window->GetID("#RESIZE");
4217                 bool hovered, held;
4218                 ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds);
4219                 resize_col = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
4220 
4221                 if (hovered || held)
4222                     g.MouseCursor = ImGuiMouseCursor_ResizeNWSE;
4223 
4224                 if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0])
4225                 {
4226                     // Manual auto-fit when double-clicking
4227                     ApplySizeFullWithConstraint(window, size_auto_fit);
4228                     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4229                         MarkIniSettingsDirty();
4230                     ClearActiveID();
4231                 }
4232                 else if (held)
4233                 {
4234                     // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
4235                     ApplySizeFullWithConstraint(window, (g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize()) - window->Pos);
4236                     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4237                         MarkIniSettingsDirty();
4238                 }
4239 
4240                 window->Size = window->SizeFull;
4241                 title_bar_rect = window->TitleBarRect();
4242             }
4243 
4244             // Scrollbars
4245             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > window->Size.y + style.ItemSpacing.y) && !(flags & ImGuiWindowFlags_NoScrollbar));
4246             window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > window->Size.x - (window->ScrollbarY ? style.ScrollbarSize : 0.0f) - window->WindowPadding.x) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
4247             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
4248             window->BorderSize = (flags & ImGuiWindowFlags_ShowBorders) ? 1.0f : 0.0f;
4249 
4250             // Window background, Default Alpha
4251             ImGuiCol bg_color_idx = ImGuiCol_WindowBg;
4252             if ((flags & ImGuiWindowFlags_ComboBox) != 0)
4253                 bg_color_idx = ImGuiCol_ComboBg;
4254             else if ((flags & ImGuiWindowFlags_Tooltip) != 0 || (flags & ImGuiWindowFlags_Popup) != 0)
4255                 bg_color_idx = ImGuiCol_PopupBg;
4256             else if ((flags & ImGuiWindowFlags_ChildWindow) != 0)
4257                 bg_color_idx = ImGuiCol_ChildWindowBg;
4258             ImVec4 bg_color = style.Colors[bg_color_idx];
4259             if (bg_alpha >= 0.0f)
4260                 bg_color.w = bg_alpha;
4261             bg_color.w *= style.Alpha;
4262             if (bg_color.w > 0.0f)
4263                 window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, ColorConvertFloat4ToU32(bg_color), window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImGuiCorner_All : ImGuiCorner_BottomLeft|ImGuiCorner_BottomRight);
4264 
4265             // Title bar
4266             if (!(flags & ImGuiWindowFlags_NoTitleBar))
4267                 window->DrawList->AddRectFilled(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32((g.FocusedWindow && window->RootNonPopupWindow == g.FocusedWindow->RootNonPopupWindow) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, ImGuiCorner_TopLeft|ImGuiCorner_TopRight);
4268 
4269             // Menu bar
4270             if (flags & ImGuiWindowFlags_MenuBar)
4271             {
4272                 ImRect menu_bar_rect = window->MenuBarRect();
4273                 if (flags & ImGuiWindowFlags_ShowBorders)
4274                     window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border));
4275                 window->DrawList->AddRectFilled(menu_bar_rect.GetTL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImGuiCorner_TopLeft|ImGuiCorner_TopRight);
4276             }
4277 
4278             // Scrollbars
4279             if (window->ScrollbarX)
4280                 Scrollbar(window, true);
4281             if (window->ScrollbarY)
4282                 Scrollbar(window, false);
4283 
4284             // Render resize grip
4285             // (after the input handling so we don't have a frame of latency)
4286             if (!(flags & ImGuiWindowFlags_NoResize))
4287             {
4288                 const ImVec2 br = window->Rect().GetBR();
4289                 window->DrawList->PathLineTo(br + ImVec2(-resize_corner_size, -window->BorderSize));
4290                 window->DrawList->PathLineTo(br + ImVec2(-window->BorderSize, -resize_corner_size));
4291                 window->DrawList->PathArcToFast(ImVec2(br.x - window_rounding - window->BorderSize, br.y - window_rounding - window->BorderSize), window_rounding, 0, 3);
4292                 window->DrawList->PathFillConvex(resize_col);
4293             }
4294 
4295             // Borders
4296             if (flags & ImGuiWindowFlags_ShowBorders)
4297             {
4298                 window->DrawList->AddRect(window->Pos+ImVec2(1,1), window->Pos+window->Size+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), window_rounding);
4299                 window->DrawList->AddRect(window->Pos, window->Pos+window->Size, GetColorU32(ImGuiCol_Border), window_rounding);
4300                 if (!(flags & ImGuiWindowFlags_NoTitleBar))
4301                     window->DrawList->AddLine(title_bar_rect.GetBL()+ImVec2(1,0), title_bar_rect.GetBR()-ImVec2(1,0), GetColorU32(ImGuiCol_Border));
4302             }
4303         }
4304 
4305         // Update ContentsRegionMax. All the variable it depends on are set above in this function.
4306         window->ContentsRegionRect.Min.x = -window->Scroll.x + window->WindowPadding.x;
4307         window->ContentsRegionRect.Min.y = -window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
4308         window->ContentsRegionRect.Max.x = -window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x));
4309         window->ContentsRegionRect.Max.y = -window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y));
4310 
4311         // Setup drawing context
4312         window->DC.IndentX = 0.0f + window->WindowPadding.x - window->Scroll.x;
4313         window->DC.GroupOffsetX = 0.0f;
4314         window->DC.ColumnsOffsetX = 0.0f;
4315         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.IndentX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y);
4316         window->DC.CursorPos = window->DC.CursorStartPos;
4317         window->DC.CursorPosPrevLine = window->DC.CursorPos;
4318         window->DC.CursorMaxPos = window->DC.CursorStartPos;
4319         window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f;
4320         window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
4321         window->DC.MenuBarAppending = false;
4322         window->DC.MenuBarOffsetX = ImMax(window->WindowPadding.x, style.ItemSpacing.x);
4323         window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
4324         window->DC.ChildWindows.resize(0);
4325         window->DC.LayoutType = ImGuiLayoutType_Vertical;
4326         window->DC.ItemWidth = window->ItemWidthDefault;
4327         window->DC.TextWrapPos = -1.0f; // disabled
4328         window->DC.AllowKeyboardFocus = true;
4329         window->DC.ButtonRepeat = false;
4330         window->DC.ItemWidthStack.resize(0);
4331         window->DC.AllowKeyboardFocusStack.resize(0);
4332         window->DC.ButtonRepeatStack.resize(0);
4333         window->DC.TextWrapPosStack.resize(0);
4334         window->DC.ColumnsCurrent = 0;
4335         window->DC.ColumnsCount = 1;
4336         window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
4337         window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.ColumnsStartPosY;
4338         window->DC.TreeDepth = 0;
4339         window->DC.StateStorage = &window->StateStorage;
4340         window->DC.GroupStack.resize(0);
4341         window->DC.ColorEditMode = ImGuiColorEditMode_UserSelect;
4342         window->MenuColumns.Update(3, style.ItemSpacing.x, !window_was_active);
4343 
4344         if (window->AutoFitFramesX > 0)
4345             window->AutoFitFramesX--;
4346         if (window->AutoFitFramesY > 0)
4347             window->AutoFitFramesY--;
4348 
4349         // New windows appears in front (we need to do that AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
4350         if (!window_was_active && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
4351             if (!(flags & (ImGuiWindowFlags_ChildWindow|ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
4352                 FocusWindow(window);
4353 
4354         // Title bar
4355         if (!(flags & ImGuiWindowFlags_NoTitleBar))
4356         {
4357             if (p_open != NULL)
4358             {
4359                 const float pad = 2.0f;
4360                 const float rad = (window->TitleBarHeight() - pad*2.0f) * 0.5f;
4361                 if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad))
4362                     *p_open = false;
4363             }
4364 
4365             const ImVec2 text_size = CalcTextSize(name, NULL, true);
4366             if (!(flags & ImGuiWindowFlags_NoCollapse))
4367                 RenderCollapseTriangle(window->Pos + style.FramePadding, !window->Collapsed, 1.0f);
4368 
4369             ImVec2 text_min = window->Pos;
4370             ImVec2 text_max = window->Pos + ImVec2(window->Size.x, style.FramePadding.y*2 + text_size.y);
4371             ImRect clip_rect;
4372             clip_rect.Max = ImVec2(window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x), text_max.y); // Match the size of CloseWindowButton()
4373             float pad_left = (flags & ImGuiWindowFlags_NoCollapse) == 0 ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x;
4374             float pad_right = (p_open != NULL) ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x;
4375             if (style.WindowTitleAlign.x > 0.0f) pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x);
4376             text_min.x += pad_left;
4377             text_max.x -= pad_right;
4378             clip_rect.Min = ImVec2(text_min.x, window->Pos.y);
4379             RenderTextClipped(text_min, text_max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect);
4380         }
4381 
4382         // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
4383         window->WindowRectClipped = window->Rect();
4384         window->WindowRectClipped.Clip(window->ClipRect);
4385 
4386         // Pressing CTRL+C while holding on a window copy its content to the clipboard
4387         // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope.
4388         // Maybe we can support CTRL+C on every element?
4389         /*
4390         if (g.ActiveId == move_id)
4391             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
4392                 ImGui::LogToClipboard();
4393         */
4394     }
4395 
4396     // Inner clipping rectangle
4397     // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
4398     // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior.
4399     const ImRect title_bar_rect = window->TitleBarRect();
4400     const float border_size = window->BorderSize;
4401     ImRect clip_rect; // Force round to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
4402     clip_rect.Min.x = ImFloor(0.5f + title_bar_rect.Min.x + ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
4403     clip_rect.Min.y = ImFloor(0.5f + title_bar_rect.Max.y + window->MenuBarHeight() + border_size);
4404     clip_rect.Max.x = ImFloor(0.5f + window->Pos.x + window->Size.x - window->ScrollbarSizes.x - ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
4405     clip_rect.Max.y = ImFloor(0.5f + window->Pos.y + window->Size.y - window->ScrollbarSizes.y - border_size);
4406     PushClipRect(clip_rect.Min, clip_rect.Max, true);
4407 
4408     // Clear 'accessed' flag last thing
4409     if (first_begin_of_the_frame)
4410         window->Accessed = false;
4411     window->BeginCount++;
4412     g.SetNextWindowSizeConstraint = false;
4413 
4414     // Child window can be out of sight and have "negative" clip windows.
4415     // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar).
4416     if (flags & ImGuiWindowFlags_ChildWindow)
4417     {
4418         IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
4419         window->Collapsed = parent_window && parent_window->Collapsed;
4420 
4421         if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
4422             window->Collapsed |= (window->WindowRectClipped.Min.x >= window->WindowRectClipped.Max.x || window->WindowRectClipped.Min.y >= window->WindowRectClipped.Max.y);
4423 
4424         // We also hide the window from rendering because we've already added its border to the command list.
4425         // (we could perform the check earlier in the function but it is simpler at this point)
4426         if (window->Collapsed)
4427             window->Active = false;
4428     }
4429     if (style.Alpha <= 0.0f)
4430         window->Active = false;
4431 
4432     // Return false if we don't intend to display anything to allow user to perform an early out optimization
4433     window->SkipItems = (window->Collapsed || !window->Active) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0;
4434     return !window->SkipItems;
4435 }
4436 
End()4437 void ImGui::End()
4438 {
4439     ImGuiContext& g = *GImGui;
4440     ImGuiWindow* window = g.CurrentWindow;
4441 
4442     if (window->DC.ColumnsCount != 1) // close columns set if any is open
4443         Columns(1, "#CLOSECOLUMNS");
4444     PopClipRect();   // inner window clip rectangle
4445 
4446     // Stop logging
4447     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
4448         LogFinish();
4449 
4450     // Pop
4451     // NB: we don't clear 'window->RootWindow'. The pointer is allowed to live until the next call to Begin().
4452     g.CurrentWindowStack.pop_back();
4453     if (window->Flags & ImGuiWindowFlags_Popup)
4454         g.CurrentPopupStack.pop_back();
4455     CheckStacksSize(window, false);
4456     SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
4457 }
4458 
4459 // Vertical scrollbar
4460 // The entire piece of code below is rather confusing because:
4461 // - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
4462 // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar
4463 // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.
Scrollbar(ImGuiWindow * window,bool horizontal)4464 static void Scrollbar(ImGuiWindow* window, bool horizontal)
4465 {
4466     ImGuiContext& g = *GImGui;
4467     const ImGuiStyle& style = g.Style;
4468     const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY");
4469 
4470     // Render background
4471     bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX);
4472     float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f;
4473     const ImRect window_rect = window->Rect();
4474     const float border_size = window->BorderSize;
4475     ImRect bb = horizontal
4476         ? ImRect(window->Pos.x + border_size, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - other_scrollbar_size_w - border_size, window_rect.Max.y - border_size)
4477         : ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size);
4478     if (!horizontal)
4479         bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f);
4480     if (bb.GetWidth() <= 0.0f || bb.GetHeight() <= 0.0f)
4481         return;
4482 
4483     float window_rounding = (window->Flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding;
4484     int window_rounding_corners;
4485     if (horizontal)
4486         window_rounding_corners = ImGuiCorner_BottomLeft | (other_scrollbar ? 0 : ImGuiCorner_BottomRight);
4487     else
4488         window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImGuiCorner_TopRight : 0) | (other_scrollbar ? 0 : ImGuiCorner_BottomRight);
4489     window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_ScrollbarBg), window_rounding, window_rounding_corners);
4490     bb.Reduce(ImVec2(ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f)));
4491 
4492     // V denote the main axis of the scrollbar
4493     float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight();
4494     float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y;
4495     float win_size_avail_v = (horizontal ? window->Size.x : window->Size.y) - other_scrollbar_size_w;
4496     float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y;
4497 
4498     // The grabable box size generally represent the amount visible (vs the total scrollable amount)
4499     // But we maintain a minimum size in pixel to allow for the user to still aim inside.
4500     const float grab_h_pixels = ImMin(ImMax(scrollbar_size_v * ImSaturate(win_size_avail_v / ImMax(win_size_contents_v, win_size_avail_v)), style.GrabMinSize), scrollbar_size_v);
4501     const float grab_h_norm = grab_h_pixels / scrollbar_size_v;
4502 
4503     // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().
4504     bool held = false;
4505     bool hovered = false;
4506     const bool previously_held = (g.ActiveId == id);
4507     ImGui::ButtonBehavior(bb, id, &hovered, &held);
4508 
4509     float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v);
4510     float scroll_ratio = ImSaturate(scroll_v / scroll_max);
4511     float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
4512     if (held && grab_h_norm < 1.0f)
4513     {
4514         float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y;
4515         float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
4516         float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y;
4517 
4518         // Click position in scrollbar normalized space (0.0f->1.0f)
4519         const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
4520         ImGui::SetHoveredID(id);
4521 
4522         bool seek_absolute = false;
4523         if (!previously_held)
4524         {
4525             // On initial click calculate the distance between mouse and the center of the grab
4526             if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm)
4527             {
4528                 *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
4529             }
4530             else
4531             {
4532                 seek_absolute = true;
4533                 *click_delta_to_grab_center_v = 0.0f;
4534             }
4535         }
4536 
4537         // Apply scroll
4538         // It is ok to modify Scroll here because we are being called in Begin() after the calculation of SizeContents and before setting up our starting position
4539         const float scroll_v_norm = ImSaturate((clicked_v_norm - *click_delta_to_grab_center_v - grab_h_norm*0.5f) / (1.0f - grab_h_norm));
4540         scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v));
4541         if (horizontal)
4542             window->Scroll.x = scroll_v;
4543         else
4544             window->Scroll.y = scroll_v;
4545 
4546         // Update values for rendering
4547         scroll_ratio = ImSaturate(scroll_v / scroll_max);
4548         grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
4549 
4550         // Update distance to grab now that we have seeked and saturated
4551         if (seek_absolute)
4552             *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
4553     }
4554 
4555     // Render
4556     const ImU32 grab_col = ImGui::GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab);
4557     if (horizontal)
4558         window->DrawList->AddRectFilled(ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y), ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y), grab_col, style.ScrollbarRounding);
4559     else
4560         window->DrawList->AddRectFilled(ImVec2(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm)), ImVec2(bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels), grab_col, style.ScrollbarRounding);
4561 }
4562 
4563 // Moving window to front of display (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)4564 void ImGui::FocusWindow(ImGuiWindow* window)
4565 {
4566     ImGuiContext& g = *GImGui;
4567 
4568     // Always mark the window we passed as focused. This is used for keyboard interactions such as tabbing.
4569     g.FocusedWindow = window;
4570 
4571     // Passing NULL allow to disable keyboard focus
4572     if (!window)
4573         return;
4574 
4575     // And move its root window to the top of the pile
4576     if (window->RootWindow)
4577         window = window->RootWindow;
4578 
4579     // Steal focus on active widgets
4580     if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
4581         if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
4582             ClearActiveID();
4583 
4584     // Bring to front
4585     if ((window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) || g.Windows.back() == window)
4586         return;
4587     for (int i = 0; i < g.Windows.Size; i++)
4588         if (g.Windows[i] == window)
4589         {
4590             g.Windows.erase(g.Windows.begin() + i);
4591             break;
4592         }
4593     g.Windows.push_back(window);
4594 }
4595 
PushItemWidth(float item_width)4596 void ImGui::PushItemWidth(float item_width)
4597 {
4598     ImGuiWindow* window = GetCurrentWindow();
4599     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
4600     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
4601 }
4602 
PushMultiItemsWidths(int components,float w_full)4603 static void PushMultiItemsWidths(int components, float w_full)
4604 {
4605     ImGuiWindow* window = ImGui::GetCurrentWindow();
4606     const ImGuiStyle& style = GImGui->Style;
4607     if (w_full <= 0.0f)
4608         w_full = ImGui::CalcItemWidth();
4609     const float w_item_one  = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
4610     const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
4611     window->DC.ItemWidthStack.push_back(w_item_last);
4612     for (int i = 0; i < components-1; i++)
4613         window->DC.ItemWidthStack.push_back(w_item_one);
4614     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
4615 }
4616 
PopItemWidth()4617 void ImGui::PopItemWidth()
4618 {
4619     ImGuiWindow* window = GetCurrentWindow();
4620     window->DC.ItemWidthStack.pop_back();
4621     window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
4622 }
4623 
CalcItemWidth()4624 float ImGui::CalcItemWidth()
4625 {
4626     ImGuiWindow* window = GetCurrentWindowRead();
4627     float w = window->DC.ItemWidth;
4628     if (w < 0.0f)
4629     {
4630         // Align to a right-side limit. We include 1 frame padding in the calculation because this is how the width is always used (we add 2 frame padding to it), but we could move that responsibility to the widget as well.
4631         float width_to_right_edge = GetContentRegionAvail().x;
4632         w = ImMax(1.0f, width_to_right_edge + w);
4633     }
4634     w = (float)(int)w;
4635     return w;
4636 }
4637 
GetDefaultFont()4638 static ImFont* GetDefaultFont()
4639 {
4640     ImGuiContext& g = *GImGui;
4641     return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0];
4642 }
4643 
SetCurrentFont(ImFont * font)4644 static void SetCurrentFont(ImFont* font)
4645 {
4646     ImGuiContext& g = *GImGui;
4647     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
4648     IM_ASSERT(font->Scale > 0.0f);
4649     g.Font = font;
4650     g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale;
4651     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
4652     g.FontTexUvWhitePixel = g.Font->ContainerAtlas->TexUvWhitePixel;
4653 }
4654 
PushFont(ImFont * font)4655 void ImGui::PushFont(ImFont* font)
4656 {
4657     ImGuiContext& g = *GImGui;
4658     if (!font)
4659         font = GetDefaultFont();
4660     SetCurrentFont(font);
4661     g.FontStack.push_back(font);
4662     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
4663 }
4664 
PopFont()4665 void  ImGui::PopFont()
4666 {
4667     ImGuiContext& g = *GImGui;
4668     g.CurrentWindow->DrawList->PopTextureID();
4669     g.FontStack.pop_back();
4670     SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
4671 }
4672 
PushAllowKeyboardFocus(bool allow_keyboard_focus)4673 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
4674 {
4675     ImGuiWindow* window = GetCurrentWindow();
4676     window->DC.AllowKeyboardFocus = allow_keyboard_focus;
4677     window->DC.AllowKeyboardFocusStack.push_back(allow_keyboard_focus);
4678 }
4679 
PopAllowKeyboardFocus()4680 void ImGui::PopAllowKeyboardFocus()
4681 {
4682     ImGuiWindow* window = GetCurrentWindow();
4683     window->DC.AllowKeyboardFocusStack.pop_back();
4684     window->DC.AllowKeyboardFocus = window->DC.AllowKeyboardFocusStack.empty() ? true : window->DC.AllowKeyboardFocusStack.back();
4685 }
4686 
PushButtonRepeat(bool repeat)4687 void ImGui::PushButtonRepeat(bool repeat)
4688 {
4689     ImGuiWindow* window = GetCurrentWindow();
4690     window->DC.ButtonRepeat = repeat;
4691     window->DC.ButtonRepeatStack.push_back(repeat);
4692 }
4693 
PopButtonRepeat()4694 void ImGui::PopButtonRepeat()
4695 {
4696     ImGuiWindow* window = GetCurrentWindow();
4697     window->DC.ButtonRepeatStack.pop_back();
4698     window->DC.ButtonRepeat = window->DC.ButtonRepeatStack.empty() ? false : window->DC.ButtonRepeatStack.back();
4699 }
4700 
PushTextWrapPos(float wrap_pos_x)4701 void ImGui::PushTextWrapPos(float wrap_pos_x)
4702 {
4703     ImGuiWindow* window = GetCurrentWindow();
4704     window->DC.TextWrapPos = wrap_pos_x;
4705     window->DC.TextWrapPosStack.push_back(wrap_pos_x);
4706 }
4707 
PopTextWrapPos()4708 void ImGui::PopTextWrapPos()
4709 {
4710     ImGuiWindow* window = GetCurrentWindow();
4711     window->DC.TextWrapPosStack.pop_back();
4712     window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
4713 }
4714 
PushStyleColor(ImGuiCol idx,const ImVec4 & col)4715 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
4716 {
4717     ImGuiContext& g = *GImGui;
4718     ImGuiColMod backup;
4719     backup.Col = idx;
4720     backup.BackupValue = g.Style.Colors[idx];
4721     g.ColorModifiers.push_back(backup);
4722     g.Style.Colors[idx] = col;
4723 }
4724 
PopStyleColor(int count)4725 void ImGui::PopStyleColor(int count)
4726 {
4727     ImGuiContext& g = *GImGui;
4728     while (count > 0)
4729     {
4730         ImGuiColMod& backup = g.ColorModifiers.back();
4731         g.Style.Colors[backup.Col] = backup.BackupValue;
4732         g.ColorModifiers.pop_back();
4733         count--;
4734     }
4735 }
4736 
4737 struct ImGuiStyleVarInfo
4738 {
4739     ImGuiDataType   Type;
4740     ImU32           Offset;
GetVarPtrImGuiStyleVarInfo4741     void*           GetVarPtr() const { return (void*)((unsigned char*)&GImGui->Style + Offset); }
4742 };
4743 
4744 static const ImGuiStyleVarInfo GStyleVarInfo[ImGuiStyleVar_Count_] =
4745 {
4746     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) },
4747     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) },
4748     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) },
4749     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) },
4750     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, ChildWindowRounding) },
4751     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) },
4752     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) },
4753     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) },
4754     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) },
4755     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) },
4756     { ImGuiDataType_Float,  (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },
4757     { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },
4758 };
4759 
GetStyleVarInfo(ImGuiStyleVar idx)4760 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
4761 {
4762     IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_Count_);
4763     return &GStyleVarInfo[idx];
4764 }
4765 
PushStyleVar(ImGuiStyleVar idx,float val)4766 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
4767 {
4768     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
4769     if (var_info->Type == ImGuiDataType_Float)
4770     {
4771         float* pvar = (float*)var_info->GetVarPtr();
4772         GImGui->StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
4773         *pvar = val;
4774         return;
4775     }
4776     IM_ASSERT(0); // Called function with wrong-type? Variable is not a float.
4777 }
4778 
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)4779 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
4780 {
4781     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
4782     if (var_info->Type == ImGuiDataType_Float2)
4783     {
4784         ImVec2* pvar = (ImVec2*)var_info->GetVarPtr();
4785         GImGui->StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
4786         *pvar = val;
4787         return;
4788     }
4789     IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2.
4790 }
4791 
PopStyleVar(int count)4792 void ImGui::PopStyleVar(int count)
4793 {
4794     ImGuiContext& g = *GImGui;
4795     while (count > 0)
4796     {
4797         ImGuiStyleMod& backup = g.StyleModifiers.back();
4798         const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
4799         if (info->Type == ImGuiDataType_Float)          (*(float*)info->GetVarPtr()) = backup.BackupFloat[0];
4800         else if (info->Type == ImGuiDataType_Float2)    (*(ImVec2*)info->GetVarPtr()) = ImVec2(backup.BackupFloat[0], backup.BackupFloat[1]);
4801         else if (info->Type == ImGuiDataType_Int)       (*(int*)info->GetVarPtr()) = backup.BackupInt[0];
4802         g.StyleModifiers.pop_back();
4803         count--;
4804     }
4805 }
4806 
GetStyleColName(ImGuiCol idx)4807 const char* ImGui::GetStyleColName(ImGuiCol idx)
4808 {
4809     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
4810     switch (idx)
4811     {
4812     case ImGuiCol_Text: return "Text";
4813     case ImGuiCol_TextDisabled: return "TextDisabled";
4814     case ImGuiCol_WindowBg: return "WindowBg";
4815     case ImGuiCol_ChildWindowBg: return "ChildWindowBg";
4816     case ImGuiCol_PopupBg: return "PopupBg";
4817     case ImGuiCol_Border: return "Border";
4818     case ImGuiCol_BorderShadow: return "BorderShadow";
4819     case ImGuiCol_FrameBg: return "FrameBg";
4820     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
4821     case ImGuiCol_FrameBgActive: return "FrameBgActive";
4822     case ImGuiCol_TitleBg: return "TitleBg";
4823     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
4824     case ImGuiCol_TitleBgActive: return "TitleBgActive";
4825     case ImGuiCol_MenuBarBg: return "MenuBarBg";
4826     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
4827     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
4828     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
4829     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
4830     case ImGuiCol_ComboBg: return "ComboBg";
4831     case ImGuiCol_CheckMark: return "CheckMark";
4832     case ImGuiCol_SliderGrab: return "SliderGrab";
4833     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
4834     case ImGuiCol_Button: return "Button";
4835     case ImGuiCol_ButtonHovered: return "ButtonHovered";
4836     case ImGuiCol_ButtonActive: return "ButtonActive";
4837     case ImGuiCol_Header: return "Header";
4838     case ImGuiCol_HeaderHovered: return "HeaderHovered";
4839     case ImGuiCol_HeaderActive: return "HeaderActive";
4840     case ImGuiCol_Column: return "Column";
4841     case ImGuiCol_ColumnHovered: return "ColumnHovered";
4842     case ImGuiCol_ColumnActive: return "ColumnActive";
4843     case ImGuiCol_ResizeGrip: return "ResizeGrip";
4844     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
4845     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
4846     case ImGuiCol_CloseButton: return "CloseButton";
4847     case ImGuiCol_CloseButtonHovered: return "CloseButtonHovered";
4848     case ImGuiCol_CloseButtonActive: return "CloseButtonActive";
4849     case ImGuiCol_PlotLines: return "PlotLines";
4850     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
4851     case ImGuiCol_PlotHistogram: return "PlotHistogram";
4852     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
4853     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
4854     case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening";
4855     }
4856     IM_ASSERT(0);
4857     return "Unknown";
4858 }
4859 
IsWindowHovered()4860 bool ImGui::IsWindowHovered()
4861 {
4862     ImGuiContext& g = *GImGui;
4863     return g.HoveredWindow == g.CurrentWindow && IsWindowContentHoverable(g.HoveredRootWindow);
4864 }
4865 
IsWindowFocused()4866 bool ImGui::IsWindowFocused()
4867 {
4868     ImGuiContext& g = *GImGui;
4869     return g.FocusedWindow == g.CurrentWindow;
4870 }
4871 
IsRootWindowFocused()4872 bool ImGui::IsRootWindowFocused()
4873 {
4874     ImGuiContext& g = *GImGui;
4875     return g.FocusedWindow == g.CurrentWindow->RootWindow;
4876 }
4877 
IsRootWindowOrAnyChildFocused()4878 bool ImGui::IsRootWindowOrAnyChildFocused()
4879 {
4880     ImGuiContext& g = *GImGui;
4881     return g.FocusedWindow && g.FocusedWindow->RootWindow == g.CurrentWindow->RootWindow;
4882 }
4883 
IsRootWindowOrAnyChildHovered()4884 bool ImGui::IsRootWindowOrAnyChildHovered()
4885 {
4886     ImGuiContext& g = *GImGui;
4887     return g.HoveredRootWindow && (g.HoveredRootWindow == g.CurrentWindow->RootWindow) && IsWindowContentHoverable(g.HoveredRootWindow);
4888 }
4889 
GetWindowWidth()4890 float ImGui::GetWindowWidth()
4891 {
4892     ImGuiWindow* window = GImGui->CurrentWindow;
4893     return window->Size.x;
4894 }
4895 
GetWindowHeight()4896 float ImGui::GetWindowHeight()
4897 {
4898     ImGuiWindow* window = GImGui->CurrentWindow;
4899     return window->Size.y;
4900 }
4901 
GetWindowPos()4902 ImVec2 ImGui::GetWindowPos()
4903 {
4904     ImGuiContext& g = *GImGui;
4905     ImGuiWindow* window = g.CurrentWindow;
4906     return window->Pos;
4907 }
4908 
SetWindowScrollY(ImGuiWindow * window,float new_scroll_y)4909 static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
4910 {
4911     window->DC.CursorMaxPos.y += window->Scroll.y;
4912     window->Scroll.y = new_scroll_y;
4913     window->DC.CursorMaxPos.y -= window->Scroll.y;
4914 }
4915 
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiSetCond cond)4916 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond)
4917 {
4918     // Test condition (NB: bit 0 is always true) and clear flags for next time
4919     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
4920         return;
4921     window->SetWindowPosAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
4922     window->SetWindowPosCenterWanted = false;
4923 
4924     // Set
4925     const ImVec2 old_pos = window->Pos;
4926     window->PosFloat = pos;
4927     window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
4928     window->DC.CursorPos += (window->Pos - old_pos);    // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor
4929     window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected.
4930 }
4931 
SetWindowPos(const ImVec2 & pos,ImGuiSetCond cond)4932 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiSetCond cond)
4933 {
4934     ImGuiWindow* window = GetCurrentWindowRead();
4935     SetWindowPos(window, pos, cond);
4936 }
4937 
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiSetCond cond)4938 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiSetCond cond)
4939 {
4940     if (ImGuiWindow* window = FindWindowByName(name))
4941         SetWindowPos(window, pos, cond);
4942 }
4943 
GetWindowSize()4944 ImVec2 ImGui::GetWindowSize()
4945 {
4946     ImGuiWindow* window = GetCurrentWindowRead();
4947     return window->Size;
4948 }
4949 
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiSetCond cond)4950 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond)
4951 {
4952     // Test condition (NB: bit 0 is always true) and clear flags for next time
4953     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
4954         return;
4955     window->SetWindowSizeAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
4956 
4957     // Set
4958     if (size.x > 0.0f)
4959     {
4960         window->AutoFitFramesX = 0;
4961         window->SizeFull.x = size.x;
4962     }
4963     else
4964     {
4965         window->AutoFitFramesX = 2;
4966         window->AutoFitOnlyGrows = false;
4967     }
4968     if (size.y > 0.0f)
4969     {
4970         window->AutoFitFramesY = 0;
4971         window->SizeFull.y = size.y;
4972     }
4973     else
4974     {
4975         window->AutoFitFramesY = 2;
4976         window->AutoFitOnlyGrows = false;
4977     }
4978 }
4979 
SetWindowSize(const ImVec2 & size,ImGuiSetCond cond)4980 void ImGui::SetWindowSize(const ImVec2& size, ImGuiSetCond cond)
4981 {
4982     SetWindowSize(GImGui->CurrentWindow, size, cond);
4983 }
4984 
SetWindowSize(const char * name,const ImVec2 & size,ImGuiSetCond cond)4985 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiSetCond cond)
4986 {
4987     ImGuiWindow* window = FindWindowByName(name);
4988     if (window)
4989         SetWindowSize(window, size, cond);
4990 }
4991 
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiSetCond cond)4992 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiSetCond cond)
4993 {
4994     // Test condition (NB: bit 0 is always true) and clear flags for next time
4995     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
4996         return;
4997     window->SetWindowCollapsedAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
4998 
4999     // Set
5000     window->Collapsed = collapsed;
5001 }
5002 
SetWindowCollapsed(bool collapsed,ImGuiSetCond cond)5003 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiSetCond cond)
5004 {
5005     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
5006 }
5007 
IsWindowCollapsed()5008 bool ImGui::IsWindowCollapsed()
5009 {
5010     return GImGui->CurrentWindow->Collapsed;
5011 }
5012 
SetWindowCollapsed(const char * name,bool collapsed,ImGuiSetCond cond)5013 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiSetCond cond)
5014 {
5015     ImGuiWindow* window = FindWindowByName(name);
5016     if (window)
5017         SetWindowCollapsed(window, collapsed, cond);
5018 }
5019 
SetWindowFocus()5020 void ImGui::SetWindowFocus()
5021 {
5022     FocusWindow(GImGui->CurrentWindow);
5023 }
5024 
SetWindowFocus(const char * name)5025 void ImGui::SetWindowFocus(const char* name)
5026 {
5027     if (name)
5028     {
5029         if (ImGuiWindow* window = FindWindowByName(name))
5030             FocusWindow(window);
5031     }
5032     else
5033     {
5034         FocusWindow(NULL);
5035     }
5036 }
5037 
SetNextWindowPos(const ImVec2 & pos,ImGuiSetCond cond)5038 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiSetCond cond)
5039 {
5040     ImGuiContext& g = *GImGui;
5041     g.SetNextWindowPosVal = pos;
5042     g.SetNextWindowPosCond = cond ? cond : ImGuiSetCond_Always;
5043 }
5044 
SetNextWindowPosCenter(ImGuiSetCond cond)5045 void ImGui::SetNextWindowPosCenter(ImGuiSetCond cond)
5046 {
5047     ImGuiContext& g = *GImGui;
5048     g.SetNextWindowPosVal = ImVec2(-FLT_MAX, -FLT_MAX);
5049     g.SetNextWindowPosCond = cond ? cond : ImGuiSetCond_Always;
5050 }
5051 
SetNextWindowSize(const ImVec2 & size,ImGuiSetCond cond)5052 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiSetCond cond)
5053 {
5054     ImGuiContext& g = *GImGui;
5055     g.SetNextWindowSizeVal = size;
5056     g.SetNextWindowSizeCond = cond ? cond : ImGuiSetCond_Always;
5057 }
5058 
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeConstraintCallback custom_callback,void * custom_callback_user_data)5059 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeConstraintCallback custom_callback, void* custom_callback_user_data)
5060 {
5061     ImGuiContext& g = *GImGui;
5062     g.SetNextWindowSizeConstraint = true;
5063     g.SetNextWindowSizeConstraintRect = ImRect(size_min, size_max);
5064     g.SetNextWindowSizeConstraintCallback = custom_callback;
5065     g.SetNextWindowSizeConstraintCallbackUserData = custom_callback_user_data;
5066 }
5067 
SetNextWindowContentSize(const ImVec2 & size)5068 void ImGui::SetNextWindowContentSize(const ImVec2& size)
5069 {
5070     ImGuiContext& g = *GImGui;
5071     g.SetNextWindowContentSizeVal = size;
5072     g.SetNextWindowContentSizeCond = ImGuiSetCond_Always;
5073 }
5074 
SetNextWindowContentWidth(float width)5075 void ImGui::SetNextWindowContentWidth(float width)
5076 {
5077     ImGuiContext& g = *GImGui;
5078     g.SetNextWindowContentSizeVal = ImVec2(width, g.SetNextWindowContentSizeCond ? g.SetNextWindowContentSizeVal.y : 0.0f);
5079     g.SetNextWindowContentSizeCond = ImGuiSetCond_Always;
5080 }
5081 
SetNextWindowCollapsed(bool collapsed,ImGuiSetCond cond)5082 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiSetCond cond)
5083 {
5084     ImGuiContext& g = *GImGui;
5085     g.SetNextWindowCollapsedVal = collapsed;
5086     g.SetNextWindowCollapsedCond = cond ? cond : ImGuiSetCond_Always;
5087 }
5088 
SetNextWindowFocus()5089 void ImGui::SetNextWindowFocus()
5090 {
5091     ImGuiContext& g = *GImGui;
5092     g.SetNextWindowFocus = true;
5093 }
5094 
5095 // In window space (not screen space!)
GetContentRegionMax()5096 ImVec2 ImGui::GetContentRegionMax()
5097 {
5098     ImGuiWindow* window = GetCurrentWindowRead();
5099     ImVec2 mx = window->ContentsRegionRect.Max;
5100     if (window->DC.ColumnsCount != 1)
5101         mx.x = GetColumnOffset(window->DC.ColumnsCurrent + 1) - window->WindowPadding.x;
5102     return mx;
5103 }
5104 
GetContentRegionAvail()5105 ImVec2 ImGui::GetContentRegionAvail()
5106 {
5107     ImGuiWindow* window = GetCurrentWindowRead();
5108     return GetContentRegionMax() - (window->DC.CursorPos - window->Pos);
5109 }
5110 
GetContentRegionAvailWidth()5111 float ImGui::GetContentRegionAvailWidth()
5112 {
5113     return GetContentRegionAvail().x;
5114 }
5115 
5116 // In window space (not screen space!)
GetWindowContentRegionMin()5117 ImVec2 ImGui::GetWindowContentRegionMin()
5118 {
5119     ImGuiWindow* window = GetCurrentWindowRead();
5120     return window->ContentsRegionRect.Min;
5121 }
5122 
GetWindowContentRegionMax()5123 ImVec2 ImGui::GetWindowContentRegionMax()
5124 {
5125     ImGuiWindow* window = GetCurrentWindowRead();
5126     return window->ContentsRegionRect.Max;
5127 }
5128 
GetWindowContentRegionWidth()5129 float ImGui::GetWindowContentRegionWidth()
5130 {
5131     ImGuiWindow* window = GetCurrentWindowRead();
5132     return window->ContentsRegionRect.Max.x - window->ContentsRegionRect.Min.x;
5133 }
5134 
GetTextLineHeight()5135 float ImGui::GetTextLineHeight()
5136 {
5137     ImGuiContext& g = *GImGui;
5138     return g.FontSize;
5139 }
5140 
GetTextLineHeightWithSpacing()5141 float ImGui::GetTextLineHeightWithSpacing()
5142 {
5143     ImGuiContext& g = *GImGui;
5144     return g.FontSize + g.Style.ItemSpacing.y;
5145 }
5146 
GetItemsLineHeightWithSpacing()5147 float ImGui::GetItemsLineHeightWithSpacing()
5148 {
5149     ImGuiContext& g = *GImGui;
5150     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
5151 }
5152 
GetWindowDrawList()5153 ImDrawList* ImGui::GetWindowDrawList()
5154 {
5155     ImGuiWindow* window = GetCurrentWindow();
5156     return window->DrawList;
5157 }
5158 
GetFont()5159 ImFont* ImGui::GetFont()
5160 {
5161     return GImGui->Font;
5162 }
5163 
GetFontSize()5164 float ImGui::GetFontSize()
5165 {
5166     return GImGui->FontSize;
5167 }
5168 
GetFontTexUvWhitePixel()5169 ImVec2 ImGui::GetFontTexUvWhitePixel()
5170 {
5171     return GImGui->FontTexUvWhitePixel;
5172 }
5173 
SetWindowFontScale(float scale)5174 void ImGui::SetWindowFontScale(float scale)
5175 {
5176     ImGuiContext& g = *GImGui;
5177     ImGuiWindow* window = GetCurrentWindow();
5178     window->FontWindowScale = scale;
5179     g.FontSize = window->CalcFontSize();
5180 }
5181 
5182 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
5183 // Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'.
GetCursorPos()5184 ImVec2 ImGui::GetCursorPos()
5185 {
5186     ImGuiWindow* window = GetCurrentWindowRead();
5187     return window->DC.CursorPos - window->Pos + window->Scroll;
5188 }
5189 
GetCursorPosX()5190 float ImGui::GetCursorPosX()
5191 {
5192     ImGuiWindow* window = GetCurrentWindowRead();
5193     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
5194 }
5195 
GetCursorPosY()5196 float ImGui::GetCursorPosY()
5197 {
5198     ImGuiWindow* window = GetCurrentWindowRead();
5199     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
5200 }
5201 
SetCursorPos(const ImVec2 & local_pos)5202 void ImGui::SetCursorPos(const ImVec2& local_pos)
5203 {
5204     ImGuiWindow* window = GetCurrentWindow();
5205     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
5206     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
5207 }
5208 
SetCursorPosX(float x)5209 void ImGui::SetCursorPosX(float x)
5210 {
5211     ImGuiWindow* window = GetCurrentWindow();
5212     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
5213     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
5214 }
5215 
SetCursorPosY(float y)5216 void ImGui::SetCursorPosY(float y)
5217 {
5218     ImGuiWindow* window = GetCurrentWindow();
5219     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
5220     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
5221 }
5222 
GetCursorStartPos()5223 ImVec2 ImGui::GetCursorStartPos()
5224 {
5225     ImGuiWindow* window = GetCurrentWindowRead();
5226     return window->DC.CursorStartPos - window->Pos;
5227 }
5228 
GetCursorScreenPos()5229 ImVec2 ImGui::GetCursorScreenPos()
5230 {
5231     ImGuiWindow* window = GetCurrentWindowRead();
5232     return window->DC.CursorPos;
5233 }
5234 
SetCursorScreenPos(const ImVec2 & screen_pos)5235 void ImGui::SetCursorScreenPos(const ImVec2& screen_pos)
5236 {
5237     ImGuiWindow* window = GetCurrentWindow();
5238     window->DC.CursorPos = screen_pos;
5239     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
5240 }
5241 
GetScrollX()5242 float ImGui::GetScrollX()
5243 {
5244     return GImGui->CurrentWindow->Scroll.x;
5245 }
5246 
GetScrollY()5247 float ImGui::GetScrollY()
5248 {
5249     return GImGui->CurrentWindow->Scroll.y;
5250 }
5251 
GetScrollMaxX()5252 float ImGui::GetScrollMaxX()
5253 {
5254     ImGuiWindow* window = GetCurrentWindowRead();
5255     return window->SizeContents.x - window->SizeFull.x - window->ScrollbarSizes.x;
5256 }
5257 
GetScrollMaxY()5258 float ImGui::GetScrollMaxY()
5259 {
5260     ImGuiWindow* window = GetCurrentWindowRead();
5261     return window->SizeContents.y - window->SizeFull.y - window->ScrollbarSizes.y;
5262 }
5263 
SetScrollX(float scroll_x)5264 void ImGui::SetScrollX(float scroll_x)
5265 {
5266     ImGuiWindow* window = GetCurrentWindow();
5267     window->ScrollTarget.x = scroll_x;
5268     window->ScrollTargetCenterRatio.x = 0.0f;
5269 }
5270 
SetScrollY(float scroll_y)5271 void ImGui::SetScrollY(float scroll_y)
5272 {
5273     ImGuiWindow* window = GetCurrentWindow();
5274     window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY
5275     window->ScrollTargetCenterRatio.y = 0.0f;
5276 }
5277 
SetScrollFromPosY(float pos_y,float center_y_ratio)5278 void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio)
5279 {
5280     // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
5281     ImGuiWindow* window = GetCurrentWindow();
5282     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
5283     window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y);
5284     if (center_y_ratio <= 0.0f && window->ScrollTarget.y <= window->WindowPadding.y)    // Minor hack to make "scroll to top" take account of WindowPadding, else it would scroll to (WindowPadding.y - ItemSpacing.y)
5285         window->ScrollTarget.y = 0.0f;
5286     window->ScrollTargetCenterRatio.y = center_y_ratio;
5287 }
5288 
5289 // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
SetScrollHere(float center_y_ratio)5290 void ImGui::SetScrollHere(float center_y_ratio)
5291 {
5292     ImGuiWindow* window = GetCurrentWindow();
5293     float target_y = window->DC.CursorPosPrevLine.y + (window->DC.PrevLineHeight * center_y_ratio) + (GImGui->Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line.
5294     SetScrollFromPosY(target_y - window->Pos.y, center_y_ratio);
5295 }
5296 
SetKeyboardFocusHere(int offset)5297 void ImGui::SetKeyboardFocusHere(int offset)
5298 {
5299     ImGuiWindow* window = GetCurrentWindow();
5300     window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
5301     window->FocusIdxTabRequestNext = INT_MAX;
5302 }
5303 
SetStateStorage(ImGuiStorage * tree)5304 void ImGui::SetStateStorage(ImGuiStorage* tree)
5305 {
5306     ImGuiWindow* window = GetCurrentWindow();
5307     window->DC.StateStorage = tree ? tree : &window->StateStorage;
5308 }
5309 
GetStateStorage()5310 ImGuiStorage* ImGui::GetStateStorage()
5311 {
5312     ImGuiWindow* window = GetCurrentWindowRead();
5313     return window->DC.StateStorage;
5314 }
5315 
TextV(const char * fmt,va_list args)5316 void ImGui::TextV(const char* fmt, va_list args)
5317 {
5318     ImGuiWindow* window = GetCurrentWindow();
5319     if (window->SkipItems)
5320         return;
5321 
5322     ImGuiContext& g = *GImGui;
5323     const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5324     TextUnformatted(g.TempBuffer, text_end);
5325 }
5326 
Text(const char * fmt,...)5327 void ImGui::Text(const char* fmt, ...)
5328 {
5329     va_list args;
5330     va_start(args, fmt);
5331     TextV(fmt, args);
5332     va_end(args);
5333 }
5334 
TextColoredV(const ImVec4 & col,const char * fmt,va_list args)5335 void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args)
5336 {
5337     PushStyleColor(ImGuiCol_Text, col);
5338     TextV(fmt, args);
5339     PopStyleColor();
5340 }
5341 
TextColored(const ImVec4 & col,const char * fmt,...)5342 void ImGui::TextColored(const ImVec4& col, const char* fmt, ...)
5343 {
5344     va_list args;
5345     va_start(args, fmt);
5346     TextColoredV(col, fmt, args);
5347     va_end(args);
5348 }
5349 
TextDisabledV(const char * fmt,va_list args)5350 void ImGui::TextDisabledV(const char* fmt, va_list args)
5351 {
5352     PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]);
5353     TextV(fmt, args);
5354     PopStyleColor();
5355 }
5356 
TextDisabled(const char * fmt,...)5357 void ImGui::TextDisabled(const char* fmt, ...)
5358 {
5359     va_list args;
5360     va_start(args, fmt);
5361     TextDisabledV(fmt, args);
5362     va_end(args);
5363 }
5364 
TextWrappedV(const char * fmt,va_list args)5365 void ImGui::TextWrappedV(const char* fmt, va_list args)
5366 {
5367     bool need_wrap = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f);    // Keep existing wrap position is one ia already set
5368     if (need_wrap) PushTextWrapPos(0.0f);
5369     TextV(fmt, args);
5370     if (need_wrap) PopTextWrapPos();
5371 }
5372 
TextWrapped(const char * fmt,...)5373 void ImGui::TextWrapped(const char* fmt, ...)
5374 {
5375     va_list args;
5376     va_start(args, fmt);
5377     TextWrappedV(fmt, args);
5378     va_end(args);
5379 }
5380 
TextUnformatted(const char * text,const char * text_end)5381 void ImGui::TextUnformatted(const char* text, const char* text_end)
5382 {
5383     ImGuiWindow* window = GetCurrentWindow();
5384     if (window->SkipItems)
5385         return;
5386 
5387     ImGuiContext& g = *GImGui;
5388     IM_ASSERT(text != NULL);
5389     const char* text_begin = text;
5390     if (text_end == NULL)
5391         text_end = text + strlen(text); // FIXME-OPT
5392 
5393     const float wrap_pos_x = window->DC.TextWrapPos;
5394     const bool wrap_enabled = wrap_pos_x >= 0.0f;
5395     if (text_end - text > 2000 && !wrap_enabled)
5396     {
5397         // Long text!
5398         // Perform manual coarse clipping to optimize for long multi-line text
5399         // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.
5400         // We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line.
5401         const char* line = text;
5402         const float line_height = GetTextLineHeight();
5403         const ImVec2 text_pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrentLineTextBaseOffset);
5404         const ImRect clip_rect = window->ClipRect;
5405         ImVec2 text_size(0,0);
5406 
5407         if (text_pos.y <= clip_rect.Max.y)
5408         {
5409             ImVec2 pos = text_pos;
5410 
5411             // Lines to skip (can't skip when logging text)
5412             if (!g.LogEnabled)
5413             {
5414                 int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height);
5415                 if (lines_skippable > 0)
5416                 {
5417                     int lines_skipped = 0;
5418                     while (line < text_end && lines_skipped < lines_skippable)
5419                     {
5420                         const char* line_end = strchr(line, '\n');
5421                         if (!line_end)
5422                             line_end = text_end;
5423                         line = line_end + 1;
5424                         lines_skipped++;
5425                     }
5426                     pos.y += lines_skipped * line_height;
5427                 }
5428             }
5429 
5430             // Lines to render
5431             if (line < text_end)
5432             {
5433                 ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height));
5434                 while (line < text_end)
5435                 {
5436                     const char* line_end = strchr(line, '\n');
5437                     if (IsClippedEx(line_rect, NULL, false))
5438                         break;
5439 
5440                     const ImVec2 line_size = CalcTextSize(line, line_end, false);
5441                     text_size.x = ImMax(text_size.x, line_size.x);
5442                     RenderText(pos, line, line_end, false);
5443                     if (!line_end)
5444                         line_end = text_end;
5445                     line = line_end + 1;
5446                     line_rect.Min.y += line_height;
5447                     line_rect.Max.y += line_height;
5448                     pos.y += line_height;
5449                 }
5450 
5451                 // Count remaining lines
5452                 int lines_skipped = 0;
5453                 while (line < text_end)
5454                 {
5455                     const char* line_end = strchr(line, '\n');
5456                     if (!line_end)
5457                         line_end = text_end;
5458                     line = line_end + 1;
5459                     lines_skipped++;
5460                 }
5461                 pos.y += lines_skipped * line_height;
5462             }
5463 
5464             text_size.y += (pos - text_pos).y;
5465         }
5466 
5467         ImRect bb(text_pos, text_pos + text_size);
5468         ItemSize(bb);
5469         ItemAdd(bb, NULL);
5470     }
5471     else
5472     {
5473         const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;
5474         const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);
5475 
5476         // Account of baseline offset
5477         ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset);
5478         ImRect bb(text_pos, text_pos + text_size);
5479         ItemSize(text_size);
5480         if (!ItemAdd(bb, NULL))
5481             return;
5482 
5483         // Render (we don't hide text after ## in this end-user function)
5484         RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width);
5485     }
5486 }
5487 
AlignFirstTextHeightToWidgets()5488 void ImGui::AlignFirstTextHeightToWidgets()
5489 {
5490     ImGuiWindow* window = GetCurrentWindow();
5491     if (window->SkipItems)
5492         return;
5493 
5494     // Declare a dummy item size to that upcoming items that are smaller will center-align on the newly expanded line height.
5495     ImGuiContext& g = *GImGui;
5496     ItemSize(ImVec2(0, g.FontSize + g.Style.FramePadding.y*2), g.Style.FramePadding.y);
5497     SameLine(0, 0);
5498 }
5499 
5500 // Add a label+text combo aligned to other label+value widgets
LabelTextV(const char * label,const char * fmt,va_list args)5501 void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
5502 {
5503     ImGuiWindow* window = GetCurrentWindow();
5504     if (window->SkipItems)
5505         return;
5506 
5507     ImGuiContext& g = *GImGui;
5508     const ImGuiStyle& style = g.Style;
5509     const float w = CalcItemWidth();
5510 
5511     const ImVec2 label_size = CalcTextSize(label, NULL, true);
5512     const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2));
5513     const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y*2) + label_size);
5514     ItemSize(total_bb, style.FramePadding.y);
5515     if (!ItemAdd(total_bb, NULL))
5516         return;
5517 
5518     // Render
5519     const char* value_text_begin = &g.TempBuffer[0];
5520     const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5521     RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f,0.5f));
5522     if (label_size.x > 0.0f)
5523         RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
5524 }
5525 
LabelText(const char * label,const char * fmt,...)5526 void ImGui::LabelText(const char* label, const char* fmt, ...)
5527 {
5528     va_list args;
5529     va_start(args, fmt);
5530     LabelTextV(label, fmt, args);
5531     va_end(args);
5532 }
5533 
IsWindowContentHoverable(ImGuiWindow * window)5534 static inline bool IsWindowContentHoverable(ImGuiWindow* window)
5535 {
5536     // An active popup disable hovering on other windows (apart from its own children)
5537     ImGuiContext& g = *GImGui;
5538     if (ImGuiWindow* focused_window = g.FocusedWindow)
5539         if (ImGuiWindow* focused_root_window = focused_window->RootWindow)
5540             if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) != 0 && focused_root_window->WasActive && focused_root_window != window->RootWindow)
5541                 return false;
5542 
5543     return true;
5544 }
5545 
ButtonBehavior(const ImRect & bb,ImGuiID id,bool * out_hovered,bool * out_held,ImGuiButtonFlags flags)5546 bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)
5547 {
5548     ImGuiContext& g = *GImGui;
5549     ImGuiWindow* window = GetCurrentWindow();
5550 
5551     if (flags & ImGuiButtonFlags_Disabled)
5552     {
5553         if (out_hovered) *out_hovered = false;
5554         if (out_held) *out_held = false;
5555         if (g.ActiveId == id) ClearActiveID();
5556         return false;
5557     }
5558 
5559     if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0)
5560         flags |= ImGuiButtonFlags_PressedOnClickRelease;
5561 
5562     bool pressed = false;
5563     bool hovered = IsHovered(bb, id, (flags & ImGuiButtonFlags_FlattenChilds) != 0);
5564     if (hovered)
5565     {
5566         SetHoveredID(id);
5567         if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
5568         {
5569             //                        | CLICKING        | HOLDING with ImGuiButtonFlags_Repeat
5570             // PressedOnClickRelease  |  <on release>*  |  <on repeat> <on repeat> .. (NOT on release)  <-- MOST COMMON! (*) only if both click/release were over bounds
5571             // PressedOnClick         |  <on click>     |  <on click> <on repeat> <on repeat> ..
5572             // PressedOnRelease       |  <on release>   |  <on repeat> <on repeat> .. (NOT on release)
5573             // PressedOnDoubleClick   |  <on dclick>    |  <on dclick> <on repeat> <on repeat> ..
5574             if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0])
5575             {
5576                 SetActiveID(id, window); // Hold on ID
5577                 FocusWindow(window);
5578                 g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;
5579             }
5580             if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0]))
5581             {
5582                 pressed = true;
5583                 ClearActiveID();
5584                 FocusWindow(window);
5585             }
5586             if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0])
5587             {
5588                 if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay))  // Repeat mode trumps <on release>
5589                     pressed = true;
5590                 ClearActiveID();
5591             }
5592 
5593             // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above).
5594             // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings.
5595             if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true))
5596                 pressed = true;
5597         }
5598     }
5599 
5600     bool held = false;
5601     if (g.ActiveId == id)
5602     {
5603         if (g.IO.MouseDown[0])
5604         {
5605             held = true;
5606         }
5607         else
5608         {
5609             if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease))
5610                 if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay))  // Repeat mode trumps <on release>
5611                     pressed = true;
5612             ClearActiveID();
5613         }
5614     }
5615 
5616     // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one.
5617     if (hovered && (flags & ImGuiButtonFlags_AllowOverlapMode) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0))
5618         hovered = pressed = held = false;
5619 
5620     if (out_hovered) *out_hovered = hovered;
5621     if (out_held) *out_held = held;
5622 
5623     return pressed;
5624 }
5625 
ButtonEx(const char * label,const ImVec2 & size_arg,ImGuiButtonFlags flags)5626 bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags)
5627 {
5628     ImGuiWindow* window = GetCurrentWindow();
5629     if (window->SkipItems)
5630         return false;
5631 
5632     ImGuiContext& g = *GImGui;
5633     const ImGuiStyle& style = g.Style;
5634     const ImGuiID id = window->GetID(label);
5635     const ImVec2 label_size = CalcTextSize(label, NULL, true);
5636 
5637     ImVec2 pos = window->DC.CursorPos;
5638     if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
5639         pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y;
5640     ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);
5641 
5642     const ImRect bb(pos, pos + size);
5643     ItemSize(bb, style.FramePadding.y);
5644     if (!ItemAdd(bb, &id))
5645         return false;
5646 
5647     if (window->DC.ButtonRepeat) flags |= ImGuiButtonFlags_Repeat;
5648     bool hovered, held;
5649     bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
5650 
5651     // Render
5652     const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
5653     RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
5654     RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb);
5655 
5656     // Automatically close popups
5657     //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
5658     //    CloseCurrentPopup();
5659 
5660     return pressed;
5661 }
5662 
Button(const char * label,const ImVec2 & size_arg)5663 bool ImGui::Button(const char* label, const ImVec2& size_arg)
5664 {
5665     return ButtonEx(label, size_arg, 0);
5666 }
5667 
5668 // Small buttons fits within text without additional vertical spacing.
SmallButton(const char * label)5669 bool ImGui::SmallButton(const char* label)
5670 {
5671     ImGuiContext& g = *GImGui;
5672     float backup_padding_y = g.Style.FramePadding.y;
5673     g.Style.FramePadding.y = 0.0f;
5674     bool pressed = ButtonEx(label, ImVec2(0,0), ImGuiButtonFlags_AlignTextBaseLine);
5675     g.Style.FramePadding.y = backup_padding_y;
5676     return pressed;
5677 }
5678 
5679 // Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack.
5680 // Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id)
InvisibleButton(const char * str_id,const ImVec2 & size_arg)5681 bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg)
5682 {
5683     ImGuiWindow* window = GetCurrentWindow();
5684     if (window->SkipItems)
5685         return false;
5686 
5687     const ImGuiID id = window->GetID(str_id);
5688     ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f);
5689     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
5690     ItemSize(bb);
5691     if (!ItemAdd(bb, &id))
5692         return false;
5693 
5694     bool hovered, held;
5695     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
5696 
5697     return pressed;
5698 }
5699 
5700 // Upper-right button to close a window.
CloseButton(ImGuiID id,const ImVec2 & pos,float radius)5701 bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius)
5702 {
5703     ImGuiWindow* window = GetCurrentWindow();
5704 
5705     const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius));
5706 
5707     bool hovered, held;
5708     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
5709 
5710     // Render
5711     const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton);
5712     const ImVec2 center = bb.GetCenter();
5713     window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), col, 12);
5714 
5715     const float cross_extent = (radius * 0.7071f) - 1.0f;
5716     if (hovered)
5717     {
5718         window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), GetColorU32(ImGuiCol_Text));
5719         window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), GetColorU32(ImGuiCol_Text));
5720     }
5721 
5722     return pressed;
5723 }
5724 
Image(ImTextureID user_texture_id,const ImVec2 & size,const ImVec2 & uv0,const ImVec2 & uv1,const ImVec4 & tint_col,const ImVec4 & border_col)5725 void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
5726 {
5727     ImGuiWindow* window = GetCurrentWindow();
5728     if (window->SkipItems)
5729         return;
5730 
5731     ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
5732     if (border_col.w > 0.0f)
5733         bb.Max += ImVec2(2,2);
5734     ItemSize(bb);
5735     if (!ItemAdd(bb, NULL))
5736         return;
5737 
5738     if (border_col.w > 0.0f)
5739     {
5740         window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f);
5741         window->DrawList->AddImage(user_texture_id, bb.Min+ImVec2(1,1), bb.Max-ImVec2(1,1), uv0, uv1, GetColorU32(tint_col));
5742     }
5743     else
5744     {
5745         window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col));
5746     }
5747 }
5748 
5749 // frame_padding < 0: uses FramePadding from style (default)
5750 // frame_padding = 0: no framing
5751 // frame_padding > 0: set framing size
5752 // The color used are the button colors.
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)5753 bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col)
5754 {
5755     ImGuiWindow* window = GetCurrentWindow();
5756     if (window->SkipItems)
5757         return false;
5758 
5759     ImGuiContext& g = *GImGui;
5760     const ImGuiStyle& style = g.Style;
5761 
5762     // Default to using texture ID as ID. User can still push string/integer prefixes.
5763     // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV.
5764     PushID((void *)user_texture_id);
5765     const ImGuiID id = window->GetID("#image");
5766     PopID();
5767 
5768     const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding;
5769     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding*2);
5770     const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size);
5771     ItemSize(bb);
5772     if (!ItemAdd(bb, &id))
5773         return false;
5774 
5775     bool hovered, held;
5776     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
5777 
5778     // Render
5779     const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
5780     RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding));
5781     if (bg_col.w > 0.0f)
5782         window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col));
5783     window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col));
5784 
5785     return pressed;
5786 }
5787 
5788 // Start logging ImGui output to TTY
LogToTTY(int max_depth)5789 void ImGui::LogToTTY(int max_depth)
5790 {
5791     ImGuiContext& g = *GImGui;
5792     if (g.LogEnabled)
5793         return;
5794     ImGuiWindow* window = GetCurrentWindowRead();
5795 
5796     g.LogEnabled = true;
5797     g.LogFile = stdout;
5798     g.LogStartDepth = window->DC.TreeDepth;
5799     if (max_depth >= 0)
5800         g.LogAutoExpandMaxDepth = max_depth;
5801 }
5802 
5803 // Start logging ImGui output to given file
LogToFile(int max_depth,const char * filename)5804 void ImGui::LogToFile(int max_depth, const char* filename)
5805 {
5806     ImGuiContext& g = *GImGui;
5807     if (g.LogEnabled)
5808         return;
5809     ImGuiWindow* window = GetCurrentWindowRead();
5810 
5811     if (!filename)
5812     {
5813         filename = g.IO.LogFilename;
5814         if (!filename)
5815             return;
5816     }
5817 
5818     g.LogFile = ImFileOpen(filename, "ab");
5819     if (!g.LogFile)
5820     {
5821         IM_ASSERT(g.LogFile != NULL); // Consider this an error
5822         return;
5823     }
5824     g.LogEnabled = true;
5825     g.LogStartDepth = window->DC.TreeDepth;
5826     if (max_depth >= 0)
5827         g.LogAutoExpandMaxDepth = max_depth;
5828 }
5829 
5830 // Start logging ImGui output to clipboard
LogToClipboard(int max_depth)5831 void ImGui::LogToClipboard(int max_depth)
5832 {
5833     ImGuiContext& g = *GImGui;
5834     if (g.LogEnabled)
5835         return;
5836     ImGuiWindow* window = GetCurrentWindowRead();
5837 
5838     g.LogEnabled = true;
5839     g.LogFile = NULL;
5840     g.LogStartDepth = window->DC.TreeDepth;
5841     if (max_depth >= 0)
5842         g.LogAutoExpandMaxDepth = max_depth;
5843 }
5844 
LogFinish()5845 void ImGui::LogFinish()
5846 {
5847     ImGuiContext& g = *GImGui;
5848     if (!g.LogEnabled)
5849         return;
5850 
5851     LogText(IM_NEWLINE);
5852     g.LogEnabled = false;
5853     if (g.LogFile != NULL)
5854     {
5855         if (g.LogFile == stdout)
5856             fflush(g.LogFile);
5857         else
5858             fclose(g.LogFile);
5859         g.LogFile = NULL;
5860     }
5861     if (g.LogClipboard->size() > 1)
5862     {
5863         SetClipboardText(g.LogClipboard->begin());
5864         g.LogClipboard->clear();
5865     }
5866 }
5867 
5868 // Helper to display logging buttons
LogButtons()5869 void ImGui::LogButtons()
5870 {
5871     ImGuiContext& g = *GImGui;
5872 
5873     PushID("LogButtons");
5874     const bool log_to_tty = Button("Log To TTY"); SameLine();
5875     const bool log_to_file = Button("Log To File"); SameLine();
5876     const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
5877     PushItemWidth(80.0f);
5878     PushAllowKeyboardFocus(false);
5879     SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL);
5880     PopAllowKeyboardFocus();
5881     PopItemWidth();
5882     PopID();
5883 
5884     // Start logging at the end of the function so that the buttons don't appear in the log
5885     if (log_to_tty)
5886         LogToTTY(g.LogAutoExpandMaxDepth);
5887     if (log_to_file)
5888         LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename);
5889     if (log_to_clipboard)
5890         LogToClipboard(g.LogAutoExpandMaxDepth);
5891 }
5892 
TreeNodeBehaviorIsOpen(ImGuiID id,ImGuiTreeNodeFlags flags)5893 bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags)
5894 {
5895     if (flags & ImGuiTreeNodeFlags_Leaf)
5896         return true;
5897 
5898     // We only write to the tree storage if the user clicks (or explicitely use SetNextTreeNode*** functions)
5899     ImGuiContext& g = *GImGui;
5900     ImGuiWindow* window = g.CurrentWindow;
5901     ImGuiStorage* storage = window->DC.StateStorage;
5902 
5903     bool is_open;
5904     if (g.SetNextTreeNodeOpenCond != 0)
5905     {
5906         if (g.SetNextTreeNodeOpenCond & ImGuiSetCond_Always)
5907         {
5908             is_open = g.SetNextTreeNodeOpenVal;
5909             storage->SetInt(id, is_open);
5910         }
5911         else
5912         {
5913             // We treat ImGuiSetCondition_Once and ImGuiSetCondition_FirstUseEver the same because tree node state are not saved persistently.
5914             const int stored_value = storage->GetInt(id, -1);
5915             if (stored_value == -1)
5916             {
5917                 is_open = g.SetNextTreeNodeOpenVal;
5918                 storage->SetInt(id, is_open);
5919             }
5920             else
5921             {
5922                 is_open = stored_value != 0;
5923             }
5924         }
5925         g.SetNextTreeNodeOpenCond = 0;
5926     }
5927     else
5928     {
5929         is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;
5930     }
5931 
5932     // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior).
5933     // NB- If we are above max depth we still allow manually opened nodes to be logged.
5934     if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth)
5935         is_open = true;
5936 
5937     return is_open;
5938 }
5939 
TreeNodeBehavior(ImGuiID id,ImGuiTreeNodeFlags flags,const char * label,const char * label_end)5940 bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end)
5941 {
5942     ImGuiWindow* window = GetCurrentWindow();
5943     if (window->SkipItems)
5944         return false;
5945 
5946     ImGuiContext& g = *GImGui;
5947     const ImGuiStyle& style = g.Style;
5948     const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;
5949     const ImVec2 padding = display_frame ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f);
5950 
5951     if (!label_end)
5952         label_end = FindRenderedTextEnd(label);
5953     const ImVec2 label_size = CalcTextSize(label, label_end, false);
5954 
5955     // We vertically grow up to current line height up the typical widget height.
5956     const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset - padding.y); // Latch before ItemSize changes it
5957     const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2);
5958     ImRect bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height));
5959     if (display_frame)
5960     {
5961         // Framed header expand a little outside the default padding
5962         bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1;
5963         bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1;
5964     }
5965 
5966     const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2));   // Collapser arrow width + Spacing
5967     const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f);   // Include collapser
5968     ItemSize(ImVec2(text_width, frame_height), text_base_offset_y);
5969 
5970     // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
5971     // (Ideally we'd want to add a flag for the user to specify we want want the hit test to be done up to the right side of the content or not)
5972     const ImRect interact_bb = display_frame ? bb : ImRect(bb.Min.x, bb.Min.y, bb.Min.x + text_width + style.ItemSpacing.x*2, bb.Max.y);
5973     bool is_open = TreeNodeBehaviorIsOpen(id, flags);
5974     if (!ItemAdd(interact_bb, &id))
5975     {
5976         if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
5977             TreePushRawID(id);
5978         return is_open;
5979     }
5980 
5981     // Flags that affects opening behavior:
5982     // - 0(default) ..................... single-click anywhere to open
5983     // - OpenOnDoubleClick .............. double-click anywhere to open
5984     // - OpenOnArrow .................... single-click on arrow to open
5985     // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open
5986     ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowOverlapMode) ? ImGuiButtonFlags_AllowOverlapMode : 0);
5987     if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
5988         button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0);
5989     bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags);
5990     if (pressed && !(flags & ImGuiTreeNodeFlags_Leaf))
5991     {
5992         bool toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick));
5993         if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
5994             toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y));
5995         if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
5996             toggled |= g.IO.MouseDoubleClicked[0];
5997         if (toggled)
5998         {
5999             is_open = !is_open;
6000             window->DC.StateStorage->SetInt(id, is_open);
6001         }
6002     }
6003     if (flags & ImGuiTreeNodeFlags_AllowOverlapMode)
6004         SetItemAllowOverlap();
6005 
6006     // Render
6007     const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
6008     const ImVec2 text_pos = bb.Min + ImVec2(text_offset_x, padding.y + text_base_offset_y);
6009     if (display_frame)
6010     {
6011         // Framed type
6012         RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
6013         RenderCollapseTriangle(bb.Min + padding + ImVec2(0.0f, text_base_offset_y), is_open, 1.0f);
6014         if (g.LogEnabled)
6015         {
6016             // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here.
6017             const char log_prefix[] = "\n##";
6018             const char log_suffix[] = "##";
6019             LogRenderedText(text_pos, log_prefix, log_prefix+3);
6020             RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size);
6021             LogRenderedText(text_pos, log_suffix+1, log_suffix+3);
6022         }
6023         else
6024         {
6025             RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size);
6026         }
6027     }
6028     else
6029     {
6030         // Unframed typed for tree nodes
6031         if (hovered || (flags & ImGuiTreeNodeFlags_Selected))
6032             RenderFrame(bb.Min, bb.Max, col, false);
6033 
6034         if (flags & ImGuiTreeNodeFlags_Bullet)
6035             RenderBullet(bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y));
6036         else if (!(flags & ImGuiTreeNodeFlags_Leaf))
6037             RenderCollapseTriangle(bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open, 0.70f);
6038         if (g.LogEnabled)
6039             LogRenderedText(text_pos, ">");
6040         RenderText(text_pos, label, label_end, false);
6041     }
6042 
6043     if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
6044         TreePushRawID(id);
6045     return is_open;
6046 }
6047 
6048 // CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag).
6049 // This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode().
CollapsingHeader(const char * label,ImGuiTreeNodeFlags flags)6050 bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags)
6051 {
6052     ImGuiWindow* window = GetCurrentWindow();
6053     if (window->SkipItems)
6054         return false;
6055 
6056     return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen, label);
6057 }
6058 
CollapsingHeader(const char * label,bool * p_open,ImGuiTreeNodeFlags flags)6059 bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags)
6060 {
6061     ImGuiWindow* window = GetCurrentWindow();
6062     if (window->SkipItems)
6063         return false;
6064 
6065     if (p_open && !*p_open)
6066         return false;
6067 
6068     ImGuiID id = window->GetID(label);
6069     bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen | (p_open ? ImGuiTreeNodeFlags_AllowOverlapMode : 0), label);
6070     if (p_open)
6071     {
6072         // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.
6073         ImGuiContext& g = *GImGui;
6074         float button_sz = g.FontSize * 0.5f;
6075         if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_sz, window->DC.LastItemRect.Min.y + g.Style.FramePadding.y + button_sz), button_sz))
6076             *p_open = false;
6077     }
6078 
6079     return is_open;
6080 }
6081 
TreeNodeEx(const char * label,ImGuiTreeNodeFlags flags)6082 bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags)
6083 {
6084     ImGuiWindow* window = GetCurrentWindow();
6085     if (window->SkipItems)
6086         return false;
6087 
6088     return TreeNodeBehavior(window->GetID(label), flags, label, NULL);
6089 }
6090 
TreeNodeExV(const char * str_id,ImGuiTreeNodeFlags flags,const char * fmt,va_list args)6091 bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
6092 {
6093     ImGuiWindow* window = GetCurrentWindow();
6094     if (window->SkipItems)
6095         return false;
6096 
6097     ImGuiContext& g = *GImGui;
6098     const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
6099     return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end);
6100 }
6101 
TreeNodeExV(const void * ptr_id,ImGuiTreeNodeFlags flags,const char * fmt,va_list args)6102 bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
6103 {
6104     ImGuiWindow* window = GetCurrentWindow();
6105     if (window->SkipItems)
6106         return false;
6107 
6108     ImGuiContext& g = *GImGui;
6109     const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
6110     return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end);
6111 }
6112 
TreeNodeV(const char * str_id,const char * fmt,va_list args)6113 bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)
6114 {
6115     return TreeNodeExV(str_id, 0, fmt, args);
6116 }
6117 
TreeNodeV(const void * ptr_id,const char * fmt,va_list args)6118 bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)
6119 {
6120     return TreeNodeExV(ptr_id, 0, fmt, args);
6121 }
6122 
TreeNodeEx(const char * str_id,ImGuiTreeNodeFlags flags,const char * fmt,...)6123 bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
6124 {
6125     va_list args;
6126     va_start(args, fmt);
6127     bool is_open = TreeNodeExV(str_id, flags, fmt, args);
6128     va_end(args);
6129     return is_open;
6130 }
6131 
TreeNodeEx(const void * ptr_id,ImGuiTreeNodeFlags flags,const char * fmt,...)6132 bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
6133 {
6134     va_list args;
6135     va_start(args, fmt);
6136     bool is_open = TreeNodeExV(ptr_id, flags, fmt, args);
6137     va_end(args);
6138     return is_open;
6139 }
6140 
TreeNode(const char * str_id,const char * fmt,...)6141 bool ImGui::TreeNode(const char* str_id, const char* fmt, ...)
6142 {
6143     va_list args;
6144     va_start(args, fmt);
6145     bool is_open = TreeNodeExV(str_id, 0, fmt, args);
6146     va_end(args);
6147     return is_open;
6148 }
6149 
TreeNode(const void * ptr_id,const char * fmt,...)6150 bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)
6151 {
6152     va_list args;
6153     va_start(args, fmt);
6154     bool is_open = TreeNodeExV(ptr_id, 0, fmt, args);
6155     va_end(args);
6156     return is_open;
6157 }
6158 
TreeNode(const char * label)6159 bool ImGui::TreeNode(const char* label)
6160 {
6161     ImGuiWindow* window = GetCurrentWindow();
6162     if (window->SkipItems)
6163         return false;
6164     return TreeNodeBehavior(window->GetID(label), 0, label, NULL);
6165 }
6166 
TreeAdvanceToLabelPos()6167 void ImGui::TreeAdvanceToLabelPos()
6168 {
6169     ImGuiContext& g = *GImGui;
6170     g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing();
6171 }
6172 
6173 // Horizontal distance preceding label when using TreeNode() or Bullet()
GetTreeNodeToLabelSpacing()6174 float ImGui::GetTreeNodeToLabelSpacing()
6175 {
6176     ImGuiContext& g = *GImGui;
6177     return g.FontSize + (g.Style.FramePadding.x * 2.0f);
6178 }
6179 
SetNextTreeNodeOpen(bool is_open,ImGuiSetCond cond)6180 void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiSetCond cond)
6181 {
6182     ImGuiContext& g = *GImGui;
6183     g.SetNextTreeNodeOpenVal = is_open;
6184     g.SetNextTreeNodeOpenCond = cond ? cond : ImGuiSetCond_Always;
6185 }
6186 
PushID(const char * str_id)6187 void ImGui::PushID(const char* str_id)
6188 {
6189     ImGuiWindow* window = GetCurrentWindow();
6190     window->IDStack.push_back(window->GetID(str_id));
6191 }
6192 
PushID(const char * str_id_begin,const char * str_id_end)6193 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6194 {
6195     ImGuiWindow* window = GetCurrentWindow();
6196     window->IDStack.push_back(window->GetID(str_id_begin, str_id_end));
6197 }
6198 
PushID(const void * ptr_id)6199 void ImGui::PushID(const void* ptr_id)
6200 {
6201     ImGuiWindow* window = GetCurrentWindow();
6202     window->IDStack.push_back(window->GetID(ptr_id));
6203 }
6204 
PushID(int int_id)6205 void ImGui::PushID(int int_id)
6206 {
6207     const void* ptr_id = (void*)(intptr_t)int_id;
6208     ImGuiWindow* window = GetCurrentWindow();
6209     window->IDStack.push_back(window->GetID(ptr_id));
6210 }
6211 
PopID()6212 void ImGui::PopID()
6213 {
6214     ImGuiWindow* window = GetCurrentWindow();
6215     window->IDStack.pop_back();
6216 }
6217 
GetID(const char * str_id)6218 ImGuiID ImGui::GetID(const char* str_id)
6219 {
6220     return GImGui->CurrentWindow->GetID(str_id);
6221 }
6222 
GetID(const char * str_id_begin,const char * str_id_end)6223 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6224 {
6225     return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end);
6226 }
6227 
GetID(const void * ptr_id)6228 ImGuiID ImGui::GetID(const void* ptr_id)
6229 {
6230     return GImGui->CurrentWindow->GetID(ptr_id);
6231 }
6232 
Bullet()6233 void ImGui::Bullet()
6234 {
6235     ImGuiWindow* window = GetCurrentWindow();
6236     if (window->SkipItems)
6237         return;
6238 
6239     ImGuiContext& g = *GImGui;
6240     const ImGuiStyle& style = g.Style;
6241     const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
6242     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height));
6243     ItemSize(bb);
6244     if (!ItemAdd(bb, NULL))
6245     {
6246         SameLine(0, style.FramePadding.x*2);
6247         return;
6248     }
6249 
6250     // Render and stay on same line
6251     RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
6252     SameLine(0, style.FramePadding.x*2);
6253 }
6254 
6255 // Text with a little bullet aligned to the typical tree node.
BulletTextV(const char * fmt,va_list args)6256 void ImGui::BulletTextV(const char* fmt, va_list args)
6257 {
6258     ImGuiWindow* window = GetCurrentWindow();
6259     if (window->SkipItems)
6260         return;
6261 
6262     ImGuiContext& g = *GImGui;
6263     const ImGuiStyle& style = g.Style;
6264 
6265     const char* text_begin = g.TempBuffer;
6266     const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
6267     const ImVec2 label_size = CalcTextSize(text_begin, text_end, false);
6268     const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
6269     const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
6270     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x*2) : 0.0f), ImMax(line_height, label_size.y)));  // Empty text doesn't add padding
6271     ItemSize(bb);
6272     if (!ItemAdd(bb, NULL))
6273         return;
6274 
6275     // Render
6276     RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
6277     RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end, false);
6278 }
6279 
BulletText(const char * fmt,...)6280 void ImGui::BulletText(const char* fmt, ...)
6281 {
6282     va_list args;
6283     va_start(args, fmt);
6284     BulletTextV(fmt, args);
6285     va_end(args);
6286 }
6287 
DataTypeFormatString(ImGuiDataType data_type,void * data_ptr,const char * display_format,char * buf,int buf_size)6288 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size)
6289 {
6290     if (data_type == ImGuiDataType_Int)
6291         ImFormatString(buf, buf_size, display_format, *(int*)data_ptr);
6292     else if (data_type == ImGuiDataType_Float)
6293         ImFormatString(buf, buf_size, display_format, *(float*)data_ptr);
6294 }
6295 
DataTypeFormatString(ImGuiDataType data_type,void * data_ptr,int decimal_precision,char * buf,int buf_size)6296 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size)
6297 {
6298     if (data_type == ImGuiDataType_Int)
6299     {
6300         if (decimal_precision < 0)
6301             ImFormatString(buf, buf_size, "%d", *(int*)data_ptr);
6302         else
6303             ImFormatString(buf, buf_size, "%.*d", decimal_precision, *(int*)data_ptr);
6304     }
6305     else if (data_type == ImGuiDataType_Float)
6306     {
6307         if (decimal_precision < 0)
6308             ImFormatString(buf, buf_size, "%f", *(float*)data_ptr);     // Ideally we'd have a minimum decimal precision of 1 to visually denote that it is a float, while hiding non-significant digits?
6309         else
6310             ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(float*)data_ptr);
6311     }
6312 }
6313 
DataTypeApplyOp(ImGuiDataType data_type,int op,void * value1,const void * value2)6314 static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2)// Store into value1
6315 {
6316     if (data_type == ImGuiDataType_Int)
6317     {
6318         if (op == '+')
6319             *(int*)value1 = *(int*)value1 + *(const int*)value2;
6320         else if (op == '-')
6321             *(int*)value1 = *(int*)value1 - *(const int*)value2;
6322     }
6323     else if (data_type == ImGuiDataType_Float)
6324     {
6325         if (op == '+')
6326             *(float*)value1 = *(float*)value1 + *(const float*)value2;
6327         else if (op == '-')
6328             *(float*)value1 = *(float*)value1 - *(const float*)value2;
6329     }
6330 }
6331 
6332 // User can input math operators (e.g. +100) to edit a numerical values.
DataTypeApplyOpFromText(const char * buf,const char * initial_value_buf,ImGuiDataType data_type,void * data_ptr,const char * scalar_format)6333 static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format)
6334 {
6335     while (ImCharIsSpace(*buf))
6336         buf++;
6337 
6338     // We don't support '-' op because it would conflict with inputing negative value.
6339     // Instead you can use +-100 to subtract from an existing value
6340     char op = buf[0];
6341     if (op == '+' || op == '*' || op == '/')
6342     {
6343         buf++;
6344         while (ImCharIsSpace(*buf))
6345             buf++;
6346     }
6347     else
6348     {
6349         op = 0;
6350     }
6351     if (!buf[0])
6352         return false;
6353 
6354     if (data_type == ImGuiDataType_Int)
6355     {
6356         if (!scalar_format)
6357             scalar_format = "%d";
6358         int* v = (int*)data_ptr;
6359         const int old_v = *v;
6360         int arg0 = *v;
6361         if (op && sscanf(initial_value_buf, scalar_format, &arg0) < 1)
6362             return false;
6363 
6364         // Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision
6365         float arg1 = 0.0f;
6366         if (op == '+')      { if (sscanf(buf, "%f", &arg1) == 1) *v = (int)(arg0 + arg1); }                // Add (use "+-" to subtract)
6367         else if (op == '*') { if (sscanf(buf, "%f", &arg1) == 1) *v = (int)(arg0 * arg1); }                // Multiply
6368         else if (op == '/') { if (sscanf(buf, "%f", &arg1) == 1 && arg1 != 0.0f) *v = (int)(arg0 / arg1); }// Divide
6369         else                { if (sscanf(buf, scalar_format, &arg0) == 1) *v = arg0; }                     // Assign constant
6370         return (old_v != *v);
6371     }
6372     else if (data_type == ImGuiDataType_Float)
6373     {
6374         // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in
6375         scalar_format = "%f";
6376         float* v = (float*)data_ptr;
6377         const float old_v = *v;
6378         float arg0 = *v;
6379         if (op && sscanf(initial_value_buf, scalar_format, &arg0) < 1)
6380             return false;
6381 
6382         float arg1 = 0.0f;
6383         if (sscanf(buf, scalar_format, &arg1) < 1)
6384             return false;
6385         if (op == '+')      { *v = arg0 + arg1; }                    // Add (use "+-" to subtract)
6386         else if (op == '*') { *v = arg0 * arg1; }                    // Multiply
6387         else if (op == '/') { if (arg1 != 0.0f) *v = arg0 / arg1; }  // Divide
6388         else                { *v = arg1; }                           // Assign constant
6389         return (old_v != *v);
6390     }
6391 
6392     return false;
6393 }
6394 
6395 // Create text input in place of a slider (when CTRL+Clicking on slider)
InputScalarAsWidgetReplacement(const ImRect & aabb,const char * label,ImGuiDataType data_type,void * data_ptr,ImGuiID id,int decimal_precision)6396 bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision)
6397 {
6398     ImGuiContext& g = *GImGui;
6399     ImGuiWindow* window = GetCurrentWindow();
6400 
6401     // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen)
6402     SetActiveID(g.ScalarAsInputTextId, window);
6403     SetHoveredID(0);
6404     FocusableItemUnregister(window);
6405 
6406     char buf[32];
6407     DataTypeFormatString(data_type, data_ptr, decimal_precision, buf, IM_ARRAYSIZE(buf));
6408     bool text_value_changed = InputTextEx(label, buf, IM_ARRAYSIZE(buf), aabb.GetSize(), ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll);
6409     if (g.ScalarAsInputTextId == 0)
6410     {
6411         // First frame
6412         IM_ASSERT(g.ActiveId == id);    // InputText ID expected to match the Slider ID (else we'd need to store them both, which is also possible)
6413         g.ScalarAsInputTextId = g.ActiveId;
6414         SetHoveredID(id);
6415     }
6416     else if (g.ActiveId != g.ScalarAsInputTextId)
6417     {
6418         // Release
6419         g.ScalarAsInputTextId = 0;
6420     }
6421     if (text_value_changed)
6422         return DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, NULL);
6423     return false;
6424 }
6425 
6426 // Parse display precision back from the display format string
ParseFormatPrecision(const char * fmt,int default_precision)6427 int ImGui::ParseFormatPrecision(const char* fmt, int default_precision)
6428 {
6429     int precision = default_precision;
6430     while ((fmt = strchr(fmt, '%')) != NULL)
6431     {
6432         fmt++;
6433         if (fmt[0] == '%') { fmt++; continue; } // Ignore "%%"
6434         while (*fmt >= '0' && *fmt <= '9')
6435             fmt++;
6436         if (*fmt == '.')
6437         {
6438             precision = atoi(fmt + 1);
6439             if (precision < 0 || precision > 10)
6440                 precision = default_precision;
6441         }
6442         break;
6443     }
6444     return precision;
6445 }
6446 
RoundScalar(float value,int decimal_precision)6447 float ImGui::RoundScalar(float value, int decimal_precision)
6448 {
6449     // Round past decimal precision
6450     // So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0
6451     // FIXME: Investigate better rounding methods
6452     static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f };
6453     float min_step = (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision);
6454     bool negative = value < 0.0f;
6455     value = fabsf(value);
6456     float remainder = fmodf(value, min_step);
6457     if (remainder <= min_step*0.5f)
6458         value -= remainder;
6459     else
6460         value += (min_step - remainder);
6461     return negative ? -value : value;
6462 }
6463 
SliderBehaviorCalcRatioFromValue(float v,float v_min,float v_max,float power,float linear_zero_pos)6464 static inline float SliderBehaviorCalcRatioFromValue(float v, float v_min, float v_max, float power, float linear_zero_pos)
6465 {
6466     if (v_min == v_max)
6467         return 0.0f;
6468 
6469     const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f);
6470     const float v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min);
6471     if (is_non_linear)
6472     {
6473         if (v_clamped < 0.0f)
6474         {
6475             const float f = 1.0f - (v_clamped - v_min) / (ImMin(0.0f,v_max) - v_min);
6476             return (1.0f - powf(f, 1.0f/power)) * linear_zero_pos;
6477         }
6478         else
6479         {
6480             const float f = (v_clamped - ImMax(0.0f,v_min)) / (v_max - ImMax(0.0f,v_min));
6481             return linear_zero_pos + powf(f, 1.0f/power) * (1.0f - linear_zero_pos);
6482         }
6483     }
6484 
6485     // Linear slider
6486     return (v_clamped - v_min) / (v_max - v_min);
6487 }
6488 
SliderBehavior(const ImRect & frame_bb,ImGuiID id,float * v,float v_min,float v_max,float power,int decimal_precision,ImGuiSliderFlags flags)6489 bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags)
6490 {
6491     ImGuiContext& g = *GImGui;
6492     ImGuiWindow* window = GetCurrentWindow();
6493     const ImGuiStyle& style = g.Style;
6494 
6495     // Draw frame
6496     RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
6497 
6498     const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f);
6499     const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0;
6500 
6501     const float grab_padding = 2.0f;
6502     const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f);
6503     float grab_sz;
6504     if (decimal_precision > 0)
6505         grab_sz = ImMin(style.GrabMinSize, slider_sz);
6506     else
6507         grab_sz = ImMin(ImMax(1.0f * (slider_sz / ((v_min < v_max ? v_max - v_min : v_min - v_max) + 1.0f)), style.GrabMinSize), slider_sz);  // Integer sliders, if possible have the grab size represent 1 unit
6508     const float slider_usable_sz = slider_sz - grab_sz;
6509     const float slider_usable_pos_min = (is_horizontal ? frame_bb.Min.x : frame_bb.Min.y) + grab_padding + grab_sz*0.5f;
6510     const float slider_usable_pos_max = (is_horizontal ? frame_bb.Max.x : frame_bb.Max.y) - grab_padding - grab_sz*0.5f;
6511 
6512     // For logarithmic sliders that cross over sign boundary we want the exponential increase to be symmetric around 0.0f
6513     float linear_zero_pos = 0.0f;   // 0.0->1.0f
6514     if (v_min * v_max < 0.0f)
6515     {
6516         // Different sign
6517         const float linear_dist_min_to_0 = powf(fabsf(0.0f - v_min), 1.0f/power);
6518         const float linear_dist_max_to_0 = powf(fabsf(v_max - 0.0f), 1.0f/power);
6519         linear_zero_pos = linear_dist_min_to_0 / (linear_dist_min_to_0+linear_dist_max_to_0);
6520     }
6521     else
6522     {
6523         // Same sign
6524         linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f;
6525     }
6526 
6527     // Process clicking on the slider
6528     bool value_changed = false;
6529     if (g.ActiveId == id)
6530     {
6531         if (g.IO.MouseDown[0])
6532         {
6533             const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
6534             float clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f;
6535             if (!is_horizontal)
6536                 clicked_t = 1.0f - clicked_t;
6537 
6538             float new_value;
6539             if (is_non_linear)
6540             {
6541                 // Account for logarithmic scale on both sides of the zero
6542                 if (clicked_t < linear_zero_pos)
6543                 {
6544                     // Negative: rescale to the negative range before powering
6545                     float a = 1.0f - (clicked_t / linear_zero_pos);
6546                     a = powf(a, power);
6547                     new_value = ImLerp(ImMin(v_max,0.0f), v_min, a);
6548                 }
6549                 else
6550                 {
6551                     // Positive: rescale to the positive range before powering
6552                     float a;
6553                     if (fabsf(linear_zero_pos - 1.0f) > 1.e-6f)
6554                         a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos);
6555                     else
6556                         a = clicked_t;
6557                     a = powf(a, power);
6558                     new_value = ImLerp(ImMax(v_min,0.0f), v_max, a);
6559                 }
6560             }
6561             else
6562             {
6563                 // Linear slider
6564                 new_value = ImLerp(v_min, v_max, clicked_t);
6565             }
6566 
6567             // Round past decimal precision
6568             new_value = RoundScalar(new_value, decimal_precision);
6569             if (*v != new_value)
6570             {
6571                 *v = new_value;
6572                 value_changed = true;
6573             }
6574         }
6575         else
6576         {
6577             ClearActiveID();
6578         }
6579     }
6580 
6581     // Calculate slider grab positioning
6582     float grab_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos);
6583 
6584     // Draw
6585     if (!is_horizontal)
6586         grab_t = 1.0f - grab_t;
6587     const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
6588     ImRect grab_bb;
6589     if (is_horizontal)
6590         grab_bb = ImRect(ImVec2(grab_pos - grab_sz*0.5f, frame_bb.Min.y + grab_padding), ImVec2(grab_pos + grab_sz*0.5f, frame_bb.Max.y - grab_padding));
6591     else
6592         grab_bb = ImRect(ImVec2(frame_bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f), ImVec2(frame_bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f));
6593     window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
6594 
6595     return value_changed;
6596 }
6597 
6598 // Use power!=1.0 for logarithmic sliders.
6599 // Adjust display_format to decorate the value with a prefix or a suffix.
6600 //   "%.3f"         1.234
6601 //   "%5.2f secs"   01.23 secs
6602 //   "Gold: %.0f"   Gold: 1
SliderFloat(const char * label,float * v,float v_min,float v_max,const char * display_format,float power)6603 bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format, float power)
6604 {
6605     ImGuiWindow* window = GetCurrentWindow();
6606     if (window->SkipItems)
6607         return false;
6608 
6609     ImGuiContext& g = *GImGui;
6610     const ImGuiStyle& style = g.Style;
6611     const ImGuiID id = window->GetID(label);
6612     const float w = CalcItemWidth();
6613 
6614     const ImVec2 label_size = CalcTextSize(label, NULL, true);
6615     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
6616     const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
6617 
6618     // NB- we don't call ItemSize() yet because we may turn into a text edit box below
6619     if (!ItemAdd(total_bb, &id))
6620     {
6621         ItemSize(total_bb, style.FramePadding.y);
6622         return false;
6623     }
6624 
6625     const bool hovered = IsHovered(frame_bb, id);
6626     if (hovered)
6627         SetHoveredID(id);
6628 
6629     if (!display_format)
6630         display_format = "%.3f";
6631     int decimal_precision = ParseFormatPrecision(display_format, 3);
6632 
6633     // Tabbing or CTRL-clicking on Slider turns it into an input box
6634     bool start_text_input = false;
6635     const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id);
6636     if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]))
6637     {
6638         SetActiveID(id, window);
6639         FocusWindow(window);
6640 
6641         if (tab_focus_requested || g.IO.KeyCtrl)
6642         {
6643             start_text_input = true;
6644             g.ScalarAsInputTextId = 0;
6645         }
6646     }
6647     if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
6648         return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
6649 
6650     ItemSize(total_bb, style.FramePadding.y);
6651 
6652     // Actual slider behavior + render grab
6653     const bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision);
6654 
6655     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
6656     char value_buf[64];
6657     const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
6658     RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
6659 
6660     if (label_size.x > 0.0f)
6661         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
6662 
6663     return value_changed;
6664 }
6665 
VSliderFloat(const char * label,const ImVec2 & size,float * v,float v_min,float v_max,const char * display_format,float power)6666 bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format, float power)
6667 {
6668     ImGuiWindow* window = GetCurrentWindow();
6669     if (window->SkipItems)
6670         return false;
6671 
6672     ImGuiContext& g = *GImGui;
6673     const ImGuiStyle& style = g.Style;
6674     const ImGuiID id = window->GetID(label);
6675 
6676     const ImVec2 label_size = CalcTextSize(label, NULL, true);
6677     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
6678     const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
6679 
6680     ItemSize(bb, style.FramePadding.y);
6681     if (!ItemAdd(frame_bb, &id))
6682         return false;
6683 
6684     const bool hovered = IsHovered(frame_bb, id);
6685     if (hovered)
6686         SetHoveredID(id);
6687 
6688     if (!display_format)
6689         display_format = "%.3f";
6690     int decimal_precision = ParseFormatPrecision(display_format, 3);
6691 
6692     if (hovered && g.IO.MouseClicked[0])
6693     {
6694         SetActiveID(id, window);
6695         FocusWindow(window);
6696     }
6697 
6698     // Actual slider behavior + render grab
6699     bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision, ImGuiSliderFlags_Vertical);
6700 
6701     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
6702     // For the vertical slider we allow centered text to overlap the frame padding
6703     char value_buf[64];
6704     char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
6705     RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.0f));
6706     if (label_size.x > 0.0f)
6707         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
6708 
6709     return value_changed;
6710 }
6711 
SliderAngle(const char * label,float * v_rad,float v_degrees_min,float v_degrees_max)6712 bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max)
6713 {
6714     float v_deg = (*v_rad) * 360.0f / (2*IM_PI);
6715     bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f);
6716     *v_rad = v_deg * (2*IM_PI) / 360.0f;
6717     return value_changed;
6718 }
6719 
SliderInt(const char * label,int * v,int v_min,int v_max,const char * display_format)6720 bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format)
6721 {
6722     if (!display_format)
6723         display_format = "%.0f";
6724     float v_f = (float)*v;
6725     bool value_changed = SliderFloat(label, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
6726     *v = (int)v_f;
6727     return value_changed;
6728 }
6729 
VSliderInt(const char * label,const ImVec2 & size,int * v,int v_min,int v_max,const char * display_format)6730 bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format)
6731 {
6732     if (!display_format)
6733         display_format = "%.0f";
6734     float v_f = (float)*v;
6735     bool value_changed = VSliderFloat(label, size, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
6736     *v = (int)v_f;
6737     return value_changed;
6738 }
6739 
6740 // Add multiple sliders on 1 line for compact edition of multiple components
SliderFloatN(const char * label,float * v,int components,float v_min,float v_max,const char * display_format,float power)6741 bool ImGui::SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power)
6742 {
6743     ImGuiWindow* window = GetCurrentWindow();
6744     if (window->SkipItems)
6745         return false;
6746 
6747     ImGuiContext& g = *GImGui;
6748     bool value_changed = false;
6749     BeginGroup();
6750     PushID(label);
6751     PushMultiItemsWidths(components);
6752     for (int i = 0; i < components; i++)
6753     {
6754         PushID(i);
6755         value_changed |= SliderFloat("##v", &v[i], v_min, v_max, display_format, power);
6756         SameLine(0, g.Style.ItemInnerSpacing.x);
6757         PopID();
6758         PopItemWidth();
6759     }
6760     PopID();
6761 
6762     TextUnformatted(label, FindRenderedTextEnd(label));
6763     EndGroup();
6764 
6765     return value_changed;
6766 }
6767 
SliderFloat2(const char * label,float v[2],float v_min,float v_max,const char * display_format,float power)6768 bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format, float power)
6769 {
6770     return SliderFloatN(label, v, 2, v_min, v_max, display_format, power);
6771 }
6772 
SliderFloat3(const char * label,float v[3],float v_min,float v_max,const char * display_format,float power)6773 bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format, float power)
6774 {
6775     return SliderFloatN(label, v, 3, v_min, v_max, display_format, power);
6776 }
6777 
SliderFloat4(const char * label,float v[4],float v_min,float v_max,const char * display_format,float power)6778 bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format, float power)
6779 {
6780     return SliderFloatN(label, v, 4, v_min, v_max, display_format, power);
6781 }
6782 
SliderIntN(const char * label,int * v,int components,int v_min,int v_max,const char * display_format)6783 bool ImGui::SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format)
6784 {
6785     ImGuiWindow* window = GetCurrentWindow();
6786     if (window->SkipItems)
6787         return false;
6788 
6789     ImGuiContext& g = *GImGui;
6790     bool value_changed = false;
6791     BeginGroup();
6792     PushID(label);
6793     PushMultiItemsWidths(components);
6794     for (int i = 0; i < components; i++)
6795     {
6796         PushID(i);
6797         value_changed |= SliderInt("##v", &v[i], v_min, v_max, display_format);
6798         SameLine(0, g.Style.ItemInnerSpacing.x);
6799         PopID();
6800         PopItemWidth();
6801     }
6802     PopID();
6803 
6804     TextUnformatted(label, FindRenderedTextEnd(label));
6805     EndGroup();
6806 
6807     return value_changed;
6808 }
6809 
SliderInt2(const char * label,int v[2],int v_min,int v_max,const char * display_format)6810 bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format)
6811 {
6812     return SliderIntN(label, v, 2, v_min, v_max, display_format);
6813 }
6814 
SliderInt3(const char * label,int v[3],int v_min,int v_max,const char * display_format)6815 bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format)
6816 {
6817     return SliderIntN(label, v, 3, v_min, v_max, display_format);
6818 }
6819 
SliderInt4(const char * label,int v[4],int v_min,int v_max,const char * display_format)6820 bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format)
6821 {
6822     return SliderIntN(label, v, 4, v_min, v_max, display_format);
6823 }
6824 
DragBehavior(const ImRect & frame_bb,ImGuiID id,float * v,float v_speed,float v_min,float v_max,int decimal_precision,float power)6825 bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power)
6826 {
6827     ImGuiContext& g = *GImGui;
6828     const ImGuiStyle& style = g.Style;
6829 
6830     // Draw frame
6831     const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
6832     RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
6833 
6834     bool value_changed = false;
6835 
6836     // Process clicking on the drag
6837     if (g.ActiveId == id)
6838     {
6839         if (g.IO.MouseDown[0])
6840         {
6841             if (g.ActiveIdIsJustActivated)
6842             {
6843                 // Lock current value on click
6844                 g.DragCurrentValue = *v;
6845                 g.DragLastMouseDelta = ImVec2(0.f, 0.f);
6846             }
6847 
6848             float v_cur = g.DragCurrentValue;
6849             const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f);
6850             if (fabsf(mouse_drag_delta.x - g.DragLastMouseDelta.x) > 0.0f)
6851             {
6852                 float speed = v_speed;
6853                 if (speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX)
6854                     speed = (v_max - v_min) * g.DragSpeedDefaultRatio;
6855                 if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f)
6856                     speed = speed * g.DragSpeedScaleFast;
6857                 if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f)
6858                     speed = speed * g.DragSpeedScaleSlow;
6859 
6860                 float delta = (mouse_drag_delta.x - g.DragLastMouseDelta.x) * speed;
6861                 if (fabsf(power - 1.0f) > 0.001f)
6862                 {
6863                     // Logarithmic curve on both side of 0.0
6864                     float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur;
6865                     float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f;
6866                     float v1 = powf(v0_abs, 1.0f / power) + (delta * v0_sign);
6867                     float v1_abs = v1 >= 0.0f ? v1 : -v1;
6868                     float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f;          // Crossed sign line
6869                     v_cur = powf(v1_abs, power) * v0_sign * v1_sign;    // Reapply sign
6870                 }
6871                 else
6872                 {
6873                     v_cur += delta;
6874                 }
6875                 g.DragLastMouseDelta.x = mouse_drag_delta.x;
6876 
6877                 // Clamp
6878                 if (v_min < v_max)
6879                     v_cur = ImClamp(v_cur, v_min, v_max);
6880                 g.DragCurrentValue = v_cur;
6881             }
6882 
6883             // Round to user desired precision, then apply
6884             v_cur = RoundScalar(v_cur, decimal_precision);
6885             if (*v != v_cur)
6886             {
6887                 *v = v_cur;
6888                 value_changed = true;
6889             }
6890         }
6891         else
6892         {
6893             ClearActiveID();
6894         }
6895     }
6896 
6897     return value_changed;
6898 }
6899 
DragFloat(const char * label,float * v,float v_speed,float v_min,float v_max,const char * display_format,float power)6900 bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* display_format, float power)
6901 {
6902     ImGuiWindow* window = GetCurrentWindow();
6903     if (window->SkipItems)
6904         return false;
6905 
6906     ImGuiContext& g = *GImGui;
6907     const ImGuiStyle& style = g.Style;
6908     const ImGuiID id = window->GetID(label);
6909     const float w = CalcItemWidth();
6910 
6911     const ImVec2 label_size = CalcTextSize(label, NULL, true);
6912     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
6913     const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
6914     const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
6915 
6916     // NB- we don't call ItemSize() yet because we may turn into a text edit box below
6917     if (!ItemAdd(total_bb, &id))
6918     {
6919         ItemSize(total_bb, style.FramePadding.y);
6920         return false;
6921     }
6922 
6923     const bool hovered = IsHovered(frame_bb, id);
6924     if (hovered)
6925         SetHoveredID(id);
6926 
6927     if (!display_format)
6928         display_format = "%.3f";
6929     int decimal_precision = ParseFormatPrecision(display_format, 3);
6930 
6931     // Tabbing or CTRL-clicking on Drag turns it into an input box
6932     bool start_text_input = false;
6933     const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id);
6934     if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] | g.IO.MouseDoubleClicked[0])))
6935     {
6936         SetActiveID(id, window);
6937         FocusWindow(window);
6938 
6939         if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0])
6940         {
6941             start_text_input = true;
6942             g.ScalarAsInputTextId = 0;
6943         }
6944     }
6945     if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
6946         return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
6947 
6948     // Actual drag behavior
6949     ItemSize(total_bb, style.FramePadding.y);
6950     const bool value_changed = DragBehavior(frame_bb, id, v, v_speed, v_min, v_max, decimal_precision, power);
6951 
6952     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
6953     char value_buf[64];
6954     const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
6955     RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
6956 
6957     if (label_size.x > 0.0f)
6958         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
6959 
6960     return value_changed;
6961 }
6962 
DragFloatN(const char * label,float * v,int components,float v_speed,float v_min,float v_max,const char * display_format,float power)6963 bool ImGui::DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power)
6964 {
6965     ImGuiWindow* window = GetCurrentWindow();
6966     if (window->SkipItems)
6967         return false;
6968 
6969     ImGuiContext& g = *GImGui;
6970     bool value_changed = false;
6971     BeginGroup();
6972     PushID(label);
6973     PushMultiItemsWidths(components);
6974     for (int i = 0; i < components; i++)
6975     {
6976         PushID(i);
6977         value_changed |= DragFloat("##v", &v[i], v_speed, v_min, v_max, display_format, power);
6978         SameLine(0, g.Style.ItemInnerSpacing.x);
6979         PopID();
6980         PopItemWidth();
6981     }
6982     PopID();
6983 
6984     TextUnformatted(label, FindRenderedTextEnd(label));
6985     EndGroup();
6986 
6987     return value_changed;
6988 }
6989 
DragFloat2(const char * label,float v[2],float v_speed,float v_min,float v_max,const char * display_format,float power)6990 bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power)
6991 {
6992     return DragFloatN(label, v, 2, v_speed, v_min, v_max, display_format, power);
6993 }
6994 
DragFloat3(const char * label,float v[3],float v_speed,float v_min,float v_max,const char * display_format,float power)6995 bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* display_format, float power)
6996 {
6997     return DragFloatN(label, v, 3, v_speed, v_min, v_max, display_format, power);
6998 }
6999 
DragFloat4(const char * label,float v[4],float v_speed,float v_min,float v_max,const char * display_format,float power)7000 bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* display_format, float power)
7001 {
7002     return DragFloatN(label, v, 4, v_speed, v_min, v_max, display_format, power);
7003 }
7004 
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)7005 bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* display_format, const char* display_format_max, float power)
7006 {
7007     ImGuiWindow* window = GetCurrentWindow();
7008     if (window->SkipItems)
7009         return false;
7010 
7011     ImGuiContext& g = *GImGui;
7012     PushID(label);
7013     BeginGroup();
7014     PushMultiItemsWidths(2);
7015 
7016     bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format, power);
7017     PopItemWidth();
7018     SameLine(0, g.Style.ItemInnerSpacing.x);
7019     value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, display_format_max ? display_format_max : display_format, power);
7020     PopItemWidth();
7021     SameLine(0, g.Style.ItemInnerSpacing.x);
7022 
7023     TextUnformatted(label, FindRenderedTextEnd(label));
7024     EndGroup();
7025     PopID();
7026 
7027     return value_changed;
7028 }
7029 
7030 // NB: v_speed is float to allow adjusting the drag speed with more precision
DragInt(const char * label,int * v,float v_speed,int v_min,int v_max,const char * display_format)7031 bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* display_format)
7032 {
7033     if (!display_format)
7034         display_format = "%.0f";
7035     float v_f = (float)*v;
7036     bool value_changed = DragFloat(label, &v_f, v_speed, (float)v_min, (float)v_max, display_format);
7037     *v = (int)v_f;
7038     return value_changed;
7039 }
7040 
DragIntN(const char * label,int * v,int components,float v_speed,int v_min,int v_max,const char * display_format)7041 bool ImGui::DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format)
7042 {
7043     ImGuiWindow* window = GetCurrentWindow();
7044     if (window->SkipItems)
7045         return false;
7046 
7047     ImGuiContext& g = *GImGui;
7048     bool value_changed = false;
7049     BeginGroup();
7050     PushID(label);
7051     PushMultiItemsWidths(components);
7052     for (int i = 0; i < components; i++)
7053     {
7054         PushID(i);
7055         value_changed |= DragInt("##v", &v[i], v_speed, v_min, v_max, display_format);
7056         SameLine(0, g.Style.ItemInnerSpacing.x);
7057         PopID();
7058         PopItemWidth();
7059     }
7060     PopID();
7061 
7062     TextUnformatted(label, FindRenderedTextEnd(label));
7063     EndGroup();
7064 
7065     return value_changed;
7066 }
7067 
DragInt2(const char * label,int v[2],float v_speed,int v_min,int v_max,const char * display_format)7068 bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* display_format)
7069 {
7070     return DragIntN(label, v, 2, v_speed, v_min, v_max, display_format);
7071 }
7072 
DragInt3(const char * label,int v[3],float v_speed,int v_min,int v_max,const char * display_format)7073 bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* display_format)
7074 {
7075     return DragIntN(label, v, 3, v_speed, v_min, v_max, display_format);
7076 }
7077 
DragInt4(const char * label,int v[4],float v_speed,int v_min,int v_max,const char * display_format)7078 bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* display_format)
7079 {
7080     return DragIntN(label, v, 4, v_speed, v_min, v_max, display_format);
7081 }
7082 
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)7083 bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* display_format, const char* display_format_max)
7084 {
7085     ImGuiWindow* window = GetCurrentWindow();
7086     if (window->SkipItems)
7087         return false;
7088 
7089     ImGuiContext& g = *GImGui;
7090     PushID(label);
7091     BeginGroup();
7092     PushMultiItemsWidths(2);
7093 
7094     bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format);
7095     PopItemWidth();
7096     SameLine(0, g.Style.ItemInnerSpacing.x);
7097     value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, display_format_max ? display_format_max : display_format);
7098     PopItemWidth();
7099     SameLine(0, g.Style.ItemInnerSpacing.x);
7100 
7101     TextUnformatted(label, FindRenderedTextEnd(label));
7102     EndGroup();
7103     PopID();
7104 
7105     return value_changed;
7106 }
7107 
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)7108 void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
7109 {
7110     ImGuiWindow* window = GetCurrentWindow();
7111     if (window->SkipItems)
7112         return;
7113 
7114     ImGuiContext& g = *GImGui;
7115     const ImGuiStyle& style = g.Style;
7116 
7117     const ImVec2 label_size = CalcTextSize(label, NULL, true);
7118     if (graph_size.x == 0.0f)
7119         graph_size.x = CalcItemWidth();
7120     if (graph_size.y == 0.0f)
7121         graph_size.y = label_size.y + (style.FramePadding.y * 2);
7122 
7123     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y));
7124     const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
7125     const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0));
7126     ItemSize(total_bb, style.FramePadding.y);
7127     if (!ItemAdd(total_bb, NULL))
7128         return;
7129 
7130     // Determine scale from values if not specified
7131     if (scale_min == FLT_MAX || scale_max == FLT_MAX)
7132     {
7133         float v_min = FLT_MAX;
7134         float v_max = -FLT_MAX;
7135         for (int i = 0; i < values_count; i++)
7136         {
7137             const float v = values_getter(data, i);
7138             v_min = ImMin(v_min, v);
7139             v_max = ImMax(v_max, v);
7140         }
7141         if (scale_min == FLT_MAX)
7142             scale_min = v_min;
7143         if (scale_max == FLT_MAX)
7144             scale_max = v_max;
7145     }
7146 
7147     RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
7148 
7149     if (values_count > 0)
7150     {
7151         int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
7152         int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
7153 
7154         // Tooltip on hover
7155         int v_hovered = -1;
7156         if (IsHovered(inner_bb, 0))
7157         {
7158             const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);
7159             const int v_idx = (int)(t * item_count);
7160             IM_ASSERT(v_idx >= 0 && v_idx < values_count);
7161 
7162             const float v0 = values_getter(data, (v_idx + values_offset) % values_count);
7163             const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count);
7164             if (plot_type == ImGuiPlotType_Lines)
7165                 SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1);
7166             else if (plot_type == ImGuiPlotType_Histogram)
7167                 SetTooltip("%d: %8.4g", v_idx, v0);
7168             v_hovered = v_idx;
7169         }
7170 
7171         const float t_step = 1.0f / (float)res_w;
7172 
7173         float v0 = values_getter(data, (0 + values_offset) % values_count);
7174         float t0 = 0.0f;
7175         ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) / (scale_max - scale_min)) );    // Point in the normalized space of our target rectangle
7176 
7177         const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram);
7178         const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered);
7179 
7180         for (int n = 0; n < res_w; n++)
7181         {
7182             const float t1 = t0 + t_step;
7183             const int v1_idx = (int)(t0 * item_count + 0.5f);
7184             IM_ASSERT(v1_idx >= 0 && v1_idx < values_count);
7185             const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count);
7186             const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) / (scale_max - scale_min)) );
7187 
7188             // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU.
7189             ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0);
7190             ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, 1.0f));
7191             if (plot_type == ImGuiPlotType_Lines)
7192             {
7193                 window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
7194             }
7195             else if (plot_type == ImGuiPlotType_Histogram)
7196             {
7197                 if (pos1.x >= pos0.x + 2.0f)
7198                     pos1.x -= 1.0f;
7199                 window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
7200             }
7201 
7202             t0 = t1;
7203             tp0 = tp1;
7204         }
7205     }
7206 
7207     // Text overlay
7208     if (overlay_text)
7209         RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f,0.0f));
7210 
7211     if (label_size.x > 0.0f)
7212         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
7213 }
7214 
7215 struct ImGuiPlotArrayGetterData
7216 {
7217     const float* Values;
7218     int Stride;
7219 
ImGuiPlotArrayGetterDataImGuiPlotArrayGetterData7220     ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; }
7221 };
7222 
Plot_ArrayGetter(void * data,int idx)7223 static float Plot_ArrayGetter(void* data, int idx)
7224 {
7225     ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data;
7226     const float v = *(float*)(void*)((unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);
7227     return v;
7228 }
7229 
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)7230 void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)
7231 {
7232     ImGuiPlotArrayGetterData data(values, stride);
7233     PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
7234 }
7235 
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)7236 void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
7237 {
7238     PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
7239 }
7240 
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)7241 void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)
7242 {
7243     ImGuiPlotArrayGetterData data(values, stride);
7244     PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
7245 }
7246 
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)7247 void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
7248 {
7249     PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
7250 }
7251 
7252 // size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size
ProgressBar(float fraction,const ImVec2 & size_arg,const char * overlay)7253 void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay)
7254 {
7255     ImGuiWindow* window = GetCurrentWindow();
7256     if (window->SkipItems)
7257         return;
7258 
7259     ImGuiContext& g = *GImGui;
7260     const ImGuiStyle& style = g.Style;
7261 
7262     ImVec2 pos = window->DC.CursorPos;
7263     ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f));
7264     ItemSize(bb, style.FramePadding.y);
7265     if (!ItemAdd(bb, NULL))
7266         return;
7267 
7268     // Render
7269     fraction = ImSaturate(fraction);
7270     RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
7271     bb.Reduce(ImVec2(window->BorderSize, window->BorderSize));
7272     const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y);
7273     RenderFrame(bb.Min, fill_br, GetColorU32(ImGuiCol_PlotHistogram), false, style.FrameRounding);
7274 
7275     // Default displaying the fraction as percentage string, but user can override it
7276     char overlay_buf[32];
7277     if (!overlay)
7278     {
7279         ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f);
7280         overlay = overlay_buf;
7281     }
7282 
7283     ImVec2 overlay_size = CalcTextSize(overlay, NULL);
7284     if (overlay_size.x > 0.0f)
7285         RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f,0.5f), &bb);
7286 }
7287 
Checkbox(const char * label,bool * v)7288 bool ImGui::Checkbox(const char* label, bool* v)
7289 {
7290     ImGuiWindow* window = GetCurrentWindow();
7291     if (window->SkipItems)
7292         return false;
7293 
7294     ImGuiContext& g = *GImGui;
7295     const ImGuiStyle& style = g.Style;
7296     const ImGuiID id = window->GetID(label);
7297     const ImVec2 label_size = CalcTextSize(label, NULL, true);
7298 
7299     const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2, label_size.y + style.FramePadding.y*2));
7300     ItemSize(check_bb, style.FramePadding.y);
7301 
7302     ImRect total_bb = check_bb;
7303     if (label_size.x > 0)
7304         SameLine(0, style.ItemInnerSpacing.x);
7305     const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size);
7306     if (label_size.x > 0)
7307     {
7308         ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
7309         total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max));
7310     }
7311 
7312     if (!ItemAdd(total_bb, &id))
7313         return false;
7314 
7315     bool hovered, held;
7316     bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
7317     if (pressed)
7318         *v = !(*v);
7319 
7320     RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
7321     if (*v)
7322     {
7323         const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
7324         const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
7325         window->DrawList->AddRectFilled(check_bb.Min+ImVec2(pad,pad), check_bb.Max-ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), style.FrameRounding);
7326     }
7327 
7328     if (g.LogEnabled)
7329         LogRenderedText(text_bb.GetTL(), *v ? "[x]" : "[ ]");
7330     if (label_size.x > 0.0f)
7331         RenderText(text_bb.GetTL(), label);
7332 
7333     return pressed;
7334 }
7335 
CheckboxFlags(const char * label,unsigned int * flags,unsigned int flags_value)7336 bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value)
7337 {
7338     bool v = ((*flags & flags_value) == flags_value);
7339     bool pressed = Checkbox(label, &v);
7340     if (pressed)
7341     {
7342         if (v)
7343             *flags |= flags_value;
7344         else
7345             *flags &= ~flags_value;
7346     }
7347 
7348     return pressed;
7349 }
7350 
RadioButton(const char * label,bool active)7351 bool ImGui::RadioButton(const char* label, bool active)
7352 {
7353     ImGuiWindow* window = GetCurrentWindow();
7354     if (window->SkipItems)
7355         return false;
7356 
7357     ImGuiContext& g = *GImGui;
7358     const ImGuiStyle& style = g.Style;
7359     const ImGuiID id = window->GetID(label);
7360     const ImVec2 label_size = CalcTextSize(label, NULL, true);
7361 
7362     const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2-1, label_size.y + style.FramePadding.y*2-1));
7363     ItemSize(check_bb, style.FramePadding.y);
7364 
7365     ImRect total_bb = check_bb;
7366     if (label_size.x > 0)
7367         SameLine(0, style.ItemInnerSpacing.x);
7368     const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size);
7369     if (label_size.x > 0)
7370     {
7371         ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
7372         total_bb.Add(text_bb);
7373     }
7374 
7375     if (!ItemAdd(total_bb, &id))
7376         return false;
7377 
7378     ImVec2 center = check_bb.GetCenter();
7379     center.x = (float)(int)center.x + 0.5f;
7380     center.y = (float)(int)center.y + 0.5f;
7381     const float radius = check_bb.GetHeight() * 0.5f;
7382 
7383     bool hovered, held;
7384     bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
7385 
7386     window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16);
7387     if (active)
7388     {
7389         const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
7390         const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
7391         window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16);
7392     }
7393 
7394     if (window->Flags & ImGuiWindowFlags_ShowBorders)
7395     {
7396         window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16);
7397         window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16);
7398     }
7399 
7400     if (g.LogEnabled)
7401         LogRenderedText(text_bb.GetTL(), active ? "(x)" : "( )");
7402     if (label_size.x > 0.0f)
7403         RenderText(text_bb.GetTL(), label);
7404 
7405     return pressed;
7406 }
7407 
RadioButton(const char * label,int * v,int v_button)7408 bool ImGui::RadioButton(const char* label, int* v, int v_button)
7409 {
7410     const bool pressed = RadioButton(label, *v == v_button);
7411     if (pressed)
7412     {
7413         *v = v_button;
7414     }
7415     return pressed;
7416 }
7417 
InputTextCalcTextLenAndLineCount(const char * text_begin,const char ** out_text_end)7418 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
7419 {
7420     int line_count = 0;
7421     const char* s = text_begin;
7422     while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding
7423         if (c == '\n')
7424             line_count++;
7425     s--;
7426     if (s[0] != '\n' && s[0] != '\r')
7427         line_count++;
7428     *out_text_end = s;
7429     return line_count;
7430 }
7431 
InputTextCalcTextSizeW(const ImWchar * text_begin,const ImWchar * text_end,const ImWchar ** remaining,ImVec2 * out_offset,bool stop_on_new_line)7432 static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line)
7433 {
7434     ImFont* font = GImGui->Font;
7435     const float line_height = GImGui->FontSize;
7436     const float scale = line_height / font->FontSize;
7437 
7438     ImVec2 text_size = ImVec2(0,0);
7439     float line_width = 0.0f;
7440 
7441     const ImWchar* s = text_begin;
7442     while (s < text_end)
7443     {
7444         unsigned int c = (unsigned int)(*s++);
7445         if (c == '\n')
7446         {
7447             text_size.x = ImMax(text_size.x, line_width);
7448             text_size.y += line_height;
7449             line_width = 0.0f;
7450             if (stop_on_new_line)
7451                 break;
7452             continue;
7453         }
7454         if (c == '\r')
7455             continue;
7456 
7457         const float char_width = font->GetCharAdvance((unsigned short)c) * scale;
7458         line_width += char_width;
7459     }
7460 
7461     if (text_size.x < line_width)
7462         text_size.x = line_width;
7463 
7464     if (out_offset)
7465         *out_offset = ImVec2(line_width, text_size.y + line_height);  // offset allow for the possibility of sitting after a trailing \n
7466 
7467     if (line_width > 0 || text_size.y == 0.0f)                        // whereas size.y will ignore the trailing \n
7468         text_size.y += line_height;
7469 
7470     if (remaining)
7471         *remaining = s;
7472 
7473     return text_size;
7474 }
7475 
7476 // Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar)
7477 namespace ImGuiStb
7478 {
7479 
STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING * obj)7480 static int     STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj)                             { return obj->CurLenW; }
STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING * obj,int idx)7481 static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx)                      { return obj->Text[idx]; }
STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING * obj,int line_start_idx,int char_idx)7482 static float   STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx)  { ImWchar c = obj->Text[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); }
STB_TEXTEDIT_KEYTOTEXT(int key)7483 static int     STB_TEXTEDIT_KEYTOTEXT(int key)                                                    { return key >= 0x10000 ? 0 : key; }
7484 static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
STB_TEXTEDIT_LAYOUTROW(StbTexteditRow * r,STB_TEXTEDIT_STRING * obj,int line_start_idx)7485 static void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
7486 {
7487     const ImWchar* text = obj->Text.Data;
7488     const ImWchar* text_remaining = NULL;
7489     const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true);
7490     r->x0 = 0.0f;
7491     r->x1 = size.x;
7492     r->baseline_y_delta = size.y;
7493     r->ymin = 0.0f;
7494     r->ymax = size.y;
7495     r->num_chars = (int)(text_remaining - (text + line_start_idx));
7496 }
7497 
is_separator(unsigned int c)7498 static bool is_separator(unsigned int c)                                        { return ImCharIsSpace(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; }
is_word_boundary_from_right(STB_TEXTEDIT_STRING * obj,int idx)7499 static int  is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx)      { return idx > 0 ? (is_separator( obj->Text[idx-1] ) && !is_separator( obj->Text[idx] ) ) : 1; }
STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING * obj,int idx)7500 static int  STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)   { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
7501 #ifdef __APPLE__    // FIXME: Move setting to IO structure
is_word_boundary_from_left(STB_TEXTEDIT_STRING * obj,int idx)7502 static int  is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx)       { return idx > 0 ? (!is_separator( obj->Text[idx-1] ) && is_separator( obj->Text[idx] ) ) : 1; }
STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING * obj,int idx)7503 static int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)  { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
7504 #else
STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING * obj,int idx)7505 static int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)  { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
7506 #endif
7507 #define STB_TEXTEDIT_MOVEWORDLEFT   STB_TEXTEDIT_MOVEWORDLEFT_IMPL    // They need to be #define for stb_textedit.h
7508 #define STB_TEXTEDIT_MOVEWORDRIGHT  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
7509 
STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING * obj,int pos,int n)7510 static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
7511 {
7512     ImWchar* dst = obj->Text.Data + pos;
7513 
7514     // We maintain our buffer length in both UTF-8 and wchar formats
7515     obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);
7516     obj->CurLenW -= n;
7517 
7518     // Offset remaining text
7519     const ImWchar* src = obj->Text.Data + pos + n;
7520     while (ImWchar c = *src++)
7521         *dst++ = c;
7522     *dst = '\0';
7523 }
7524 
STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING * obj,int pos,const ImWchar * new_text,int new_text_len)7525 static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len)
7526 {
7527     const int text_len = obj->CurLenW;
7528     IM_ASSERT(pos <= text_len);
7529     if (new_text_len + text_len + 1 > obj->Text.Size)
7530         return false;
7531 
7532     const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
7533     if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA)
7534         return false;
7535 
7536     ImWchar* text = obj->Text.Data;
7537     if (pos != text_len)
7538         memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));
7539     memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
7540 
7541     obj->CurLenW += new_text_len;
7542     obj->CurLenA += new_text_len_utf8;
7543     obj->Text[obj->CurLenW] = '\0';
7544 
7545     return true;
7546 }
7547 
7548 // We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols)
7549 #define STB_TEXTEDIT_K_LEFT         0x10000 // keyboard input to move cursor left
7550 #define STB_TEXTEDIT_K_RIGHT        0x10001 // keyboard input to move cursor right
7551 #define STB_TEXTEDIT_K_UP           0x10002 // keyboard input to move cursor up
7552 #define STB_TEXTEDIT_K_DOWN         0x10003 // keyboard input to move cursor down
7553 #define STB_TEXTEDIT_K_LINESTART    0x10004 // keyboard input to move cursor to start of line
7554 #define STB_TEXTEDIT_K_LINEEND      0x10005 // keyboard input to move cursor to end of line
7555 #define STB_TEXTEDIT_K_TEXTSTART    0x10006 // keyboard input to move cursor to start of text
7556 #define STB_TEXTEDIT_K_TEXTEND      0x10007 // keyboard input to move cursor to end of text
7557 #define STB_TEXTEDIT_K_DELETE       0x10008 // keyboard input to delete selection or character under cursor
7558 #define STB_TEXTEDIT_K_BACKSPACE    0x10009 // keyboard input to delete selection or character left of cursor
7559 #define STB_TEXTEDIT_K_UNDO         0x1000A // keyboard input to perform undo
7560 #define STB_TEXTEDIT_K_REDO         0x1000B // keyboard input to perform redo
7561 #define STB_TEXTEDIT_K_WORDLEFT     0x1000C // keyboard input to move cursor left one word
7562 #define STB_TEXTEDIT_K_WORDRIGHT    0x1000D // keyboard input to move cursor right one word
7563 #define STB_TEXTEDIT_K_SHIFT        0x20000
7564 
7565 #define STB_TEXTEDIT_IMPLEMENTATION
7566 #include "stb_textedit.h"
7567 
7568 }
7569 
OnKeyPressed(int key)7570 void ImGuiTextEditState::OnKeyPressed(int key)
7571 {
7572     stb_textedit_key(this, &StbState, key);
7573     CursorFollow = true;
7574     CursorAnimReset();
7575 }
7576 
7577 // Public API to manipulate UTF-8 text
7578 // We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar)
7579 // FIXME: The existence of this rarely exercised code path is a bit of a nuisance.
DeleteChars(int pos,int bytes_count)7580 void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count)
7581 {
7582     IM_ASSERT(pos + bytes_count <= BufTextLen);
7583     char* dst = Buf + pos;
7584     const char* src = Buf + pos + bytes_count;
7585     while (char c = *src++)
7586         *dst++ = c;
7587     *dst = '\0';
7588 
7589     if (CursorPos + bytes_count >= pos)
7590         CursorPos -= bytes_count;
7591     else if (CursorPos >= pos)
7592         CursorPos = pos;
7593     SelectionStart = SelectionEnd = CursorPos;
7594     BufDirty = true;
7595     BufTextLen -= bytes_count;
7596 }
7597 
InsertChars(int pos,const char * new_text,const char * new_text_end)7598 void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)
7599 {
7600     const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
7601     if (new_text_len + BufTextLen + 1 >= BufSize)
7602         return;
7603 
7604     if (BufTextLen != pos)
7605         memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos));
7606     memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char));
7607     Buf[BufTextLen + new_text_len] = '\0';
7608 
7609     if (CursorPos >= pos)
7610         CursorPos += new_text_len;
7611     SelectionStart = SelectionEnd = CursorPos;
7612     BufDirty = true;
7613     BufTextLen += new_text_len;
7614 }
7615 
7616 // Return false to discard a character.
InputTextFilterCharacter(unsigned int * p_char,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)7617 static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
7618 {
7619     unsigned int c = *p_char;
7620 
7621     if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF)))
7622     {
7623         bool pass = false;
7624         pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline));
7625         pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput));
7626         if (!pass)
7627             return false;
7628     }
7629 
7630     if (c >= 0xE000 && c <= 0xF8FF) // Filter private Unicode range. I don't imagine anybody would want to input them. GLFW on OSX seems to send private characters for special keys like arrow keys.
7631         return false;
7632 
7633     if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank))
7634     {
7635         if (flags & ImGuiInputTextFlags_CharsDecimal)
7636             if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/'))
7637                 return false;
7638 
7639         if (flags & ImGuiInputTextFlags_CharsHexadecimal)
7640             if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F'))
7641                 return false;
7642 
7643         if (flags & ImGuiInputTextFlags_CharsUppercase)
7644             if (c >= 'a' && c <= 'z')
7645                 *p_char = (c += (unsigned int)('A'-'a'));
7646 
7647         if (flags & ImGuiInputTextFlags_CharsNoBlank)
7648             if (ImCharIsSpace(c))
7649                 return false;
7650     }
7651 
7652     if (flags & ImGuiInputTextFlags_CallbackCharFilter)
7653     {
7654         ImGuiTextEditCallbackData callback_data;
7655         memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
7656         callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;
7657         callback_data.EventChar = (ImWchar)c;
7658         callback_data.Flags = flags;
7659         callback_data.UserData = user_data;
7660         if (callback(&callback_data) != 0)
7661             return false;
7662         *p_char = callback_data.EventChar;
7663         if (!callback_data.EventChar)
7664             return false;
7665     }
7666 
7667     return true;
7668 }
7669 
7670 // Edit a string of text
7671 // NB: when active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while active has no effect.
7672 // FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188
InputTextEx(const char * label,char * buf,int buf_size,const ImVec2 & size_arg,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)7673 bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
7674 {
7675     ImGuiWindow* window = GetCurrentWindow();
7676     if (window->SkipItems)
7677         return false;
7678 
7679     IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys)
7680     IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)
7681 
7682     ImGuiContext& g = *GImGui;
7683     const ImGuiIO& io = g.IO;
7684     const ImGuiStyle& style = g.Style;
7685 
7686     const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;
7687     const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0;
7688     const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
7689 
7690     if (is_multiline) // Open group before calling GetID() because groups tracks id created during their spawn
7691         BeginGroup();
7692     const ImGuiID id = window->GetID(label);
7693     const ImVec2 label_size = CalcTextSize(label, NULL, true);
7694     ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line
7695     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
7696     const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f, 0.0f));
7697 
7698     ImGuiWindow* draw_window = window;
7699     if (is_multiline)
7700     {
7701         if (!BeginChildFrame(id, frame_bb.GetSize()))
7702         {
7703             EndChildFrame();
7704             EndGroup();
7705             return false;
7706         }
7707         draw_window = GetCurrentWindow();
7708         size.x -= draw_window->ScrollbarSizes.x;
7709     }
7710     else
7711     {
7712         ItemSize(total_bb, style.FramePadding.y);
7713         if (!ItemAdd(total_bb, &id))
7714             return false;
7715     }
7716 
7717     // Password pushes a temporary font with only a fallback glyph
7718     if (is_password)
7719     {
7720         const ImFont::Glyph* glyph = g.Font->FindGlyph('*');
7721         ImFont* password_font = &g.InputTextPasswordFont;
7722         password_font->FontSize = g.Font->FontSize;
7723         password_font->Scale = g.Font->Scale;
7724         password_font->DisplayOffset = g.Font->DisplayOffset;
7725         password_font->Ascent = g.Font->Ascent;
7726         password_font->Descent = g.Font->Descent;
7727         password_font->ContainerAtlas = g.Font->ContainerAtlas;
7728         password_font->FallbackGlyph = glyph;
7729         password_font->FallbackXAdvance = glyph->XAdvance;
7730         IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexXAdvance.empty() && password_font->IndexLookup.empty());
7731         PushFont(password_font);
7732     }
7733 
7734     // NB: we are only allowed to access 'edit_state' if we are the active widget.
7735     ImGuiTextEditState& edit_state = g.InputTextState;
7736 
7737     const bool focus_requested = FocusableItemRegister(window, g.ActiveId == id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0);    // Using completion callback disable keyboard tabbing
7738     const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent);
7739     const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code;
7740 
7741     const bool hovered = IsHovered(frame_bb, id);
7742     if (hovered)
7743     {
7744         SetHoveredID(id);
7745         g.MouseCursor = ImGuiMouseCursor_TextInput;
7746     }
7747     const bool user_clicked = hovered && io.MouseClicked[0];
7748     const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY");
7749 
7750     bool select_all = (g.ActiveId != id) && (flags & ImGuiInputTextFlags_AutoSelectAll) != 0;
7751     if (focus_requested || user_clicked || user_scrolled)
7752     {
7753         if (g.ActiveId != id)
7754         {
7755             // Start edition
7756             // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)
7757             // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode)
7758             const int prev_len_w = edit_state.CurLenW;
7759             edit_state.Text.resize(buf_size+1);        // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash.
7760             edit_state.InitialText.resize(buf_size+1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash.
7761             ImStrncpy(edit_state.InitialText.Data, buf, edit_state.InitialText.Size);
7762             const char* buf_end = NULL;
7763             edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
7764             edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImFormatString() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8.
7765             edit_state.CursorAnimReset();
7766 
7767             // Preserve cursor position and undo/redo stack if we come back to same widget
7768             // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar).
7769             const bool recycle_state = (edit_state.Id == id) && (prev_len_w == edit_state.CurLenW);
7770             if (recycle_state)
7771             {
7772                 // Recycle existing cursor/selection/undo stack but clamp position
7773                 // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
7774                 edit_state.CursorClamp();
7775             }
7776             else
7777             {
7778                 edit_state.Id = id;
7779                 edit_state.ScrollX = 0.0f;
7780                 stb_textedit_initialize_state(&edit_state.StbState, !is_multiline);
7781                 if (!is_multiline && focus_requested_by_code)
7782                     select_all = true;
7783             }
7784             if (flags & ImGuiInputTextFlags_AlwaysInsertMode)
7785                 edit_state.StbState.insert_mode = true;
7786             if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl)))
7787                 select_all = true;
7788         }
7789         SetActiveID(id, window);
7790         FocusWindow(window);
7791     }
7792     else if (io.MouseClicked[0])
7793     {
7794         // Release focus when we click outside
7795         if (g.ActiveId == id)
7796             ClearActiveID();
7797     }
7798 
7799     bool value_changed = false;
7800     bool enter_pressed = false;
7801 
7802     if (g.ActiveId == id)
7803     {
7804         if (!is_editable && !g.ActiveIdIsJustActivated)
7805         {
7806             // When read-only we always use the live data passed to the function
7807             edit_state.Text.resize(buf_size+1);
7808             const char* buf_end = NULL;
7809             edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
7810             edit_state.CurLenA = (int)(buf_end - buf);
7811             edit_state.CursorClamp();
7812         }
7813 
7814         edit_state.BufSizeA = buf_size;
7815 
7816         // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
7817         // Down the line we should have a cleaner library-wide concept of Selected vs Active.
7818         g.ActiveIdAllowOverlap = !io.MouseDown[0];
7819 
7820         // Edit in progress
7821         const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX;
7822         const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f));
7823 
7824         const bool osx_double_click_selects_words = io.OSXBehaviors;      // OS X style: Double click selects by word instead of selecting whole text
7825         if (select_all || (hovered && !osx_double_click_selects_words && io.MouseDoubleClicked[0]))
7826         {
7827             edit_state.SelectAll();
7828             edit_state.SelectedAllMouseLock = true;
7829         }
7830         else if (hovered && osx_double_click_selects_words && io.MouseDoubleClicked[0])
7831         {
7832             // Select a word only, OS X style (by simulating keystrokes)
7833             edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT);
7834             edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);
7835         }
7836         else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock)
7837         {
7838             stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
7839             edit_state.CursorAnimReset();
7840         }
7841         else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f))
7842         {
7843             stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
7844             edit_state.CursorAnimReset();
7845             edit_state.CursorFollow = true;
7846         }
7847         if (edit_state.SelectedAllMouseLock && !io.MouseDown[0])
7848             edit_state.SelectedAllMouseLock = false;
7849 
7850         if (io.InputCharacters[0])
7851         {
7852             // Process text input (before we check for Return because using some IME will effectively send a Return?)
7853             // We ignore CTRL inputs, but need to allow CTRL+ALT as some keyboards (e.g. German) use AltGR - which is Alt+Ctrl - to input certain characters.
7854             if (!(io.KeyCtrl && !io.KeyAlt) && is_editable)
7855             {
7856                 for (int n = 0; n < IM_ARRAYSIZE(io.InputCharacters) && io.InputCharacters[n]; n++)
7857                     if (unsigned int c = (unsigned int)io.InputCharacters[n])
7858                     {
7859                         // Insert character if they pass filtering
7860                         if (!InputTextFilterCharacter(&c, flags, callback, user_data))
7861                             continue;
7862                         edit_state.OnKeyPressed((int)c);
7863                     }
7864             }
7865 
7866             // Consume characters
7867             memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
7868         }
7869 
7870         // Handle various key-presses
7871         bool cancel_edit = false;
7872         const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
7873         const bool is_shortcut_key_only = (io.OSXBehaviors ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl
7874         const bool is_wordmove_key_down = io.OSXBehaviors ? io.KeyAlt : io.KeyCtrl;                     // OS X style: Text editing cursor movement using Alt instead of Ctrl
7875         const bool is_startend_key_down = io.OSXBehaviors && io.KeySuper && !io.KeyCtrl && !io.KeyAlt;  // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
7876 
7877         if (IsKeyPressedMap(ImGuiKey_LeftArrow))                        { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
7878         else if (IsKeyPressedMap(ImGuiKey_RightArrow))                  { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
7879         else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline)     { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
7880         else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline)   { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }
7881         else if (IsKeyPressedMap(ImGuiKey_Home))                        { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
7882         else if (IsKeyPressedMap(ImGuiKey_End))                         { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
7883         else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable)       { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
7884         else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable)
7885         {
7886             if (!edit_state.HasSelection())
7887             {
7888                 if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT);
7889                 else if (io.OSXBehaviors && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT);
7890             }
7891             edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);
7892         }
7893         else if (IsKeyPressedMap(ImGuiKey_Enter))
7894         {
7895             bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;
7896             if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl))
7897             {
7898                 ClearActiveID();
7899                 enter_pressed = true;
7900             }
7901             else if (is_editable)
7902             {
7903                 unsigned int c = '\n'; // Insert new line
7904                 if (InputTextFilterCharacter(&c, flags, callback, user_data))
7905                     edit_state.OnKeyPressed((int)c);
7906             }
7907         }
7908         else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable)
7909         {
7910             unsigned int c = '\t'; // Insert TAB
7911             if (InputTextFilterCharacter(&c, flags, callback, user_data))
7912                 edit_state.OnKeyPressed((int)c);
7913         }
7914         else if (IsKeyPressedMap(ImGuiKey_Escape))                                     { ClearActiveID(); cancel_edit = true; }
7915         else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable)   { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); }
7916         else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable)   { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); }
7917         else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_A))                  { edit_state.SelectAll(); edit_state.CursorFollow = true; }
7918         else if (is_shortcut_key_only && !is_password && ((IsKeyPressedMap(ImGuiKey_X) && is_editable) || IsKeyPressedMap(ImGuiKey_C)) && (!is_multiline || edit_state.HasSelection()))
7919         {
7920             // Cut, Copy
7921             const bool cut = IsKeyPressedMap(ImGuiKey_X);
7922             if (cut && !edit_state.HasSelection())
7923                 edit_state.SelectAll();
7924 
7925             if (io.SetClipboardTextFn)
7926             {
7927                 const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0;
7928                 const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW;
7929                 edit_state.TempTextBuffer.resize((ie-ib) * 4 + 1);
7930                 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data+ib, edit_state.Text.Data+ie);
7931                 SetClipboardText(edit_state.TempTextBuffer.Data);
7932             }
7933 
7934             if (cut)
7935             {
7936                 edit_state.CursorFollow = true;
7937                 stb_textedit_cut(&edit_state, &edit_state.StbState);
7938             }
7939         }
7940         else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_V) && is_editable)
7941         {
7942             // Paste
7943             if (const char* clipboard = GetClipboardText())
7944             {
7945                 // Filter pasted buffer
7946                 const int clipboard_len = (int)strlen(clipboard);
7947                 ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar));
7948                 int clipboard_filtered_len = 0;
7949                 for (const char* s = clipboard; *s; )
7950                 {
7951                     unsigned int c;
7952                     s += ImTextCharFromUtf8(&c, s, NULL);
7953                     if (c == 0)
7954                         break;
7955                     if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, user_data))
7956                         continue;
7957                     clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;
7958                 }
7959                 clipboard_filtered[clipboard_filtered_len] = 0;
7960                 if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation
7961                 {
7962                     stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len);
7963                     edit_state.CursorFollow = true;
7964                 }
7965                 ImGui::MemFree(clipboard_filtered);
7966             }
7967         }
7968 
7969         if (cancel_edit)
7970         {
7971             // Restore initial value
7972             if (is_editable)
7973             {
7974                 ImStrncpy(buf, edit_state.InitialText.Data, buf_size);
7975                 value_changed = true;
7976             }
7977         }
7978         else
7979         {
7980             // Apply new value immediately - copy modified buffer back
7981             // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer
7982             // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect.
7983             // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks.
7984             if (is_editable)
7985             {
7986                 edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4);
7987                 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL);
7988             }
7989 
7990             // User callback
7991             if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0)
7992             {
7993                 IM_ASSERT(callback != NULL);
7994 
7995                 // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
7996                 ImGuiInputTextFlags event_flag = 0;
7997                 ImGuiKey event_key = ImGuiKey_COUNT;
7998                 if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab))
7999                 {
8000                     event_flag = ImGuiInputTextFlags_CallbackCompletion;
8001                     event_key = ImGuiKey_Tab;
8002                 }
8003                 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow))
8004                 {
8005                     event_flag = ImGuiInputTextFlags_CallbackHistory;
8006                     event_key = ImGuiKey_UpArrow;
8007                 }
8008                 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow))
8009                 {
8010                     event_flag = ImGuiInputTextFlags_CallbackHistory;
8011                     event_key = ImGuiKey_DownArrow;
8012                 }
8013                 else if (flags & ImGuiInputTextFlags_CallbackAlways)
8014                     event_flag = ImGuiInputTextFlags_CallbackAlways;
8015 
8016                 if (event_flag)
8017                 {
8018                     ImGuiTextEditCallbackData callback_data;
8019                     memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
8020                     callback_data.EventFlag = event_flag;
8021                     callback_data.Flags = flags;
8022                     callback_data.UserData = user_data;
8023                     callback_data.ReadOnly = !is_editable;
8024 
8025                     callback_data.EventKey = event_key;
8026                     callback_data.Buf = edit_state.TempTextBuffer.Data;
8027                     callback_data.BufTextLen = edit_state.CurLenA;
8028                     callback_data.BufSize = edit_state.BufSizeA;
8029                     callback_data.BufDirty = false;
8030 
8031                     // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188)
8032                     ImWchar* text = edit_state.Text.Data;
8033                     const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor);
8034                     const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start);
8035                     const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end);
8036 
8037                     // Call user code
8038                     callback(&callback_data);
8039 
8040                     // Read back what user may have modified
8041                     IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data);  // Invalid to modify those fields
8042                     IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA);
8043                     IM_ASSERT(callback_data.Flags == flags);
8044                     if (callback_data.CursorPos != utf8_cursor_pos)            edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos);
8045                     if (callback_data.SelectionStart != utf8_selection_start)  edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart);
8046                     if (callback_data.SelectionEnd != utf8_selection_end)      edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd);
8047                     if (callback_data.BufDirty)
8048                     {
8049                         IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
8050                         edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, callback_data.Buf, NULL);
8051                         edit_state.CurLenA = callback_data.BufTextLen;  // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
8052                         edit_state.CursorAnimReset();
8053                     }
8054                 }
8055             }
8056 
8057             // Copy back to user buffer
8058             if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0)
8059             {
8060                 ImStrncpy(buf, edit_state.TempTextBuffer.Data, buf_size);
8061                 value_changed = true;
8062             }
8063         }
8064     }
8065 
8066     // Render
8067     // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on.
8068     const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf; buf = NULL;
8069 
8070     if (!is_multiline)
8071         RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
8072 
8073     const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size
8074     ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;
8075     ImVec2 text_size(0.f, 0.f);
8076     const bool is_currently_scrolling = (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY"));
8077     if (g.ActiveId == id || is_currently_scrolling)
8078     {
8079         edit_state.CursorAnim += io.DeltaTime;
8080 
8081         // This is going to be messy. We need to:
8082         // - Display the text (this alone can be more easily clipped)
8083         // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)
8084         // - Measure text height (for scrollbar)
8085         // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)
8086         // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
8087         const ImWchar* text_begin = edit_state.Text.Data;
8088         ImVec2 cursor_offset, select_start_offset;
8089 
8090         {
8091             // Count lines + find lines numbers straddling 'cursor' and 'select_start' position.
8092             const ImWchar* searches_input_ptr[2];
8093             searches_input_ptr[0] = text_begin + edit_state.StbState.cursor;
8094             searches_input_ptr[1] = NULL;
8095             int searches_remaining = 1;
8096             int searches_result_line_number[2] = { -1, -999 };
8097             if (edit_state.StbState.select_start != edit_state.StbState.select_end)
8098             {
8099                 searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
8100                 searches_result_line_number[1] = -1;
8101                 searches_remaining++;
8102             }
8103 
8104             // Iterate all lines to find our line numbers
8105             // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter.
8106             searches_remaining += is_multiline ? 1 : 0;
8107             int line_count = 0;
8108             for (const ImWchar* s = text_begin; *s != 0; s++)
8109                 if (*s == '\n')
8110                 {
8111                     line_count++;
8112                     if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; }
8113                     if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; }
8114                 }
8115             line_count++;
8116             if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count;
8117             if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count;
8118 
8119             // Calculate 2d position by finding the beginning of the line and measuring distance
8120             cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;
8121             cursor_offset.y = searches_result_line_number[0] * g.FontSize;
8122             if (searches_result_line_number[1] >= 0)
8123             {
8124                 select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;
8125                 select_start_offset.y = searches_result_line_number[1] * g.FontSize;
8126             }
8127 
8128             // Calculate text height
8129             if (is_multiline)
8130                 text_size = ImVec2(size.x, line_count * g.FontSize);
8131         }
8132 
8133         // Scroll
8134         if (edit_state.CursorFollow)
8135         {
8136             // Horizontal scroll in chunks of quarter width
8137             if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll))
8138             {
8139                 const float scroll_increment_x = size.x * 0.25f;
8140                 if (cursor_offset.x < edit_state.ScrollX)
8141                     edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x);
8142                 else if (cursor_offset.x - size.x >= edit_state.ScrollX)
8143                     edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x);
8144             }
8145             else
8146             {
8147                 edit_state.ScrollX = 0.0f;
8148             }
8149 
8150             // Vertical scroll
8151             if (is_multiline)
8152             {
8153                 float scroll_y = draw_window->Scroll.y;
8154                 if (cursor_offset.y - g.FontSize < scroll_y)
8155                     scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
8156                 else if (cursor_offset.y - size.y >= scroll_y)
8157                     scroll_y = cursor_offset.y - size.y;
8158                 draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y);   // To avoid a frame of lag
8159                 draw_window->Scroll.y = scroll_y;
8160                 render_pos.y = draw_window->DC.CursorPos.y;
8161             }
8162         }
8163         edit_state.CursorFollow = false;
8164         const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f);
8165 
8166         // Draw selection
8167         if (edit_state.StbState.select_start != edit_state.StbState.select_end)
8168         {
8169             const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
8170             const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end);
8171 
8172             float bg_offy_up = is_multiline ? 0.0f : -1.0f;    // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection.
8173             float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
8174             ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg);
8175             ImVec2 rect_pos = render_pos + select_start_offset - render_scroll;
8176             for (const ImWchar* p = text_selected_begin; p < text_selected_end; )
8177             {
8178                 if (rect_pos.y > clip_rect.w + g.FontSize)
8179                     break;
8180                 if (rect_pos.y < clip_rect.y)
8181                 {
8182                     while (p < text_selected_end)
8183                         if (*p++ == '\n')
8184                             break;
8185                 }
8186                 else
8187                 {
8188                     ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true);
8189                     if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((unsigned short)' ') * 0.50f); // So we can see selected empty lines
8190                     ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn));
8191                     rect.Clip(clip_rect);
8192                     if (rect.Overlaps(clip_rect))
8193                         draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
8194                 }
8195                 rect_pos.x = render_pos.x - render_scroll.x;
8196                 rect_pos.y += g.FontSize;
8197             }
8198         }
8199 
8200         draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect);
8201 
8202         // Draw blinking cursor
8203         bool cursor_is_visible = (g.InputTextState.CursorAnim <= 0.0f) || fmodf(g.InputTextState.CursorAnim, 1.20f) <= 0.80f;
8204         ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll;
8205         ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y-g.FontSize+0.5f, cursor_screen_pos.x+1.0f, cursor_screen_pos.y-1.5f);
8206         if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
8207             draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
8208 
8209         // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
8210         if (is_editable)
8211             g.OsImePosRequest = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize);
8212     }
8213     else
8214     {
8215         // Render text only
8216         const char* buf_end = NULL;
8217         if (is_multiline)
8218             text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width
8219         draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect);
8220     }
8221 
8222     if (is_multiline)
8223     {
8224         Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line
8225         EndChildFrame();
8226         EndGroup();
8227     }
8228 
8229     if (is_password)
8230         PopFont();
8231 
8232     // Log as text
8233     if (g.LogEnabled && !is_password)
8234         LogRenderedText(render_pos, buf_display, NULL);
8235 
8236     if (label_size.x > 0)
8237         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
8238 
8239     if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)
8240         return enter_pressed;
8241     else
8242         return value_changed;
8243 }
8244 
InputText(const char * label,char * buf,size_t buf_size,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)8245 bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
8246 {
8247     IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline()
8248     return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data);
8249 }
8250 
InputTextMultiline(const char * label,char * buf,size_t buf_size,const ImVec2 & size,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)8251 bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
8252 {
8253     return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);
8254 }
8255 
8256 // NB: scalar_format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "display_format" argument)
InputScalarEx(const char * label,ImGuiDataType data_type,void * data_ptr,void * step_ptr,void * step_fast_ptr,const char * scalar_format,ImGuiInputTextFlags extra_flags)8257 bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags)
8258 {
8259     ImGuiWindow* window = GetCurrentWindow();
8260     if (window->SkipItems)
8261         return false;
8262 
8263     ImGuiContext& g = *GImGui;
8264     const ImGuiStyle& style = g.Style;
8265     const ImVec2 label_size = CalcTextSize(label, NULL, true);
8266 
8267     BeginGroup();
8268     PushID(label);
8269     const ImVec2 button_sz = ImVec2(g.FontSize, g.FontSize) + style.FramePadding*2.0f;
8270     if (step_ptr)
8271         PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_sz.x + style.ItemInnerSpacing.x)*2));
8272 
8273     char buf[64];
8274     DataTypeFormatString(data_type, data_ptr, scalar_format, buf, IM_ARRAYSIZE(buf));
8275 
8276     bool value_changed = false;
8277     if (!(extra_flags & ImGuiInputTextFlags_CharsHexadecimal))
8278         extra_flags |= ImGuiInputTextFlags_CharsDecimal;
8279     extra_flags |= ImGuiInputTextFlags_AutoSelectAll;
8280     if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags)) // PushId(label) + "" gives us the expected ID from outside point of view
8281         value_changed = DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, scalar_format);
8282 
8283     // Step buttons
8284     if (step_ptr)
8285     {
8286         PopItemWidth();
8287         SameLine(0, style.ItemInnerSpacing.x);
8288         if (ButtonEx("-", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
8289         {
8290             DataTypeApplyOp(data_type, '-', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
8291             value_changed = true;
8292         }
8293         SameLine(0, style.ItemInnerSpacing.x);
8294         if (ButtonEx("+", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
8295         {
8296             DataTypeApplyOp(data_type, '+', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
8297             value_changed = true;
8298         }
8299     }
8300     PopID();
8301 
8302     if (label_size.x > 0)
8303     {
8304         SameLine(0, style.ItemInnerSpacing.x);
8305         RenderText(ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + style.FramePadding.y), label);
8306         ItemSize(label_size, style.FramePadding.y);
8307     }
8308     EndGroup();
8309 
8310     return value_changed;
8311 }
8312 
InputFloat(const char * label,float * v,float step,float step_fast,int decimal_precision,ImGuiInputTextFlags extra_flags)8313 bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags)
8314 {
8315     char display_format[16];
8316     if (decimal_precision < 0)
8317         strcpy(display_format, "%f");      // Ideally we'd have a minimum decimal precision of 1 to visually denote that this is a float, while hiding non-significant digits? %f doesn't have a minimum of 1
8318     else
8319         ImFormatString(display_format, IM_ARRAYSIZE(display_format), "%%.%df", decimal_precision);
8320     return InputScalarEx(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), display_format, extra_flags);
8321 }
8322 
InputInt(const char * label,int * v,int step,int step_fast,ImGuiInputTextFlags extra_flags)8323 bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags)
8324 {
8325     // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes.
8326     const char* scalar_format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d";
8327     return InputScalarEx(label, ImGuiDataType_Int, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), scalar_format, extra_flags);
8328 }
8329 
InputFloatN(const char * label,float * v,int components,int decimal_precision,ImGuiInputTextFlags extra_flags)8330 bool ImGui::InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags)
8331 {
8332     ImGuiWindow* window = GetCurrentWindow();
8333     if (window->SkipItems)
8334         return false;
8335 
8336     ImGuiContext& g = *GImGui;
8337     bool value_changed = false;
8338     BeginGroup();
8339     PushID(label);
8340     PushMultiItemsWidths(components);
8341     for (int i = 0; i < components; i++)
8342     {
8343         PushID(i);
8344         value_changed |= InputFloat("##v", &v[i], 0, 0, decimal_precision, extra_flags);
8345         SameLine(0, g.Style.ItemInnerSpacing.x);
8346         PopID();
8347         PopItemWidth();
8348     }
8349     PopID();
8350 
8351     window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
8352     TextUnformatted(label, FindRenderedTextEnd(label));
8353     EndGroup();
8354 
8355     return value_changed;
8356 }
8357 
InputFloat2(const char * label,float v[2],int decimal_precision,ImGuiInputTextFlags extra_flags)8358 bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags)
8359 {
8360     return InputFloatN(label, v, 2, decimal_precision, extra_flags);
8361 }
8362 
InputFloat3(const char * label,float v[3],int decimal_precision,ImGuiInputTextFlags extra_flags)8363 bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags)
8364 {
8365     return InputFloatN(label, v, 3, decimal_precision, extra_flags);
8366 }
8367 
InputFloat4(const char * label,float v[4],int decimal_precision,ImGuiInputTextFlags extra_flags)8368 bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags)
8369 {
8370     return InputFloatN(label, v, 4, decimal_precision, extra_flags);
8371 }
8372 
InputIntN(const char * label,int * v,int components,ImGuiInputTextFlags extra_flags)8373 bool ImGui::InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags)
8374 {
8375     ImGuiWindow* window = GetCurrentWindow();
8376     if (window->SkipItems)
8377         return false;
8378 
8379     ImGuiContext& g = *GImGui;
8380     bool value_changed = false;
8381     BeginGroup();
8382     PushID(label);
8383     PushMultiItemsWidths(components);
8384     for (int i = 0; i < components; i++)
8385     {
8386         PushID(i);
8387         value_changed |= InputInt("##v", &v[i], 0, 0, extra_flags);
8388         SameLine(0, g.Style.ItemInnerSpacing.x);
8389         PopID();
8390         PopItemWidth();
8391     }
8392     PopID();
8393 
8394     window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
8395     TextUnformatted(label, FindRenderedTextEnd(label));
8396     EndGroup();
8397 
8398     return value_changed;
8399 }
8400 
InputInt2(const char * label,int v[2],ImGuiInputTextFlags extra_flags)8401 bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags)
8402 {
8403     return InputIntN(label, v, 2, extra_flags);
8404 }
8405 
InputInt3(const char * label,int v[3],ImGuiInputTextFlags extra_flags)8406 bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags)
8407 {
8408     return InputIntN(label, v, 3, extra_flags);
8409 }
8410 
InputInt4(const char * label,int v[4],ImGuiInputTextFlags extra_flags)8411 bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags)
8412 {
8413     return InputIntN(label, v, 4, extra_flags);
8414 }
8415 
Items_ArrayGetter(void * data,int idx,const char ** out_text)8416 static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
8417 {
8418     const char* const* items = (const char* const*)data;
8419     if (out_text)
8420         *out_text = items[idx];
8421     return true;
8422 }
8423 
Items_SingleStringGetter(void * data,int idx,const char ** out_text)8424 static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
8425 {
8426     // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
8427     const char* items_separated_by_zeros = (const char*)data;
8428     int items_count = 0;
8429     const char* p = items_separated_by_zeros;
8430     while (*p)
8431     {
8432         if (idx == items_count)
8433             break;
8434         p += strlen(p) + 1;
8435         items_count++;
8436     }
8437     if (!*p)
8438         return false;
8439     if (out_text)
8440         *out_text = p;
8441     return true;
8442 }
8443 
8444 // Combo box helper allowing to pass an array of strings.
Combo(const char * label,int * current_item,const char * const * items,int items_count,int height_in_items)8445 bool ImGui::Combo(const char* label, int* current_item, const char* const* items, int items_count, int height_in_items)
8446 {
8447     const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
8448     return value_changed;
8449 }
8450 
8451 // Combo box helper allowing to pass all items in a single string.
Combo(const char * label,int * current_item,const char * items_separated_by_zeros,int height_in_items)8452 bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
8453 {
8454     int items_count = 0;
8455     const char* p = items_separated_by_zeros;       // FIXME-OPT: Avoid computing this, or at least only when combo is open
8456     while (*p)
8457     {
8458         p += strlen(p) + 1;
8459         items_count++;
8460     }
8461     bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
8462     return value_changed;
8463 }
8464 
8465 // Combo box function.
Combo(const char * label,int * current_item,bool (* items_getter)(void *,int,const char **),void * data,int items_count,int height_in_items)8466 bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
8467 {
8468     ImGuiWindow* window = GetCurrentWindow();
8469     if (window->SkipItems)
8470         return false;
8471 
8472     ImGuiContext& g = *GImGui;
8473     const ImGuiStyle& style = g.Style;
8474     const ImGuiID id = window->GetID(label);
8475     const float w = CalcItemWidth();
8476 
8477     const ImVec2 label_size = CalcTextSize(label, NULL, true);
8478     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
8479     const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
8480     ItemSize(total_bb, style.FramePadding.y);
8481     if (!ItemAdd(total_bb, &id))
8482         return false;
8483 
8484     const float arrow_size = (g.FontSize + style.FramePadding.x * 2.0f);
8485     const bool hovered = IsHovered(frame_bb, id);
8486     bool popup_open = IsPopupOpen(id);
8487     bool popup_opened_now = false;
8488 
8489     const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
8490     RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
8491     RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(popup_open || hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding); // FIXME-ROUNDING
8492     RenderCollapseTriangle(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y) + style.FramePadding, true);
8493 
8494     if (*current_item >= 0 && *current_item < items_count)
8495     {
8496         const char* item_text;
8497         if (items_getter(data, *current_item, &item_text))
8498             RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, item_text, NULL, NULL, ImVec2(0.0f,0.0f));
8499     }
8500 
8501     if (label_size.x > 0)
8502         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
8503 
8504     if (hovered)
8505     {
8506         SetHoveredID(id);
8507         if (g.IO.MouseClicked[0])
8508         {
8509             ClearActiveID();
8510             if (IsPopupOpen(id))
8511             {
8512                 ClosePopup(id);
8513             }
8514             else
8515             {
8516                 FocusWindow(window);
8517                 OpenPopup(label);
8518                 popup_open = popup_opened_now = true;
8519             }
8520         }
8521     }
8522 
8523     bool value_changed = false;
8524     if (IsPopupOpen(id))
8525     {
8526         // Size default to hold ~7 items
8527         if (height_in_items < 0)
8528             height_in_items = 7;
8529 
8530         float popup_height = (label_size.y + style.ItemSpacing.y) * ImMin(items_count, height_in_items) + (style.FramePadding.y * 3);
8531         float popup_y1 = frame_bb.Max.y;
8532         float popup_y2 = ImClamp(popup_y1 + popup_height, popup_y1, g.IO.DisplaySize.y - style.DisplaySafeAreaPadding.y);
8533         if ((popup_y2 - popup_y1) < ImMin(popup_height, frame_bb.Min.y - style.DisplaySafeAreaPadding.y))
8534         {
8535             // Position our combo ABOVE because there's more space to fit! (FIXME: Handle in Begin() or use a shared helper. We have similar code in Begin() for popup placement)
8536             popup_y1 = ImClamp(frame_bb.Min.y - popup_height, style.DisplaySafeAreaPadding.y, frame_bb.Min.y);
8537             popup_y2 = frame_bb.Min.y;
8538         }
8539         ImRect popup_rect(ImVec2(frame_bb.Min.x, popup_y1), ImVec2(frame_bb.Max.x, popup_y2));
8540         SetNextWindowPos(popup_rect.Min);
8541         SetNextWindowSize(popup_rect.GetSize());
8542         PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
8543 
8544         const ImGuiWindowFlags flags = ImGuiWindowFlags_ComboBox | ((window->Flags & ImGuiWindowFlags_ShowBorders) ? ImGuiWindowFlags_ShowBorders : 0);
8545         if (BeginPopupEx(label, flags))
8546         {
8547             // Display items
8548             Spacing();
8549             for (int i = 0; i < items_count; i++)
8550             {
8551                 PushID((void*)(intptr_t)i);
8552                 const bool item_selected = (i == *current_item);
8553                 const char* item_text;
8554                 if (!items_getter(data, i, &item_text))
8555                     item_text = "*Unknown item*";
8556                 if (Selectable(item_text, item_selected))
8557                 {
8558                     ClearActiveID();
8559                     value_changed = true;
8560                     *current_item = i;
8561                 }
8562                 if (item_selected && popup_opened_now)
8563                     SetScrollHere();
8564                 PopID();
8565             }
8566             EndPopup();
8567         }
8568         PopStyleVar();
8569     }
8570     return value_changed;
8571 }
8572 
8573 // Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image.
8574 // But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID.
Selectable(const char * label,bool selected,ImGuiSelectableFlags flags,const ImVec2 & size_arg)8575 bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
8576 {
8577     ImGuiWindow* window = GetCurrentWindow();
8578     if (window->SkipItems)
8579         return false;
8580 
8581     ImGuiContext& g = *GImGui;
8582     const ImGuiStyle& style = g.Style;
8583 
8584     if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
8585         PopClipRect();
8586 
8587     ImGuiID id = window->GetID(label);
8588     ImVec2 label_size = CalcTextSize(label, NULL, true);
8589     ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
8590     ImVec2 pos = window->DC.CursorPos;
8591     pos.y += window->DC.CurrentLineTextBaseOffset;
8592     ImRect bb(pos, pos + size);
8593     ItemSize(bb);
8594 
8595     // Fill horizontal space.
8596     ImVec2 window_padding = window->WindowPadding;
8597     float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x;
8598     float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x);
8599     ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y);
8600     ImRect bb_with_spacing(pos, pos + size_draw);
8601     if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth))
8602         bb_with_spacing.Max.x += window_padding.x;
8603 
8604     // Selectables are tightly packed together, we extend the box to cover spacing between selectable.
8605     float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f);
8606     float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f);
8607     float spacing_R = style.ItemSpacing.x - spacing_L;
8608     float spacing_D = style.ItemSpacing.y - spacing_U;
8609     bb_with_spacing.Min.x -= spacing_L;
8610     bb_with_spacing.Min.y -= spacing_U;
8611     bb_with_spacing.Max.x += spacing_R;
8612     bb_with_spacing.Max.y += spacing_D;
8613     if (!ItemAdd(bb_with_spacing, &id))
8614     {
8615         if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
8616             PushColumnClipRect();
8617         return false;
8618     }
8619 
8620     ImGuiButtonFlags button_flags = 0;
8621     if (flags & ImGuiSelectableFlags_Menu) button_flags |= ImGuiButtonFlags_PressedOnClick;
8622     if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnClick|ImGuiButtonFlags_PressedOnRelease;
8623     if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled;
8624     if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
8625     bool hovered, held;
8626     bool pressed = ButtonBehavior(bb_with_spacing, id, &hovered, &held, button_flags);
8627     if (flags & ImGuiSelectableFlags_Disabled)
8628         selected = false;
8629 
8630     // Render
8631     if (hovered || selected)
8632     {
8633         const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
8634         RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, 0.0f);
8635     }
8636 
8637     if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
8638     {
8639         PushColumnClipRect();
8640         bb_with_spacing.Max.x -= (GetContentRegionMax().x - max_x);
8641     }
8642 
8643     if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
8644     RenderTextClipped(bb.Min, bb_with_spacing.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f));
8645     if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor();
8646 
8647     // Automatically close popups
8648     if (pressed && !(flags & ImGuiSelectableFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
8649         CloseCurrentPopup();
8650     return pressed;
8651 }
8652 
Selectable(const char * label,bool * p_selected,ImGuiSelectableFlags flags,const ImVec2 & size_arg)8653 bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
8654 {
8655     if (Selectable(label, *p_selected, flags, size_arg))
8656     {
8657         *p_selected = !*p_selected;
8658         return true;
8659     }
8660     return false;
8661 }
8662 
8663 // Helper to calculate the size of a listbox and display a label on the right.
8664 // Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty"
ListBoxHeader(const char * label,const ImVec2 & size_arg)8665 bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
8666 {
8667     ImGuiWindow* window = GetCurrentWindow();
8668     if (window->SkipItems)
8669         return false;
8670 
8671     const ImGuiStyle& style = GetStyle();
8672     const ImGuiID id = GetID(label);
8673     const ImVec2 label_size = CalcTextSize(label, NULL, true);
8674 
8675     // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
8676     ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y);
8677     ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y));
8678     ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
8679     ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
8680     window->DC.LastItemRect = bb;
8681 
8682     BeginGroup();
8683     if (label_size.x > 0)
8684         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
8685 
8686     BeginChildFrame(id, frame_bb.GetSize());
8687     return true;
8688 }
8689 
ListBoxHeader(const char * label,int items_count,int height_in_items)8690 bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items)
8691 {
8692     // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
8693     // However we don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size.
8694     // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution.
8695     if (height_in_items < 0)
8696         height_in_items = ImMin(items_count, 7);
8697     float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f);
8698 
8699     // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild().
8700     ImVec2 size;
8701     size.x = 0.0f;
8702     size.y = GetTextLineHeightWithSpacing() * height_in_items_f + GetStyle().ItemSpacing.y;
8703     return ListBoxHeader(label, size);
8704 }
8705 
ListBoxFooter()8706 void ImGui::ListBoxFooter()
8707 {
8708     ImGuiWindow* parent_window = GetParentWindow();
8709     const ImRect bb = parent_window->DC.LastItemRect;
8710     const ImGuiStyle& style = GetStyle();
8711 
8712     EndChildFrame();
8713 
8714     // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect)
8715     // We call SameLine() to restore DC.CurrentLine* data
8716     SameLine();
8717     parent_window->DC.CursorPos = bb.Min;
8718     ItemSize(bb, style.FramePadding.y);
8719     EndGroup();
8720 }
8721 
ListBox(const char * label,int * current_item,const char * const * items,int items_count,int height_items)8722 bool ImGui::ListBox(const char* label, int* current_item, const char* const* items, int items_count, int height_items)
8723 {
8724     const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items);
8725     return value_changed;
8726 }
8727 
ListBox(const char * label,int * current_item,bool (* items_getter)(void *,int,const char **),void * data,int items_count,int height_in_items)8728 bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
8729 {
8730     if (!ListBoxHeader(label, items_count, height_in_items))
8731         return false;
8732 
8733     // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper.
8734     bool value_changed = false;
8735     ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing());
8736     while (clipper.Step())
8737         for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
8738         {
8739             const bool item_selected = (i == *current_item);
8740             const char* item_text;
8741             if (!items_getter(data, i, &item_text))
8742                 item_text = "*Unknown item*";
8743 
8744             PushID(i);
8745             if (Selectable(item_text, item_selected))
8746             {
8747                 *current_item = i;
8748                 value_changed = true;
8749             }
8750             PopID();
8751         }
8752     ListBoxFooter();
8753     return value_changed;
8754 }
8755 
MenuItem(const char * label,const char * shortcut,bool selected,bool enabled)8756 bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)
8757 {
8758     ImGuiWindow* window = GetCurrentWindow();
8759     if (window->SkipItems)
8760         return false;
8761 
8762     ImGuiContext& g = *GImGui;
8763     ImVec2 pos = window->DC.CursorPos;
8764     ImVec2 label_size = CalcTextSize(label, NULL, true);
8765     ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f);
8766     float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame
8767     float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
8768 
8769     bool pressed = Selectable(label, false, ImGuiSelectableFlags_MenuItem | ImGuiSelectableFlags_DrawFillAvailWidth | (enabled ? 0 : ImGuiSelectableFlags_Disabled), ImVec2(w, 0.0f));
8770     if (shortcut_size.x > 0.0f)
8771     {
8772         PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
8773         RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
8774         PopStyleColor();
8775     }
8776 
8777     if (selected)
8778         RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled));
8779 
8780     return pressed;
8781 }
8782 
MenuItem(const char * label,const char * shortcut,bool * p_selected,bool enabled)8783 bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled)
8784 {
8785     if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled))
8786     {
8787         if (p_selected)
8788             *p_selected = !*p_selected;
8789         return true;
8790     }
8791     return false;
8792 }
8793 
BeginMainMenuBar()8794 bool ImGui::BeginMainMenuBar()
8795 {
8796     ImGuiContext& g = *GImGui;
8797     SetNextWindowPos(ImVec2(0.0f, 0.0f));
8798     SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.FontBaseSize + g.Style.FramePadding.y * 2.0f));
8799     PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
8800     PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0));
8801     if (!Begin("##MainMenuBar", NULL, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_MenuBar)
8802         || !BeginMenuBar())
8803     {
8804         End();
8805         PopStyleVar(2);
8806         return false;
8807     }
8808     g.CurrentWindow->DC.MenuBarOffsetX += g.Style.DisplaySafeAreaPadding.x;
8809     return true;
8810 }
8811 
EndMainMenuBar()8812 void ImGui::EndMainMenuBar()
8813 {
8814     EndMenuBar();
8815     End();
8816     PopStyleVar(2);
8817 }
8818 
BeginMenuBar()8819 bool ImGui::BeginMenuBar()
8820 {
8821     ImGuiWindow* window = GetCurrentWindow();
8822     if (window->SkipItems)
8823         return false;
8824     if (!(window->Flags & ImGuiWindowFlags_MenuBar))
8825         return false;
8826 
8827     IM_ASSERT(!window->DC.MenuBarAppending);
8828     BeginGroup(); // Save position
8829     PushID("##menubar");
8830     ImRect rect = window->MenuBarRect();
8831     PushClipRect(ImVec2(ImFloor(rect.Min.x+0.5f), ImFloor(rect.Min.y + window->BorderSize + 0.5f)), ImVec2(ImFloor(rect.Max.x+0.5f), ImFloor(rect.Max.y+0.5f)), false);
8832     window->DC.CursorPos = ImVec2(rect.Min.x + window->DC.MenuBarOffsetX, rect.Min.y);// + g.Style.FramePadding.y);
8833     window->DC.LayoutType = ImGuiLayoutType_Horizontal;
8834     window->DC.MenuBarAppending = true;
8835     AlignFirstTextHeightToWidgets();
8836     return true;
8837 }
8838 
EndMenuBar()8839 void ImGui::EndMenuBar()
8840 {
8841     ImGuiWindow* window = GetCurrentWindow();
8842     if (window->SkipItems)
8843         return;
8844 
8845     IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);
8846     IM_ASSERT(window->DC.MenuBarAppending);
8847     PopClipRect();
8848     PopID();
8849     window->DC.MenuBarOffsetX = window->DC.CursorPos.x - window->MenuBarRect().Min.x;
8850     window->DC.GroupStack.back().AdvanceCursor = false;
8851     EndGroup();
8852     window->DC.LayoutType = ImGuiLayoutType_Vertical;
8853     window->DC.MenuBarAppending = false;
8854 }
8855 
BeginMenu(const char * label,bool enabled)8856 bool ImGui::BeginMenu(const char* label, bool enabled)
8857 {
8858     ImGuiWindow* window = GetCurrentWindow();
8859     if (window->SkipItems)
8860         return false;
8861 
8862     ImGuiContext& g = *GImGui;
8863     const ImGuiStyle& style = g.Style;
8864     const ImGuiID id = window->GetID(label);
8865 
8866     ImVec2 label_size = CalcTextSize(label, NULL, true);
8867     ImGuiWindow* backed_focused_window = g.FocusedWindow;
8868 
8869     bool pressed;
8870     bool menu_is_open = IsPopupOpen(id);
8871     bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentMenuSet == window->GetID("##menus"));
8872     if (menuset_is_open)
8873         g.FocusedWindow = window;
8874 
8875     // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestPopupWindowPos).
8876     ImVec2 popup_pos, pos = window->DC.CursorPos;
8877     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
8878     {
8879         popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight());
8880         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
8881         PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
8882         float w = label_size.x;
8883         pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
8884         PopStyleVar();
8885         SameLine();
8886         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
8887     }
8888     else
8889     {
8890         popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
8891         float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame
8892         float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
8893         pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
8894         if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
8895         RenderCollapseTriangle(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), false);
8896         if (!enabled) PopStyleColor();
8897     }
8898 
8899     bool hovered = enabled && IsHovered(window->DC.LastItemRect, id);
8900     if (menuset_is_open)
8901         g.FocusedWindow = backed_focused_window;
8902 
8903     bool want_open = false, want_close = false;
8904     if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
8905     {
8906         // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
8907         bool moving_within_opened_triangle = false;
8908         if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window)
8909         {
8910             if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window)
8911             {
8912                 ImRect next_window_rect = next_window->Rect();
8913                 ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
8914                 ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
8915                 ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
8916                 float extra = ImClamp(fabsf(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
8917                 ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f;   // to avoid numerical issues
8918                 tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f);            // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
8919                 tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
8920                 moving_within_opened_triangle = ImIsPointInTriangle(g.IO.MousePos, ta, tb, tc);
8921                 //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug
8922             }
8923         }
8924 
8925         want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle);
8926         want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed);
8927     }
8928     else if (menu_is_open && pressed && menuset_is_open) // menu-bar: click open menu to close
8929     {
8930         want_close = true;
8931         want_open = menu_is_open = false;
8932     }
8933     else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // menu-bar: first click to open, then hover to open others
8934         want_open = true;
8935     if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }'
8936         want_close = true;
8937     if (want_close && IsPopupOpen(id))
8938         ClosePopupToLevel(GImGui->CurrentPopupStack.Size);
8939 
8940     if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size)
8941     {
8942         // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
8943         OpenPopup(label);
8944         return false;
8945     }
8946 
8947     menu_is_open |= want_open;
8948     if (want_open)
8949         OpenPopup(label);
8950 
8951     if (menu_is_open)
8952     {
8953         SetNextWindowPos(popup_pos, ImGuiSetCond_Always);
8954         ImGuiWindowFlags flags = ImGuiWindowFlags_ShowBorders | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu);
8955         menu_is_open = BeginPopupEx(label, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
8956     }
8957 
8958     return menu_is_open;
8959 }
8960 
EndMenu()8961 void ImGui::EndMenu()
8962 {
8963     EndPopup();
8964 }
8965 
8966 // A little colored square. Return true when clicked.
8967 // FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip.
ColorButton(const ImVec4 & col,bool small_height,bool outline_border)8968 bool ImGui::ColorButton(const ImVec4& col, bool small_height, bool outline_border)
8969 {
8970     ImGuiWindow* window = GetCurrentWindow();
8971     if (window->SkipItems)
8972         return false;
8973 
8974     ImGuiContext& g = *GImGui;
8975     const ImGuiStyle& style = g.Style;
8976     const ImGuiID id = window->GetID("#colorbutton");
8977     const float square_size = g.FontSize;
8978     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(square_size + style.FramePadding.y*2, square_size + (small_height ? 0 : style.FramePadding.y*2)));
8979     ItemSize(bb, small_height ? 0.0f : style.FramePadding.y);
8980     if (!ItemAdd(bb, &id))
8981         return false;
8982 
8983     bool hovered, held;
8984     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
8985     RenderFrame(bb.Min, bb.Max, GetColorU32(col), outline_border, style.FrameRounding);
8986 
8987     if (hovered)
8988         SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col.x, col.y, col.z, col.w, IM_F32_TO_INT8_SAT(col.x), IM_F32_TO_INT8_SAT(col.y), IM_F32_TO_INT8_SAT(col.z), IM_F32_TO_INT8_SAT(col.w));
8989 
8990     return pressed;
8991 }
8992 
ColorEdit3(const char * label,float col[3])8993 bool ImGui::ColorEdit3(const char* label, float col[3])
8994 {
8995     float col4[4];
8996     col4[0] = col[0];
8997     col4[1] = col[1];
8998     col4[2] = col[2];
8999     col4[3] = 1.0f;
9000     const bool value_changed = ColorEdit4(label, col4, false);
9001     col[0] = col4[0];
9002     col[1] = col4[1];
9003     col[2] = col4[2];
9004     return value_changed;
9005 }
9006 
9007 // Edit colors components (each component in 0.0f..1.0f range
9008 // Use CTRL-Click to input value and TAB to go to next item.
ColorEdit4(const char * label,float col[4],bool alpha)9009 bool ImGui::ColorEdit4(const char* label, float col[4], bool alpha)
9010 {
9011     ImGuiWindow* window = GetCurrentWindow();
9012     if (window->SkipItems)
9013         return false;
9014 
9015     ImGuiContext& g = *GImGui;
9016     const ImGuiStyle& style = g.Style;
9017     const ImGuiID id = window->GetID(label);
9018     const float w_full = CalcItemWidth();
9019     const float square_sz = (g.FontSize + style.FramePadding.y * 2.0f);
9020 
9021     ImGuiColorEditMode edit_mode = window->DC.ColorEditMode;
9022     if (edit_mode == ImGuiColorEditMode_UserSelect || edit_mode == ImGuiColorEditMode_UserSelectShowButton)
9023         edit_mode = g.ColorEditModeStorage.GetInt(id, 0) % 3;
9024 
9025     float f[4] = { col[0], col[1], col[2], col[3] };
9026     if (edit_mode == ImGuiColorEditMode_HSV)
9027         ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
9028 
9029     int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) };
9030 
9031     int components = alpha ? 4 : 3;
9032     bool value_changed = false;
9033 
9034     BeginGroup();
9035     PushID(label);
9036 
9037     const bool hsv = (edit_mode == 1);
9038     switch (edit_mode)
9039     {
9040     case ImGuiColorEditMode_RGB:
9041     case ImGuiColorEditMode_HSV:
9042         {
9043             // RGB/HSV 0..255 Sliders
9044             const float w_items_all = w_full - (square_sz + style.ItemInnerSpacing.x);
9045             const float w_item_one  = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
9046             const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
9047 
9048             const bool hide_prefix = (w_item_one <= CalcTextSize("M:999").x);
9049             const char* ids[4] = { "##X", "##Y", "##Z", "##W" };
9050             const char* fmt_table[3][4] =
9051             {
9052                 {   "%3.0f",   "%3.0f",   "%3.0f",   "%3.0f" },
9053                 { "R:%3.0f", "G:%3.0f", "B:%3.0f", "A:%3.0f" },
9054                 { "H:%3.0f", "S:%3.0f", "V:%3.0f", "A:%3.0f" }
9055             };
9056             const char** fmt = hide_prefix ? fmt_table[0] : hsv ? fmt_table[2] : fmt_table[1];
9057 
9058             PushItemWidth(w_item_one);
9059             for (int n = 0; n < components; n++)
9060             {
9061                 if (n > 0)
9062                     SameLine(0, style.ItemInnerSpacing.x);
9063                 if (n + 1 == components)
9064                     PushItemWidth(w_item_last);
9065                 value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, 255, fmt[n]);
9066             }
9067             PopItemWidth();
9068             PopItemWidth();
9069         }
9070         break;
9071     case ImGuiColorEditMode_HEX:
9072         {
9073             // RGB Hexadecimal Input
9074             const float w_slider_all = w_full - square_sz;
9075             char buf[64];
9076             if (alpha)
9077                 ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", i[0], i[1], i[2], i[3]);
9078             else
9079                 ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", i[0], i[1], i[2]);
9080             PushItemWidth(w_slider_all - style.ItemInnerSpacing.x);
9081             if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase))
9082             {
9083                 value_changed |= true;
9084                 char* p = buf;
9085                 while (*p == '#' || ImCharIsSpace(*p))
9086                     p++;
9087                 i[0] = i[1] = i[2] = i[3] = 0;
9088                 if (alpha)
9089                     sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned)
9090                 else
9091                     sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
9092             }
9093             PopItemWidth();
9094         }
9095         break;
9096     }
9097 
9098     SameLine(0, style.ItemInnerSpacing.x);
9099 
9100     const ImVec4 col_display(col[0], col[1], col[2], 1.0f);
9101     if (ColorButton(col_display))
9102         g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away!
9103 
9104     // Recreate our own tooltip over's ColorButton() one because we want to display correct alpha here
9105     if (IsItemHovered())
9106         SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col[0], col[1], col[2], col[3], IM_F32_TO_INT8_SAT(col[0]), IM_F32_TO_INT8_SAT(col[1]), IM_F32_TO_INT8_SAT(col[2]), IM_F32_TO_INT8_SAT(col[3]));
9107 
9108     if (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton)
9109     {
9110         SameLine(0, style.ItemInnerSpacing.x);
9111         const char* button_titles[3] = { "RGB", "HSV", "HEX" };
9112         if (ButtonEx(button_titles[edit_mode], ImVec2(0,0), ImGuiButtonFlags_DontClosePopups))
9113             g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away!
9114     }
9115 
9116     const char* label_display_end = FindRenderedTextEnd(label);
9117     if (label != label_display_end)
9118     {
9119         SameLine(0, (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton) ? -1.0f : style.ItemInnerSpacing.x);
9120         TextUnformatted(label, label_display_end);
9121     }
9122 
9123     // Convert back
9124     for (int n = 0; n < 4; n++)
9125         f[n] = i[n] / 255.0f;
9126     if (edit_mode == 1)
9127         ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
9128 
9129     if (value_changed)
9130     {
9131         col[0] = f[0];
9132         col[1] = f[1];
9133         col[2] = f[2];
9134         if (alpha)
9135             col[3] = f[3];
9136     }
9137 
9138     PopID();
9139     EndGroup();
9140 
9141     return value_changed;
9142 }
9143 
ColorEditMode(ImGuiColorEditMode mode)9144 void ImGui::ColorEditMode(ImGuiColorEditMode mode)
9145 {
9146     ImGuiWindow* window = GetCurrentWindow();
9147     window->DC.ColorEditMode = mode;
9148 }
9149 
9150 // Horizontal separating line.
Separator()9151 void ImGui::Separator()
9152 {
9153     ImGuiWindow* window = GetCurrentWindow();
9154     if (window->SkipItems)
9155         return;
9156 
9157     if (window->DC.ColumnsCount > 1)
9158         PopClipRect();
9159 
9160     float x1 = window->Pos.x;
9161     float x2 = window->Pos.x + window->Size.x;
9162     if (!window->DC.GroupStack.empty())
9163         x1 += window->DC.IndentX;
9164 
9165     const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f));
9166     ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit, we don't provide height to not alter layout.
9167     if (!ItemAdd(bb, NULL))
9168     {
9169         if (window->DC.ColumnsCount > 1)
9170             PushColumnClipRect();
9171         return;
9172     }
9173 
9174     window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Border));
9175 
9176     ImGuiContext& g = *GImGui;
9177     if (g.LogEnabled)
9178         LogText(IM_NEWLINE "--------------------------------");
9179 
9180     if (window->DC.ColumnsCount > 1)
9181     {
9182         PushColumnClipRect();
9183         window->DC.ColumnsCellMinY = window->DC.CursorPos.y;
9184     }
9185 }
9186 
Spacing()9187 void ImGui::Spacing()
9188 {
9189     ImGuiWindow* window = GetCurrentWindow();
9190     if (window->SkipItems)
9191         return;
9192     ItemSize(ImVec2(0,0));
9193 }
9194 
Dummy(const ImVec2 & size)9195 void ImGui::Dummy(const ImVec2& size)
9196 {
9197     ImGuiWindow* window = GetCurrentWindow();
9198     if (window->SkipItems)
9199         return;
9200 
9201     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
9202     ItemSize(bb);
9203     ItemAdd(bb, NULL);
9204 }
9205 
IsRectVisible(const ImVec2 & size)9206 bool ImGui::IsRectVisible(const ImVec2& size)
9207 {
9208     ImGuiWindow* window = GetCurrentWindowRead();
9209     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
9210 }
9211 
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)9212 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
9213 {
9214     ImGuiWindow* window = GetCurrentWindowRead();
9215     return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
9216 }
9217 
9218 // Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
BeginGroup()9219 void ImGui::BeginGroup()
9220 {
9221     ImGuiWindow* window = GetCurrentWindow();
9222 
9223     window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
9224     ImGuiGroupData& group_data = window->DC.GroupStack.back();
9225     group_data.BackupCursorPos = window->DC.CursorPos;
9226     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
9227     group_data.BackupIndentX = window->DC.IndentX;
9228     group_data.BackupGroupOffsetX = window->DC.GroupOffsetX;
9229     group_data.BackupCurrentLineHeight = window->DC.CurrentLineHeight;
9230     group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
9231     group_data.BackupLogLinePosY = window->DC.LogLinePosY;
9232     group_data.BackupActiveIdIsAlive = GImGui->ActiveIdIsAlive;
9233     group_data.AdvanceCursor = true;
9234 
9235     window->DC.GroupOffsetX = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffsetX;
9236     window->DC.IndentX = window->DC.GroupOffsetX;
9237     window->DC.CursorMaxPos = window->DC.CursorPos;
9238     window->DC.CurrentLineHeight = 0.0f;
9239     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
9240 }
9241 
EndGroup()9242 void ImGui::EndGroup()
9243 {
9244     ImGuiContext& g = *GImGui;
9245     ImGuiWindow* window = GetCurrentWindow();
9246 
9247     IM_ASSERT(!window->DC.GroupStack.empty());	// Mismatched BeginGroup()/EndGroup() calls
9248 
9249     ImGuiGroupData& group_data = window->DC.GroupStack.back();
9250 
9251     ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
9252     group_bb.Max.y -= g.Style.ItemSpacing.y;      // Cancel out last vertical spacing because we are adding one ourselves.
9253     group_bb.Max = ImMax(group_bb.Min, group_bb.Max);
9254 
9255     window->DC.CursorPos = group_data.BackupCursorPos;
9256     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
9257     window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight;
9258     window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
9259     window->DC.IndentX = group_data.BackupIndentX;
9260     window->DC.GroupOffsetX = group_data.BackupGroupOffsetX;
9261     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
9262 
9263     if (group_data.AdvanceCursor)
9264     {
9265         window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrentLineTextBaseOffset);      // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.
9266         ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset);
9267         ItemAdd(group_bb, NULL);
9268     }
9269 
9270     // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive() will function on the entire group.
9271     // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but if you search for LastItemId you'll notice it is only used in that context.
9272     const bool active_id_within_group = (!group_data.BackupActiveIdIsAlive && g.ActiveIdIsAlive && g.ActiveId && g.ActiveIdWindow->RootWindow == window->RootWindow);
9273     if (active_id_within_group)
9274         window->DC.LastItemId = g.ActiveId;
9275     if (active_id_within_group && g.HoveredId == g.ActiveId)
9276         window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = true;
9277 
9278     window->DC.GroupStack.pop_back();
9279 
9280     //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // Debug
9281 }
9282 
9283 // Gets back to previous line and continue with horizontal layout
9284 //      pos_x == 0      : follow right after previous item
9285 //      pos_x != 0      : align to specified x position (relative to window/group left)
9286 //      spacing_w < 0   : use default spacing if pos_x == 0, no spacing if pos_x != 0
9287 //      spacing_w >= 0  : enforce spacing amount
SameLine(float pos_x,float spacing_w)9288 void ImGui::SameLine(float pos_x, float spacing_w)
9289 {
9290     ImGuiWindow* window = GetCurrentWindow();
9291     if (window->SkipItems)
9292         return;
9293 
9294     ImGuiContext& g = *GImGui;
9295     if (pos_x != 0.0f)
9296     {
9297         if (spacing_w < 0.0f) spacing_w = 0.0f;
9298         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffsetX + window->DC.ColumnsOffsetX;
9299         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
9300     }
9301     else
9302     {
9303         if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
9304         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
9305         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
9306     }
9307     window->DC.CurrentLineHeight = window->DC.PrevLineHeight;
9308     window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
9309 }
9310 
NewLine()9311 void ImGui::NewLine()
9312 {
9313     ImGuiWindow* window = GetCurrentWindow();
9314     if (window->SkipItems)
9315         return;
9316     if (window->DC.CurrentLineHeight > 0.0f)     // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height.
9317         ItemSize(ImVec2(0,0));
9318     else
9319         ItemSize(ImVec2(0.0f, GImGui->FontSize));
9320 }
9321 
NextColumn()9322 void ImGui::NextColumn()
9323 {
9324     ImGuiWindow* window = GetCurrentWindow();
9325     if (window->SkipItems || window->DC.ColumnsCount <= 1)
9326         return;
9327 
9328     ImGuiContext& g = *GImGui;
9329     PopItemWidth();
9330     PopClipRect();
9331 
9332     window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
9333     if (++window->DC.ColumnsCurrent < window->DC.ColumnsCount)
9334     {
9335         // Columns 1+ cancel out IndentX
9336         window->DC.ColumnsOffsetX = GetColumnOffset(window->DC.ColumnsCurrent) - window->DC.IndentX + g.Style.ItemSpacing.x;
9337         window->DrawList->ChannelsSetCurrent(window->DC.ColumnsCurrent);
9338     }
9339     else
9340     {
9341         window->DC.ColumnsCurrent = 0;
9342         window->DC.ColumnsOffsetX = 0.0f;
9343         window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY;
9344         window->DrawList->ChannelsSetCurrent(0);
9345     }
9346     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
9347     window->DC.CursorPos.y = window->DC.ColumnsCellMinY;
9348     window->DC.CurrentLineHeight = 0.0f;
9349     window->DC.CurrentLineTextBaseOffset = 0.0f;
9350 
9351     PushColumnClipRect();
9352     PushItemWidth(GetColumnWidth() * 0.65f);  // FIXME: Move on columns setup
9353 }
9354 
GetColumnIndex()9355 int ImGui::GetColumnIndex()
9356 {
9357     ImGuiWindow* window = GetCurrentWindowRead();
9358     return window->DC.ColumnsCurrent;
9359 }
9360 
GetColumnsCount()9361 int ImGui::GetColumnsCount()
9362 {
9363     ImGuiWindow* window = GetCurrentWindowRead();
9364     return window->DC.ColumnsCount;
9365 }
9366 
GetDraggedColumnOffset(int column_index)9367 static float GetDraggedColumnOffset(int column_index)
9368 {
9369     // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
9370     // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
9371     ImGuiContext& g = *GImGui;
9372     ImGuiWindow* window = ImGui::GetCurrentWindowRead();
9373     IM_ASSERT(column_index > 0); // We cannot drag column 0. If you get this assert you may have a conflict between the ID of your columns and another widgets.
9374     IM_ASSERT(g.ActiveId == window->DC.ColumnsSetId + ImGuiID(column_index));
9375 
9376     float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x - window->Pos.x;
9377     x = ImClamp(x, ImGui::GetColumnOffset(column_index-1)+g.Style.ColumnsMinSpacing, ImGui::GetColumnOffset(column_index+1)-g.Style.ColumnsMinSpacing);
9378 
9379     return (float)(int)x;
9380 }
9381 
GetColumnOffset(int column_index)9382 float ImGui::GetColumnOffset(int column_index)
9383 {
9384     ImGuiContext& g = *GImGui;
9385     ImGuiWindow* window = GetCurrentWindowRead();
9386     if (column_index < 0)
9387         column_index = window->DC.ColumnsCurrent;
9388 
9389     if (g.ActiveId)
9390     {
9391         const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
9392         if (g.ActiveId == column_id)
9393             return GetDraggedColumnOffset(column_index);
9394     }
9395 
9396     IM_ASSERT(column_index < window->DC.ColumnsData.Size);
9397     const float t = window->DC.ColumnsData[column_index].OffsetNorm;
9398     const float x_offset = window->DC.ColumnsMinX + t * (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
9399     return (float)(int)x_offset;
9400 }
9401 
SetColumnOffset(int column_index,float offset)9402 void ImGui::SetColumnOffset(int column_index, float offset)
9403 {
9404     ImGuiWindow* window = GetCurrentWindow();
9405     if (column_index < 0)
9406         column_index = window->DC.ColumnsCurrent;
9407 
9408     IM_ASSERT(column_index < window->DC.ColumnsData.Size);
9409     const float t = (offset - window->DC.ColumnsMinX) / (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
9410     window->DC.ColumnsData[column_index].OffsetNorm = t;
9411 
9412     const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
9413     window->DC.StateStorage->SetFloat(column_id, t);
9414 }
9415 
GetColumnWidth(int column_index)9416 float ImGui::GetColumnWidth(int column_index)
9417 {
9418     ImGuiWindow* window = GetCurrentWindowRead();
9419     if (column_index < 0)
9420         column_index = window->DC.ColumnsCurrent;
9421 
9422     float w = GetColumnOffset(column_index+1) - GetColumnOffset(column_index);
9423     return w;
9424 }
9425 
PushColumnClipRect(int column_index)9426 static void PushColumnClipRect(int column_index)
9427 {
9428     ImGuiWindow* window = ImGui::GetCurrentWindow();
9429     if (column_index < 0)
9430         column_index = window->DC.ColumnsCurrent;
9431 
9432     float x1 = ImFloor(0.5f + window->Pos.x + ImGui::GetColumnOffset(column_index) - 1.0f);
9433     float x2 = ImFloor(0.5f + window->Pos.x + ImGui::GetColumnOffset(column_index+1) - 1.0f);
9434     ImGui::PushClipRect(ImVec2(x1,-FLT_MAX), ImVec2(x2,+FLT_MAX), true);
9435 }
9436 
Columns(int columns_count,const char * id,bool border)9437 void ImGui::Columns(int columns_count, const char* id, bool border)
9438 {
9439     ImGuiContext& g = *GImGui;
9440     ImGuiWindow* window = GetCurrentWindow();
9441     IM_ASSERT(columns_count >= 1);
9442 
9443     if (window->DC.ColumnsCount != 1)
9444     {
9445         if (window->DC.ColumnsCurrent != 0)
9446             ItemSize(ImVec2(0,0));   // Advance to column 0
9447         PopItemWidth();
9448         PopClipRect();
9449         window->DrawList->ChannelsMerge();
9450 
9451         window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
9452         window->DC.CursorPos.y = window->DC.ColumnsCellMaxY;
9453     }
9454 
9455     // Draw columns borders and handle resize at the time of "closing" a columns set
9456     if (window->DC.ColumnsCount != columns_count && window->DC.ColumnsCount != 1 && window->DC.ColumnsShowBorders && !window->SkipItems)
9457     {
9458         const float y1 = window->DC.ColumnsStartPosY;
9459         const float y2 = window->DC.CursorPos.y;
9460         for (int i = 1; i < window->DC.ColumnsCount; i++)
9461         {
9462             float x = window->Pos.x + GetColumnOffset(i);
9463             const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(i);
9464             const ImRect column_rect(ImVec2(x-4,y1),ImVec2(x+4,y2));
9465             if (IsClippedEx(column_rect, &column_id, false))
9466                 continue;
9467 
9468             bool hovered, held;
9469             ButtonBehavior(column_rect, column_id, &hovered, &held);
9470             if (hovered || held)
9471                 g.MouseCursor = ImGuiMouseCursor_ResizeEW;
9472 
9473             // Draw before resize so our items positioning are in sync with the line being drawn
9474             const ImU32 col = GetColorU32(held ? ImGuiCol_ColumnActive : hovered ? ImGuiCol_ColumnHovered : ImGuiCol_Column);
9475             const float xi = (float)(int)x;
9476             window->DrawList->AddLine(ImVec2(xi, y1+1.0f), ImVec2(xi, y2), col);
9477 
9478             if (held)
9479             {
9480                 if (g.ActiveIdIsJustActivated)
9481                     g.ActiveIdClickOffset.x -= 4;   // Store from center of column line (we used a 8 wide rect for columns clicking)
9482                 x = GetDraggedColumnOffset(i);
9483                 SetColumnOffset(i, x);
9484             }
9485         }
9486     }
9487 
9488     // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
9489     // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
9490     PushID(0x11223347 + (id ? 0 : columns_count));
9491     window->DC.ColumnsSetId = window->GetID(id ? id : "columns");
9492     PopID();
9493 
9494     // Set state for first column
9495     window->DC.ColumnsCurrent = 0;
9496     window->DC.ColumnsCount = columns_count;
9497     window->DC.ColumnsShowBorders = border;
9498 
9499     const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : window->Size.x;
9500     window->DC.ColumnsMinX = window->DC.IndentX; // Lock our horizontal range
9501     window->DC.ColumnsMaxX = content_region_width - window->Scroll.x - ((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x;
9502     window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
9503     window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.CursorPos.y;
9504     window->DC.ColumnsOffsetX = 0.0f;
9505     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
9506 
9507     if (window->DC.ColumnsCount != 1)
9508     {
9509         // Cache column offsets
9510         window->DC.ColumnsData.resize(columns_count + 1);
9511         for (int column_index = 0; column_index < columns_count + 1; column_index++)
9512         {
9513             const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
9514             KeepAliveID(column_id);
9515             const float default_t = column_index / (float)window->DC.ColumnsCount;
9516             const float t = window->DC.StateStorage->GetFloat(column_id, default_t);      // Cheaply store our floating point value inside the integer (could store a union into the map?)
9517             window->DC.ColumnsData[column_index].OffsetNorm = t;
9518         }
9519         window->DrawList->ChannelsSplit(window->DC.ColumnsCount);
9520         PushColumnClipRect();
9521         PushItemWidth(GetColumnWidth() * 0.65f);
9522     }
9523     else
9524     {
9525         window->DC.ColumnsData.resize(0);
9526     }
9527 }
9528 
Indent(float indent_w)9529 void ImGui::Indent(float indent_w)
9530 {
9531     ImGuiContext& g = *GImGui;
9532     ImGuiWindow* window = GetCurrentWindow();
9533     window->DC.IndentX += (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing;
9534     window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
9535 }
9536 
Unindent(float indent_w)9537 void ImGui::Unindent(float indent_w)
9538 {
9539     ImGuiContext& g = *GImGui;
9540     ImGuiWindow* window = GetCurrentWindow();
9541     window->DC.IndentX -= (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing;
9542     window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
9543 }
9544 
TreePush(const char * str_id)9545 void ImGui::TreePush(const char* str_id)
9546 {
9547     ImGuiWindow* window = GetCurrentWindow();
9548     Indent();
9549     window->DC.TreeDepth++;
9550     PushID(str_id ? str_id : "#TreePush");
9551 }
9552 
TreePush(const void * ptr_id)9553 void ImGui::TreePush(const void* ptr_id)
9554 {
9555     ImGuiWindow* window = GetCurrentWindow();
9556     Indent();
9557     window->DC.TreeDepth++;
9558     PushID(ptr_id ? ptr_id : (const void*)"#TreePush");
9559 }
9560 
TreePushRawID(ImGuiID id)9561 void ImGui::TreePushRawID(ImGuiID id)
9562 {
9563     ImGuiWindow* window = GetCurrentWindow();
9564     Indent();
9565     window->DC.TreeDepth++;
9566     window->IDStack.push_back(id);
9567 }
9568 
TreePop()9569 void ImGui::TreePop()
9570 {
9571     ImGuiWindow* window = GetCurrentWindow();
9572     Unindent();
9573     window->DC.TreeDepth--;
9574     PopID();
9575 }
9576 
Value(const char * prefix,bool b)9577 void ImGui::Value(const char* prefix, bool b)
9578 {
9579     Text("%s: %s", prefix, (b ? "true" : "false"));
9580 }
9581 
Value(const char * prefix,int v)9582 void ImGui::Value(const char* prefix, int v)
9583 {
9584     Text("%s: %d", prefix, v);
9585 }
9586 
Value(const char * prefix,unsigned int v)9587 void ImGui::Value(const char* prefix, unsigned int v)
9588 {
9589     Text("%s: %d", prefix, v);
9590 }
9591 
Value(const char * prefix,float v,const char * float_format)9592 void ImGui::Value(const char* prefix, float v, const char* float_format)
9593 {
9594     if (float_format)
9595     {
9596         char fmt[64];
9597         ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format);
9598         Text(fmt, prefix, v);
9599     }
9600     else
9601     {
9602         Text("%s: %.3f", prefix, v);
9603     }
9604 }
9605 
9606 // FIXME: May want to remove those helpers?
ValueColor(const char * prefix,const ImVec4 & v)9607 void ImGui::ValueColor(const char* prefix, const ImVec4& v)
9608 {
9609     Text("%s: (%.2f,%.2f,%.2f,%.2f)", prefix, v.x, v.y, v.z, v.w);
9610     SameLine();
9611     ColorButton(v, true);
9612 }
9613 
ValueColor(const char * prefix,ImU32 v)9614 void ImGui::ValueColor(const char* prefix, ImU32 v)
9615 {
9616     Text("%s: %08X", prefix, v);
9617     SameLine();
9618     ColorButton(ColorConvertU32ToFloat4(v), true);
9619 }
9620 
9621 //-----------------------------------------------------------------------------
9622 // PLATFORM DEPENDENT HELPERS
9623 //-----------------------------------------------------------------------------
9624 
9625 #if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS))
9626 #undef WIN32_LEAN_AND_MEAN
9627 #define WIN32_LEAN_AND_MEAN
9628 #include <windows.h>
9629 #endif
9630 
9631 // Win32 API clipboard implementation
9632 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS)
9633 
9634 #ifdef _MSC_VER
9635 #pragma comment(lib, "user32")
9636 #endif
9637 
GetClipboardTextFn_DefaultImpl(void *)9638 static const char* GetClipboardTextFn_DefaultImpl(void*)
9639 {
9640     static ImVector<char> buf_local;
9641     buf_local.clear();
9642     if (!OpenClipboard(NULL))
9643         return NULL;
9644     HANDLE wbuf_handle = GetClipboardData(CF_UNICODETEXT);
9645     if (wbuf_handle == NULL)
9646         return NULL;
9647     if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle))
9648     {
9649         int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
9650         buf_local.resize(buf_len);
9651         ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
9652     }
9653     GlobalUnlock(wbuf_handle);
9654     CloseClipboard();
9655     return buf_local.Data;
9656 }
9657 
SetClipboardTextFn_DefaultImpl(void *,const char * text)9658 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9659 {
9660     if (!OpenClipboard(NULL))
9661         return;
9662     const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
9663     HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
9664     if (wbuf_handle == NULL)
9665         return;
9666     ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle);
9667     ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
9668     GlobalUnlock(wbuf_handle);
9669     EmptyClipboard();
9670     SetClipboardData(CF_UNICODETEXT, wbuf_handle);
9671     CloseClipboard();
9672 }
9673 
9674 #else
9675 
9676 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
GetClipboardTextFn_DefaultImpl(void *)9677 static const char* GetClipboardTextFn_DefaultImpl(void*)
9678 {
9679     return GImGui->PrivateClipboard;
9680 }
9681 
9682 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
SetClipboardTextFn_DefaultImpl(void *,const char * text)9683 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9684 {
9685     ImGuiContext& g = *GImGui;
9686     if (g.PrivateClipboard)
9687     {
9688         ImGui::MemFree(g.PrivateClipboard);
9689         g.PrivateClipboard = NULL;
9690     }
9691     const char* text_end = text + strlen(text);
9692     g.PrivateClipboard = (char*)ImGui::MemAlloc((size_t)(text_end - text) + 1);
9693     memcpy(g.PrivateClipboard, text, (size_t)(text_end - text));
9694     g.PrivateClipboard[(int)(text_end - text)] = 0;
9695 }
9696 
9697 #endif
9698 
9699 // Win32 API IME support (for Asian languages, etc.)
9700 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS)
9701 
9702 #include <imm.h>
9703 #ifdef _MSC_VER
9704 #pragma comment(lib, "imm32")
9705 #endif
9706 
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)9707 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
9708 {
9709     // Notify OS Input Method Editor of text input position
9710     if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle)
9711         if (HIMC himc = ImmGetContext(hwnd))
9712         {
9713             COMPOSITIONFORM cf;
9714             cf.ptCurrentPos.x = x;
9715             cf.ptCurrentPos.y = y;
9716             cf.dwStyle = CFS_FORCE_POSITION;
9717             ImmSetCompositionWindow(himc, &cf);
9718         }
9719 }
9720 
9721 #else
9722 
ImeSetInputScreenPosFn_DefaultImpl(int,int)9723 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
9724 
9725 #endif
9726 
9727 //-----------------------------------------------------------------------------
9728 // HELP
9729 //-----------------------------------------------------------------------------
9730 
ShowMetricsWindow(bool * p_open)9731 void ImGui::ShowMetricsWindow(bool* p_open)
9732 {
9733     if (ImGui::Begin("ImGui Metrics", p_open))
9734     {
9735         ImGui::Text("ImGui %s", ImGui::GetVersion());
9736         ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
9737         ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3);
9738         ImGui::Text("%d allocations", ImGui::GetIO().MetricsAllocs);
9739         static bool show_clip_rects = true;
9740         ImGui::Checkbox("Show clipping rectangles when hovering a ImDrawCmd", &show_clip_rects);
9741         ImGui::Separator();
9742 
9743         struct Funcs
9744         {
9745             static void NodeDrawList(ImDrawList* draw_list, const char* label)
9746             {
9747                 bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size);
9748                 if (draw_list == ImGui::GetWindowDrawList())
9749                 {
9750                     ImGui::SameLine();
9751                     ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
9752                     if (node_open) ImGui::TreePop();
9753                     return;
9754                 }
9755                 if (!node_open)
9756                     return;
9757 
9758                 ImDrawList* overlay_draw_list = &GImGui->OverlayDrawList;   // Render additional visuals into the top-most draw list
9759                 overlay_draw_list->PushClipRectFullScreen();
9760                 int elem_offset = 0;
9761                 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
9762                 {
9763                     if (pcmd->UserCallback)
9764                     {
9765                         ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
9766                         continue;
9767                     }
9768                     ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
9769                     bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %-4d %s vtx, tex = %p, clip_rect = (%.0f,%.0f)..(%.0f,%.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
9770                     if (show_clip_rects && ImGui::IsItemHovered())
9771                     {
9772                         ImRect clip_rect = pcmd->ClipRect;
9773                         ImRect vtxs_rect;
9774                         for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
9775                             vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
9776                         clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255));
9777                         vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255));
9778                     }
9779                     if (!pcmd_node_open)
9780                         continue;
9781                     ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
9782                     while (clipper.Step())
9783                         for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
9784                         {
9785                             char buf[300], *buf_p = buf;
9786                             ImVec2 triangles_pos[3];
9787                             for (int n = 0; n < 3; n++, vtx_i++)
9788                             {
9789                                 ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i];
9790                                 triangles_pos[n] = v.pos;
9791                                 buf_p += sprintf(buf_p, "%s %04d { pos = (%8.2f,%8.2f), uv = (%.6f,%.6f), col = %08X }\n", (n == 0) ? "vtx" : "   ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
9792                             }
9793                             ImGui::Selectable(buf, false);
9794                             if (ImGui::IsItemHovered())
9795                                 overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f, false);  // Add triangle without AA, more readable for large-thin triangle
9796                         }
9797                     ImGui::TreePop();
9798                 }
9799                 overlay_draw_list->PopClipRect();
9800                 ImGui::TreePop();
9801             }
9802 
9803             static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
9804             {
9805                 if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
9806                     return;
9807                 for (int i = 0; i < windows.Size; i++)
9808                     Funcs::NodeWindow(windows[i], "Window");
9809                 ImGui::TreePop();
9810             }
9811 
9812             static void NodeWindow(ImGuiWindow* window, const char* label)
9813             {
9814                 if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
9815                     return;
9816                 NodeDrawList(window->DrawList, "DrawList");
9817                 ImGui::BulletText("Pos: (%.1f,%.1f)", window->Pos.x, window->Pos.y);
9818                 ImGui::BulletText("Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y);
9819                 ImGui::BulletText("Scroll: (%.2f,%.2f)", window->Scroll.x, window->Scroll.y);
9820                 if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
9821                 if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
9822                 ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
9823                 ImGui::TreePop();
9824             }
9825         };
9826 
9827         ImGuiContext& g = *GImGui;                // Access private state
9828         Funcs::NodeWindows(g.Windows, "Windows");
9829         if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.RenderDrawLists[0].Size))
9830         {
9831             for (int i = 0; i < g.RenderDrawLists[0].Size; i++)
9832                 Funcs::NodeDrawList(g.RenderDrawLists[0][i], "DrawList");
9833             ImGui::TreePop();
9834         }
9835         if (ImGui::TreeNode("Popups", "Open Popups Stack (%d)", g.OpenPopupStack.Size))
9836         {
9837             for (int i = 0; i < g.OpenPopupStack.Size; i++)
9838             {
9839                 ImGuiWindow* window = g.OpenPopupStack[i].Window;
9840                 ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : "");
9841             }
9842             ImGui::TreePop();
9843         }
9844         if (ImGui::TreeNode("Basic state"))
9845         {
9846             ImGui::Text("FocusedWindow: '%s'", g.FocusedWindow ? g.FocusedWindow->Name : "NULL");
9847             ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
9848             ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
9849             ImGui::Text("HoveredID: 0x%08X/0x%08X", g.HoveredId, g.HoveredIdPreviousFrame); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
9850             ImGui::Text("ActiveID: 0x%08X/0x%08X", g.ActiveId, g.ActiveIdPreviousFrame);
9851             ImGui::TreePop();
9852         }
9853     }
9854     ImGui::End();
9855 }
9856 
9857 //-----------------------------------------------------------------------------
9858 
9859 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
9860 // Prefer just including imgui_internal.h from your code rather than using this define. If a declaration is missing from imgui_internal.h add it or request it on the github.
9861 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
9862 #include "imgui_user.inl"
9863 #endif
9864 
9865 //-----------------------------------------------------------------------------
9866