1 // dear imgui, v1.49 WIP
2 // (main code and documentation)
3 
4 // See ImGui::ShowTestWindow() in imgui_demo.cpp for demo code.
5 // Newcomers, read 'Programmer guide' below for notes on how to setup 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 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
9 // This library is free but I need your support to sustain development and maintenance.
10 // If you work for a company, please consider financial support, e.g: https://www.patreon.com/imgui
11 
12 /*
13 
14  Index
15  - MISSION STATEMENT
16  - END-USER GUIDE
17  - PROGRAMMER GUIDE (read me!)
18  - API BREAKING CHANGES (read me when you update!)
19  - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
20    - How can I help?
21    - How do I update to a newer version of ImGui?
22    - What is ImTextureID and how do I display an image?
23    - Can I have multiple widgets with the same label? Can I have widget without a label? (Yes) / A primer on the use of labels/IDs in ImGui.
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 load a different font than the default?
27    - How can I load multiple fonts?
28    - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
29  - ISSUES & TODO-LIST
30  - CODE
31 
32 
33  MISSION STATEMENT
34  =================
35 
36  - easy to use to create code-driven and data-driven tools
37  - easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools
38  - easy to hack and improve
39  - minimize screen real-estate usage
40  - minimize setup and maintenance
41  - minimize state storage on user side
42  - portable, minimize dependencies, run on target (consoles, phones, etc.)
43  - 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)
44  - read about immediate-mode gui principles @ http://mollyrocket.com/861, http://mollyrocket.com/forums/index.html
45 
46  Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
47  - doesn't look fancy, doesn't animate
48  - limited layout features, intricate layouts are typically crafted in code
49  - 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.
50 
51 
52  END-USER GUIDE
53  ==============
54 
55  - double-click title bar to collapse window
56  - click upper right corner to close a window, available when 'bool* p_opened' is passed to ImGui::Begin()
57  - click and drag on lower right corner to resize window
58  - click and drag on any empty space to move window
59  - double-click/double-tap on lower right corner grip to auto-fit to content
60  - TAB/SHIFT+TAB to cycle through keyboard editable fields
61  - use mouse wheel to scroll
62  - use CTRL+mouse wheel to zoom window contents (if IO.FontAllowScaling is true)
63  - CTRL+Click on a slider or drag box to input value as text
64  - text editor:
65    - Hold SHIFT or use mouse to select text.
66    - CTRL+Left/Right to word jump
67    - CTRL+Shift+Left/Right to select words
68    - CTRL+A our Double-Click to select all
69    - CTRL+X,CTRL+C,CTRL+V to use OS clipboard
70    - CTRL+Z,CTRL+Y to undo/redo
71    - ESCAPE to revert text to its original value
72    - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
73 
74 
75  PROGRAMMER GUIDE
76  ================
77 
78  - read the FAQ below this section!
79  - 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.
80  - call and read ImGui::ShowTestWindow() for demo code demonstrating most features.
81  - see examples/ folder for standalone sample applications. Prefer reading examples/opengl_example/ first at it is the simplest.
82    you may be able to grab and copy a ready made imgui_impl_*** file from the examples/.
83  - 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).
84 
85  - getting started:
86    - init: call ImGui::GetIO() to retrieve the ImGuiIO structure and fill the fields marked 'Settings'.
87    - init: call io.Fonts->GetTexDataAsRGBA32(...) and load the font texture pixels into graphics memory.
88    - every frame:
89       1/ in your mainloop or right after you got your keyboard/mouse info, call ImGui::GetIO() and fill the fields marked 'Input'
90       2/ call ImGui::NewFrame() as early as you can!
91       3/ use any ImGui function you want between NewFrame() and Render()
92       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.
93          (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.)
94    - all rendering information are stored into command-lists until ImGui::Render() is called.
95    - ImGui never touches or know about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide.
96    - 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.
97    - refer to the examples applications in the examples/ folder for instruction on how to setup your code.
98    - a typical application skeleton may be:
99 
100         // Application init
101         ImGuiIO& io = ImGui::GetIO();
102         io.DisplaySize.x = 1920.0f;
103         io.DisplaySize.y = 1280.0f;
104         io.IniFilename = "imgui.ini";
105         io.RenderDrawListsFn = my_render_function;  // Setup a render function, or set to NULL and call GetDrawData() after Render() to access the render data.
106         // TODO: Fill others settings of the io structure
107 
108         // Load texture atlas
109         // There is a default font so you don't need to care about choosing a font yet
110         unsigned char* pixels;
111         int width, height;
112         io.Fonts->GetTexDataAsRGBA32(pixels, &width, &height);
113         // TODO: At this points you've got a texture pointed to by 'pixels' and you need to upload that your your graphic system
114         // TODO: Store your texture pointer/identifier (whatever your engine uses) in 'io.Fonts->TexID'
115 
116         // Application main loop
117         while (true)
118         {
119             // 1) get low-level inputs (e.g. on Win32, GetKeyboardState(), or poll your events, etc.)
120             // TODO: fill all fields of IO structure and call NewFrame
121             ImGuiIO& io = ImGui::GetIO();
122             io.DeltaTime = 1.0f/60.0f;
123             io.MousePos = mouse_pos;
124             io.MouseDown[0] = mouse_button_0;
125             io.MouseDown[1] = mouse_button_1;
126             io.KeysDown[i] = ...
127 
128             // 2) call NewFrame(), after this point you can use ImGui::* functions anytime
129             ImGui::NewFrame();
130 
131             // 3) most of your application code here
132             ImGui::Begin("My window");
133             ImGui::Text("Hello, world.");
134             ImGui::End();
135             MyGameUpdate(); // may use ImGui functions
136             MyGameRender(); // may use ImGui functions
137 
138             // 4) render & swap video buffers
139             ImGui::Render();
140             SwapBuffers();
141         }
142 
143    - after calling ImGui::NewFrame() you can read back flags from the IO structure to tell how ImGui intends to use your inputs.
144      When 'io.WantCaptureMouse' or 'io.WantCaptureKeyboard' flags are set you may want to discard/hide the inputs from the rest of your application.
145      When 'io.WantInputsCharacters' is set to may want to notify your OS to popup an on-screen keyboard, if available.
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  - 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).
156  - 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)
157  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
158  - 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.
159  - 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.
160  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
161  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
162  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
163                        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.
164                        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!
165  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
166  - 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.
167  - 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
168  - 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.
169                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
170  - 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.
171                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
172                      - 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.
173                      - the signature of the io.RenderDrawListsFn handler has changed!
174                             ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
175                        became:
176                             ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
177                               argument   'cmd_lists'        -> 'draw_data->CmdLists'
178                               argument   'cmd_lists_count'  -> 'draw_data->CmdListsCount'
179                               ImDrawList 'commands'         -> 'CmdBuffer'
180                               ImDrawList 'vtx_buffer'       -> 'VtxBuffer'
181                               ImDrawList  n/a               -> 'IdxBuffer' (new)
182                               ImDrawCmd  'vtx_count'        -> 'ElemCount'
183                               ImDrawCmd  'clip_rect'        -> 'ClipRect'
184                               ImDrawCmd  'user_callback'    -> 'UserCallback'
185                               ImDrawCmd  'texture_id'       -> 'TextureId'
186                      - 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.
187                      - 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!
188                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
189  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
190  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
191  - 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.
192  - 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
193  - 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!
194  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
195  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
196  - 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.
197  - 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 "opened" state of a popup. BeginPopup() returns true if the popup is opened.
198  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
199  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function (will obsolete).
200  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
201  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
202  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
203  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function (will obsolete).
204  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
205  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function (will obsolete).
206  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
207  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function (will obsolete).
208  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
209  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
210  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
211  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
212  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
213  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
214  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
215               (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
216                        this sequence:
217                            const void* png_data;
218                            unsigned int png_size;
219                            ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size);
220                            // <Copy to GPU>
221                        became:
222                            unsigned char* pixels;
223                            int width, height;
224                            io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
225                            // <Copy to GPU>
226                            io.Fonts->TexID = (your_texture_identifier);
227                        you now have much more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
228                        it is now recommended that you sample the font texture with bilinear interpolation.
229               (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
230               (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
231               (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
232  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
233  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
234  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
235  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
236  - 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)
237  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
238  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
239  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
240  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
241  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
242  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
243 
244 
245  FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
246  ======================================
247 
248  Q: How can I help?
249  A: - If you are experienced enough with ImGui and with C/C++, look at the todo list and see how you want/can help!
250     - Become a Patron/donate. Convince your company to become a Patron or provide serious funding for development time.
251 
252  Q: How do I update to a newer version of ImGui?
253  A: Overwrite the following files:
254       imgui.cpp
255       imgui.h
256       imgui_demo.cpp
257       imgui_draw.cpp
258       imgui_internal.h
259       stb_rect_pack.h
260       stb_textedit.h
261       stb_truetype.h
262     Don't overwrite imconfig.h if you have made modification to your copy.
263     Check the "API BREAKING CHANGES" sections for a list of occasional API breaking changes. If you have a problem with a function, search for its name
264     in the code, there will likely be a comment about it. Please report any issue to the GitHub page!
265 
266 
267  Q: What is ImTextureID and how do I display an image?
268  A: ImTextureID is a void* used to pass renderer-agnostic texture references around until it hits your render function.
269     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!
270     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.
271     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.
272     Refer to examples applications, where each renderer (in a imgui_impl_xxxx.cpp file) is treating ImTextureID as a different thing.
273     (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!)
274     To display a custom image/texture within an ImGui window, you may use ImGui::Image(), ImGui::ImageButton(), ImDrawList::AddImage() functions.
275     ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use.
276     It is your responsibility to get textures uploaded to your GPU.
277 
278  Q: Can I have multiple widgets with the same label? Can I have widget without a label? (Yes)
279  A: Yes. A primer on the use of labels/IDs in ImGui..
280 
281    - Elements that are not clickable, such as Text() items don't need an ID.
282 
283    - Interactive widgets require state to be carried over multiple frames (most typically ImGui often needs to remember what is the "active" widget).
284      to do so they need an unique ID. unique ID are typically derived from a string label, an integer index or a pointer.
285 
286        Button("OK");        // Label = "OK",     ID = hash of "OK"
287        Button("Cancel");    // Label = "Cancel", ID = hash of "Cancel"
288 
289    - 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
290      or in two different locations of a tree.
291 
292    - If you have a same ID twice in the same location, you'll have a conflict:
293 
294        Button("OK");
295        Button("OK");           // ID collision! Both buttons will be treated as the same.
296 
297      Fear not! this is easy to solve and there are many ways to solve it!
298 
299    - When passing a label you can optionally specify extra unique ID information within string itself. This helps solving the simpler collision cases.
300      use "##" to pass a complement to the ID that won't be visible to the end-user:
301 
302        Button("Play");         // Label = "Play",   ID = hash of "Play"
303        Button("Play##foo1");   // Label = "Play",   ID = hash of "Play##foo1" (different from above)
304        Button("Play##foo2");   // Label = "Play",   ID = hash of "Play##foo2" (different from above)
305 
306    - If you want to completely hide the label, but still need an ID:
307 
308        Checkbox("##On", &b);   // Label = "",       ID = hash of "##On" (no label!)
309 
310    - Occasionally/rarely you might want change a label while preserving a constant ID. This allows you to animate labels.
311      For example you may want to include varying information in a window title bar (and windows are uniquely identified by their ID.. obviously)
312      Use "###" to pass a label that isn't part of ID:
313 
314        Button("Hello###ID";   // Label = "Hello",  ID = hash of "ID"
315        Button("World###ID";   // Label = "World",  ID = hash of "ID" (same as above)
316 
317        sprintf(buf, "My game (%f FPS)###MyGame");
318        Begin(buf);            // Variable label,   ID = hash of "MyGame"
319 
320    - Use PushID() / PopID() to create scopes and avoid ID conflicts within the same Window.
321      This is the most convenient way of distinguishing ID if you are iterating and creating many UI elements.
322      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!
323 
324        for (int i = 0; i < 100; i++)
325        {
326          PushID(i);
327          Button("Click");   // Label = "Click",  ID = hash of integer + "label" (unique)
328          PopID();
329        }
330 
331        for (int i = 0; i < 100; i++)
332        {
333          MyObject* obj = Objects[i];
334          PushID(obj);
335          Button("Click");   // Label = "Click",  ID = hash of pointer + "label" (unique)
336          PopID();
337        }
338 
339        for (int i = 0; i < 100; i++)
340        {
341          MyObject* obj = Objects[i];
342          PushID(obj->Name);
343          Button("Click");   // Label = "Click",  ID = hash of string + "label" (unique)
344          PopID();
345        }
346 
347    - More example showing that you can stack multiple prefixes into the ID stack:
348 
349        Button("Click");     // Label = "Click",  ID = hash of "Click"
350        PushID("node");
351        Button("Click");     // Label = "Click",  ID = hash of "node" + "Click"
352          PushID(my_ptr);
353            Button("Click"); // Label = "Click",  ID = hash of "node" + ptr + "Click"
354          PopID();
355        PopID();
356 
357    - Tree nodes implicitly creates a scope for you by calling PushID().
358 
359        Button("Click");     // Label = "Click",  ID = hash of "Click"
360        if (TreeNode("node"))
361        {
362          Button("Click");   // Label = "Click",  ID = hash of "node" + "Click"
363          TreePop();
364        }
365 
366    - When working with trees, ID are used to preserve the opened/closed state of each tree node.
367      Depending on your use cases you may want to use strings, indices or pointers as ID.
368       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.
369       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!
370 
371  Q: I integrated ImGui in my engine and the text or lines are blurry..
372  A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
373     Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
374 
375  Q: I integrated ImGui in my engine and some elements are clipping or disappearing when I move windows around..
376  A: Most likely you are mishandling the clipping rectangles in your render function. Rectangles provided by ImGui are defined as (x1,y1,x2,y2) and NOT as (x1,y1,width,height).
377 
378  Q: How can I load a different font than the default? (default is an embedded version of ProggyClean.ttf, rendered at size 13)
379  A: Use the font atlas to load the TTF file you want:
380 
381       ImGuiIO& io = ImGui::GetIO();
382       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
383       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
384 
385  Q: How can I load multiple fonts?
386  A: Use the font atlas to pack them into a single texture:
387     (Read extra_fonts/README.txt and the code in ImFontAtlas for more details.)
388 
389       ImGuiIO& io = ImGui::GetIO();
390       ImFont* font0 = io.Fonts->AddFontDefault();
391       ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
392       ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
393       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
394       // the first loaded font gets used by default
395       // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
396 
397       // Options
398       ImFontConfig config;
399       config.OversampleH = 3;
400       config.OversampleV = 1;
401       config.GlyphExtraSpacing.x = 1.0f;
402       io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config);
403 
404       // Combine multiple fonts into one
405       ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
406       ImFontConfig config;
407       config.MergeMode = true;
408       io.Fonts->AddFontDefault();
409       io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges);
410       io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese());
411 
412  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
413  A: When loading a font, pass custom Unicode ranges to specify the glyphs to load. ImGui will support UTF-8 encoding across the board.
414     Character input depends on you passing the right character code to io.AddInputCharacter(). The example applications do that.
415 
416       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese());  // Load Japanese characters
417       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
418       io.ImeWindowHandle = MY_HWND;      // To input using Microsoft IME, give ImGui the hwnd of your application
419 
420  - 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.
421  - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug"
422  - 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)
423  - tip: you can call Render() multiple times (e.g for VR renders).
424  - tip: call and read the ShowTestWindow() code in imgui_demo.cpp for more example of how to use ImGui!
425 
426 
427  ISSUES & TODO-LIST
428  ==================
429  Issue numbers (#) refer to github issues listed at https://github.com/ocornut/imgui/issues
430  The list below consist mostly of notes of things to do before they are requested/discussed by users (at that point it usually happens on the github)
431 
432  - doc: add a proper documentation+regression testing system (#435)
433  - window: maximum window size settings (per-axis). for large popups in particular user may not want the popup to fill all space.
434  - 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.
435  - window: calling SetNextWindowSize() every frame with <= 0 doesn't do anything, may be useful to allow (particularly when used for a single axis).
436  - window: auto-fit feedback loop when user relies on any dynamic layout (window width multiplier, column) appears weird to end-user. clarify.
437  - window: allow resizing of child windows (possibly given min/max for each axis?)
438  - window: background options for child windows, border option (disable rounding)
439  - 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)
440  - window: resizing from any sides? + mouse cursor directives for app.
441 !- window: begin with *p_opened == false should return false.
442  - window: get size/pos helpers given names (see discussion in #249)
443  - window: a collapsed window can be stuck behind the main menu bar?
444  - window: when window is small, prioritize resize button over close button.
445  - window: detect extra End() call that pop the "Debug" window out and assert at call site instead of later.
446  - window/tooltip: allow to set the width of a tooltip to allow TextWrapped() etc. while keeping the height automatic.
447  - window: increase minimum size of a window with menus or fix the menu rendering so that it doesn't look odd.
448  - 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).
449 !- scrolling: allow immediately effective change of scroll if we haven't appended items yet
450  - splitter/separator: formalize the splitter idiom into an official api (we want to handle n-way split) (#319)
451  - widgets: display mode: widget-label, label-widget (aligned on column or using fixed size), label-newline-tab-widget etc.
452  - widgets: clean up widgets internal toward exposing everything.
453  - widgets: add disabled and read-only modes (#211)
454  - main: considering adding an Init() function? some constructs are awkward in the implementation because of the lack of them.
455 !- main: make it so that a frame with no window registered won't refocus every window on subsequent frames (~bump LastFrameActive of all windows).
456  - 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
457  - main: IsItemHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode?
458  - input text: clean up the mess caused by converting UTF-8 <> wchar. the code is rather inefficient right now.
459  - input text: reorganize event handling, allow CharFilter to modify buffers, allow multiple events? (#541)
460  - input text: flag to disable live update of the user buffer (also applies to float/int text input)
461  - input text: resize behavior - field could stretch when being edited? hover tooltip shows more text?
462  - input text: add ImGuiInputTextFlags_EnterToApply? (off #218)
463  - 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).
464  - input text multi-line: way to dynamically grow the buffer without forcing the user to initially allocate for worse case (follow up on #200)
465  - input text multi-line: line numbers? status bar? (follow up on #200)
466  - input text: allow centering/positioning text so that ctrl+clicking Drag or Slider keeps the textual value at the same pixel position.
467  - input number: optional range min/max for Input*() functions
468  - input number: holding [-]/[+] buttons could increase the step speed non-linearly (or user-controlled)
469  - input number: use mouse wheel to step up/down
470  - input number: applying arithmetics ops (+,-,*,/) messes up with text edit undo stack.
471  - button: provide a button that looks framed.
472  - text: proper alignment options
473  - image/image button: misalignment on padded/bordered button?
474  - 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?
475  - layout: horizontal layout helper (#97)
476  - layout: horizontal flow until no space left (#404)
477  - layout: more generic alignment state (left/right/centered) for single items?
478  - layout: clean up the InputFloatN/SliderFloatN/ColorEdit4 layout code. item width should include frame padding.
479  - layout: BeginGroup() needs a border option.
480  - columns: declare column set (each column: fixed size, %, fill, distribute default size among fills) (#513, #125)
481  - columns: add a conditional parameter to SetColumnOffset() (#513, #125)
482  - columns: separator function or parameter that works within the column (currently Separator() bypass all columns) (#125)
483  - columns: columns header to act as button (~sort op) and allow resize/reorder (#513, #125)
484  - columns: user specify columns size (#513, #125)
485  - columns: flag to add horizontal separator above/below?
486  - columns/layout: setup minimum line height (equivalent of automatically calling AlignFirstTextHeightToWidgets)
487  - combo: sparse combo boxes (via function call?) / iterators
488  - combo: contents should extends to fit label if combo widget is small
489  - combo/listbox: keyboard control. need InputText-like non-active focus + key handling. considering keyboard for custom listbox (pr #203)
490  - listbox: multiple selection
491  - listbox: user may want to initial scroll to focus on the one selected value?
492  - listbox: keyboard navigation.
493  - listbox: scrolling should track modified selection.
494 !- 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)
495  - popups: add variant using global identifier similar to Begin/End (#402)
496  - popups: border options. richer api like BeginChild() perhaps? (#197)
497  - tooltip: tooltip that doesn't fit in entire screen seems to lose their "last prefered button" and may teleport when moving mouse
498  - menus: local shortcuts, global shortcuts (#456, #126)
499  - menus: icons
500  - menus: menubars: some sort of priority / effect of main menu-bar on desktop size?
501  - menus: calling BeginMenu() twice with a same name doesn't seem to append nicely
502  - statusbar: add a per-window status bar helper similar to what menubar does.
503  - tabs (#261, #351)
504  - separator: separator on the initial position of a window is not visible (cursorpos.y <= clippos.y)
505 !- color: the color helpers/typing is a mess and needs sorting out.
506  - color: add a better color picker (#346)
507  - node/graph editor (#306)
508  - pie menus patterns (#434)
509  - drag'n drop, dragging helpers (carry dragging info, visualize drag source before clicking, drop target, etc.) (#143, #479)
510  - plot: PlotLines() should use the polygon-stroke facilities (currently issues with averaging normals)
511  - plot: make it easier for user to draw extra stuff into the graph (e.g: draw basis, highlight certain points, 2d plots, multiple plots)
512  - plot: "smooth" automatic scale over time, user give an input 0.0(full user scale) 1.0(full derived from value)
513  - 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)
514  - slider: allow using the [-]/[+] buttons used by InputFloat()/InputInt()
515  - slider: initial absolute click is imprecise. change to relative movement slider (same as scrollbar).
516  - slider: add dragging-based widgets to edit values with mouse (on 2 axises), saving screen real-estate.
517  - slider: tint background based on value (e.g. v_min -> v_max, or use 0.0f either side of the sign)
518  - slider & drag: int data passing through a float
519  - drag float: up/down axis
520  - drag float: added leeway on edge (e.g. a few invisible steps past the clamp limits)
521  - tree node / optimization: avoid formatting when clipped.
522  - tree node: clarify spacing, perhaps provide API to query exact spacing. provide API to draw the primitive. same with Bullet().
523  - tree node: tree-node/header right-most side doesn't take account of horizontal scrolling.
524  - tree node: add treenode/treepush int variants? because (void*) cast from int warns on some platforms/settings
525  - tree node: try to apply scrolling at time of TreePop() if node was just opened and end of node is past scrolling limits?
526  - tree node / selectable render mismatch which is visible if you use them both next to each other (e.g. cf. property viewer)
527  - textwrapped: figure out better way to use TextWrapped() in an always auto-resize context (tooltip, etc.) (git issue #249)
528  - settings: write more decent code to allow saving/loading new fields
529  - settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file
530  - style: add window shadows.
531  - style/optimization: store rounded corners in texture to use 1 quad per corner (filled and wireframe) to lower the cost of rounding.
532  - style: color-box not always square?
533  - 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.
534  - style: try to make PushStyleVar() more robust to incorrect parameters (to be more friendly to edit & continues situation).
535  - style/opt: PopStyleVar could be optimized by having GetStyleVar returns the type, using a table mapping stylevar enum to data type.
536  - style: global scale setting.
537  - style: WindowPadding needs to be EVEN needs the 0.5 multiplier probably have a subtle effect on clip rectangle
538  - text: simple markup language for color change?
539  - font: dynamic font atlas to avoid baking huge ranges into bitmap and make scaling easier.
540  - font: helper to add glyph redirect/replacements (e.g. redirect alternate apostrophe unicode code points to ascii one, etc.)
541  - log: LogButtons() options for specifying depth and/or hiding depth slider
542  - log: have more control over the log scope (e.g. stop logging when leaving current tree node scope)
543  - log: be able to log anything (e.g. right-click on a window/tree-node, shows context menu? log into tty/file/clipboard)
544  - 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.
545  - filters: set a current filter that tree node can automatically query to hide themselves
546  - filters: handle wildcards (with implicit leading/trailing *), regexps
547  - 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)
548 !- keyboard: tooltip & combo boxes are messing up / not honoring keyboard tabbing
549  - keyboard: full keyboard navigation and focus. (#323)
550  - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame)
551  - input: rework IO system to be able to pass actual ordered/timestamped events.
552  - input: allow to decide and pass explicit double-clicks (e.g. for windows by the CS_DBLCLKS style).
553  - input: support track pad style scrolling & slider edit.
554  - misc: provide a way to compile out the entire implementation while providing a dummy API (e.g. #define IMGUI_DUMMY_IMPL)
555  - misc: double-clicking on title bar to minimize isn't consistent, perhaps move to single-click on left-most collapse icon?
556  - misc: provide HoveredTime and ActivatedTime to ease the creation of animations.
557  - 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)
558  - style editor: color child window height expressed in multiple of line height.
559  - remote: make a system like RemoteImGui first-class citizen/project (#75)
560 !- demo: custom render demo pushes a clipping rectangle past parent window bounds. expose ImGui::PushClipRect() from imgui_internal.h?
561  - drawlist: end-user probably can't call Clear() directly because we expect a texture to be pushed in the stack.
562  - examples: directx9: save/restore device state more thoroughly.
563  - examples: window minimize, maximize (#583)
564  - optimization: use another hash function than crc32, e.g. FNV1a
565  - 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)?
566  - optimization: turn some the various stack vectors into statically-sized arrays
567  - optimization: better clipping for multi-component widgets
568 */
569 
570 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
571 #define _CRT_SECURE_NO_WARNINGS
572 #endif
573 
574 #include "imgui.h"
575 #define IMGUI_DEFINE_MATH_OPERATORS
576 #define IMGUI_DEFINE_PLACEMENT_NEW
577 #include "imgui_internal.h"
578 
579 #include <ctype.h>      // toupper, isprint
580 #include <math.h>       // sqrtf, fabsf, fmodf, powf, cosf, sinf, floorf, ceilf
581 #include <stdlib.h>     // NULL, malloc, free, qsort, atoi
582 #include <stdio.h>      // vsnprintf, sscanf, printf
583 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
584 #include <stddef.h>     // intptr_t
585 #else
586 #include <stdint.h>     // intptr_t
587 #endif
588 
589 #ifdef _MSC_VER
590 #pragma warning (disable: 4127) // condition expression is constant
591 #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
592 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
593 #define snprintf _snprintf
594 #endif
595 
596 // Clang warnings with -Weverything
597 #ifdef __clang__
598 #pragma clang diagnostic ignored "-Wold-style-cast"         // warning : use of old-style cast                              // yes, they are more terse.
599 #pragma clang diagnostic ignored "-Wfloat-equal"            // warning : comparing floating point with == or != is unsafe   // storing and comparing against same constants ok.
600 #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.
601 #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.
602 #pragma clang diagnostic ignored "-Wglobal-constructors"    // warning : declaration requires a global destructor           // similar to above, not sure what the exact difference it.
603 #pragma clang diagnostic ignored "-Wsign-conversion"        // warning : implicit conversion changes signedness             //
604 #pragma clang diagnostic ignored "-Wmissing-noreturn"       // warning : function xx could be declared with attribute 'noreturn' warning    // GetDefaultFontData() asserts which some implementation makes it never return.
605 #pragma clang diagnostic ignored "-Wdeprecated-declarations"// warning : 'xx' is deprecated: The POSIX name for this item.. // for strdup used in demo code (so user can copy & paste the code)
606 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int'
607 #endif
608 #ifdef __GNUC__
609 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
610 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
611 #endif
612 
613 //-------------------------------------------------------------------------
614 // Forward Declarations
615 //-------------------------------------------------------------------------
616 
617 static void             LogRenderedText(const ImVec2& ref_pos, const char* text, const char* text_end = NULL);
618 
619 static void             PushMultiItemsWidths(int components, float w_full = 0.0f);
620 static float            GetDraggedColumnOffset(int column_index);
621 
622 static bool             IsKeyPressedMap(ImGuiKey key, bool repeat = true);
623 
624 static void             SetCurrentFont(ImFont* font);
625 static void             SetCurrentWindow(ImGuiWindow* window);
626 static void             SetWindowScrollY(ImGuiWindow* window, float new_scroll_y);
627 static void             SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond);
628 static void             SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond);
629 static void             SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiSetCond cond);
630 static ImGuiWindow*     FindHoveredWindow(ImVec2 pos, bool excluding_childs);
631 static ImGuiWindow*     CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
632 static inline bool      IsWindowContentHoverable(ImGuiWindow* window);
633 static void             ClearSetNextWindowData();
634 static void             CheckStacksSize(ImGuiWindow* window, bool write);
635 static void             Scrollbar(ImGuiWindow* window, bool horizontal);
636 static bool             CloseWindowButton(bool* p_opened);
637 
638 static void             AddDrawListToRenderList(ImVector<ImDrawList*>& out_render_list, ImDrawList* draw_list);
639 static void             AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiWindow* window);
640 static void             AddWindowToSortedBuffer(ImVector<ImGuiWindow*>& out_sorted_windows, ImGuiWindow* window);
641 
642 static ImGuiIniData*    FindWindowSettings(const char* name);
643 static ImGuiIniData*    AddWindowSettings(const char* name);
644 static void             LoadSettings();
645 static void             SaveSettings();
646 static void             MarkSettingsDirty();
647 
648 static void             PushColumnClipRect(int column_index = -1);
649 static ImRect           GetVisibleRect();
650 
651 static bool             BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags);
652 static void             CloseInactivePopups();
653 static void             ClosePopupToLevel(int remaining);
654 static void             ClosePopup(ImGuiID id);
655 static bool             IsPopupOpen(ImGuiID id);
656 static ImGuiWindow*     GetFrontMostModalRootWindow();
657 static ImVec2           FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& rect_to_avoid);
658 
659 static bool             InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data);
660 static int              InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
661 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);
662 
663 static inline void      DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size);
664 static inline void      DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size);
665 static void             DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2);
666 static bool             DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format);
667 
668 //-----------------------------------------------------------------------------
669 // Platform dependent default implementations
670 //-----------------------------------------------------------------------------
671 
672 static const char*      GetClipboardTextFn_DefaultImpl();
673 static void             SetClipboardTextFn_DefaultImpl(const char* text);
674 static void             ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
675 
676 //-----------------------------------------------------------------------------
677 // Context
678 //-----------------------------------------------------------------------------
679 
680 // We access everything through this pointer (always assumed to be != NULL)
681 // You can swap the pointer to a different context by calling ImGui::SetInternalState()
682 static ImGuiState       GImDefaultState;
683 ImGuiState*             GImGui = &GImDefaultState;
684 
685 // Statically allocated default font atlas. This is merely a maneuver to keep ImFontAtlas definition at the bottom of the .h file (otherwise it'd be inside ImGuiIO)
686 // Also we wouldn't be able to new() one at this point, before users may define IO.MemAllocFn.
687 static ImFontAtlas      GImDefaultFontAtlas;
688 
689 //-----------------------------------------------------------------------------
690 // User facing structures
691 //-----------------------------------------------------------------------------
692 
ImGuiStyle()693 ImGuiStyle::ImGuiStyle()
694 {
695     Alpha                   = 1.0f;             // Global alpha applies to everything in ImGui
696     WindowPadding           = ImVec2(8,8);      // Padding within a window
697     WindowMinSize           = ImVec2(32,32);    // Minimum window size
698     WindowRounding          = 9.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows
699     WindowTitleAlign        = ImGuiAlign_Left;  // Alignment for title bar text
700     ChildWindowRounding     = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
701     FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
702     FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
703     ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
704     ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
705     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!
706     IndentSpacing           = 22.0f;            // Horizontal spacing when e.g. entering a tree node
707     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns
708     ScrollbarSize           = 16.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
709     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar
710     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar
711     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
712     DisplayWindowPadding    = ImVec2(22,22);    // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows.
713     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.
714     AntiAliasedLines        = true;             // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
715     AntiAliasedShapes       = true;             // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
716     CurveTessellationTol    = 1.25f;            // Tessellation tolerance. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
717 
718     Colors[ImGuiCol_Text]                   = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
719     Colors[ImGuiCol_TextDisabled]           = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
720     Colors[ImGuiCol_WindowBg]               = ImVec4(0.00f, 0.00f, 0.00f, 0.70f);
721     Colors[ImGuiCol_ChildWindowBg]          = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
722     Colors[ImGuiCol_PopupBg]                = ImVec4(0.05f, 0.05f, 0.10f, 0.90f);
723     Colors[ImGuiCol_Border]                 = ImVec4(0.70f, 0.70f, 0.70f, 0.65f);
724     Colors[ImGuiCol_BorderShadow]           = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
725     Colors[ImGuiCol_FrameBg]                = ImVec4(0.80f, 0.80f, 0.80f, 0.30f);   // Background of checkbox, radio button, plot, slider, text input
726     Colors[ImGuiCol_FrameBgHovered]         = ImVec4(0.90f, 0.80f, 0.80f, 0.40f);
727     Colors[ImGuiCol_FrameBgActive]          = ImVec4(0.90f, 0.65f, 0.65f, 0.45f);
728     Colors[ImGuiCol_TitleBg]                = ImVec4(0.50f, 0.50f, 1.00f, 0.45f);
729     Colors[ImGuiCol_TitleBgCollapsed]       = ImVec4(0.40f, 0.40f, 0.80f, 0.20f);
730     Colors[ImGuiCol_TitleBgActive]          = ImVec4(0.50f, 0.50f, 1.00f, 0.55f);
731     Colors[ImGuiCol_MenuBarBg]              = ImVec4(0.40f, 0.40f, 0.55f, 0.80f);
732     Colors[ImGuiCol_ScrollbarBg]            = ImVec4(0.20f, 0.25f, 0.30f, 0.60f);
733     Colors[ImGuiCol_ScrollbarGrab]          = ImVec4(0.40f, 0.40f, 0.80f, 0.30f);
734     Colors[ImGuiCol_ScrollbarGrabHovered]   = ImVec4(0.40f, 0.40f, 0.80f, 0.40f);
735     Colors[ImGuiCol_ScrollbarGrabActive]    = ImVec4(0.80f, 0.50f, 0.50f, 0.40f);
736     Colors[ImGuiCol_ComboBg]                = ImVec4(0.20f, 0.20f, 0.20f, 0.99f);
737     Colors[ImGuiCol_CheckMark]              = ImVec4(0.90f, 0.90f, 0.90f, 0.50f);
738     Colors[ImGuiCol_SliderGrab]             = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
739     Colors[ImGuiCol_SliderGrabActive]       = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
740     Colors[ImGuiCol_Button]                 = ImVec4(0.67f, 0.40f, 0.40f, 0.60f);
741     Colors[ImGuiCol_ButtonHovered]          = ImVec4(0.67f, 0.40f, 0.40f, 1.00f);
742     Colors[ImGuiCol_ButtonActive]           = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
743     Colors[ImGuiCol_Header]                 = ImVec4(0.40f, 0.40f, 0.90f, 0.45f);
744     Colors[ImGuiCol_HeaderHovered]          = ImVec4(0.45f, 0.45f, 0.90f, 0.80f);
745     Colors[ImGuiCol_HeaderActive]           = ImVec4(0.53f, 0.53f, 0.87f, 0.80f);
746     Colors[ImGuiCol_Column]                 = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
747     Colors[ImGuiCol_ColumnHovered]          = ImVec4(0.70f, 0.60f, 0.60f, 1.00f);
748     Colors[ImGuiCol_ColumnActive]           = ImVec4(0.90f, 0.70f, 0.70f, 1.00f);
749     Colors[ImGuiCol_ResizeGrip]             = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
750     Colors[ImGuiCol_ResizeGripHovered]      = ImVec4(1.00f, 1.00f, 1.00f, 0.60f);
751     Colors[ImGuiCol_ResizeGripActive]       = ImVec4(1.00f, 1.00f, 1.00f, 0.90f);
752     Colors[ImGuiCol_CloseButton]            = ImVec4(0.50f, 0.50f, 0.90f, 0.50f);
753     Colors[ImGuiCol_CloseButtonHovered]     = ImVec4(0.70f, 0.70f, 0.90f, 0.60f);
754     Colors[ImGuiCol_CloseButtonActive]      = ImVec4(0.70f, 0.70f, 0.70f, 1.00f);
755     Colors[ImGuiCol_PlotLines]              = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
756     Colors[ImGuiCol_PlotLinesHovered]       = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
757     Colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
758     Colors[ImGuiCol_PlotHistogramHovered]   = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
759     Colors[ImGuiCol_TextSelectedBg]         = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
760     Colors[ImGuiCol_ModalWindowDarkening]   = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
761 }
762 
ImGuiIO()763 ImGuiIO::ImGuiIO()
764 {
765     // Most fields are initialized with zero
766     memset(this, 0, sizeof(*this));
767 
768     DisplaySize = ImVec2(-1.0f, -1.0f);
769     DeltaTime = 1.0f/60.0f;
770     IniSavingRate = 5.0f;
771     IniFilename = "imgui.ini";
772     LogFilename = "imgui_log.txt";
773     Fonts = &GImDefaultFontAtlas;
774     FontGlobalScale = 1.0f;
775     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
776     MousePos = ImVec2(-1,-1);
777     MousePosPrev = ImVec2(-1,-1);
778     MouseDoubleClickTime = 0.30f;
779     MouseDoubleClickMaxDist = 6.0f;
780     MouseDragThreshold = 6.0f;
781     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++)
782         MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
783     for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++)
784         KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
785     for (int i = 0; i < ImGuiKey_COUNT; i++)
786         KeyMap[i] = -1;
787     KeyRepeatDelay = 0.250f;
788     KeyRepeatRate = 0.050f;
789     UserData = NULL;
790 
791     // User functions
792     RenderDrawListsFn = NULL;
793     MemAllocFn = malloc;
794     MemFreeFn = free;
795     GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;   // Platform dependent default implementations
796     SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
797     ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
798 
799     // Set OS X style defaults based on __APPLE__ compile time flag
800 #ifdef __APPLE__
801     WordMovementUsesAltKey = true;      // OS X style: Text editing cursor movement using Alt instead of Ctrl
802     ShortcutsUseSuperKey = true;        // OS X style: Shortcuts using Cmd/Super instead of Ctrl
803     DoubleClickSelectsWord = true;      // OS X style: Double click selects by word instead of selecting whole text
804     MultiSelectUsesSuperKey = true;     // OS X style: Multi-selection in lists uses Cmd/Super instead of Ctrl
805 #endif
806 }
807 
808 // Pass in translated ASCII characters for text input.
809 // - with glfw you can get those from the callback set in glfwSetCharCallback()
810 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(ImWchar c)811 void ImGuiIO::AddInputCharacter(ImWchar c)
812 {
813     const int n = ImStrlenW(InputCharacters);
814     if (n + 1 < IM_ARRAYSIZE(InputCharacters))
815     {
816         InputCharacters[n] = c;
817         InputCharacters[n+1] = '\0';
818     }
819 }
820 
AddInputCharactersUTF8(const char * utf8_chars)821 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
822 {
823     // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more
824     const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar);
825     ImWchar wchars[wchars_buf_len];
826     ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL);
827     for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++)
828         AddInputCharacter(wchars[i]);
829 }
830 
831 //-----------------------------------------------------------------------------
832 // HELPERS
833 //-----------------------------------------------------------------------------
834 
835 #define IM_F32_TO_INT8(_VAL)  ((int)((_VAL) * 255.0f + 0.5f))
836 
837 #define IM_INT_MIN  (-2147483647-1)
838 #define IM_INT_MAX  (2147483647)
839 
840 // Play it nice with Windows users. Notepad in 2015 still doesn't display text data with Unix-style \n.
841 #ifdef _WIN32
842 #define IM_NEWLINE "\r\n"
843 #else
844 #define IM_NEWLINE "\n"
845 #endif
846 
ImIsPointInTriangle(const ImVec2 & p,const ImVec2 & a,const ImVec2 & b,const ImVec2 & c)847 bool ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, const ImVec2& c)
848 {
849     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
850     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
851     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
852     return ((b1 == b2) && (b2 == b3));
853 }
854 
ImStricmp(const char * str1,const char * str2)855 int ImStricmp(const char* str1, const char* str2)
856 {
857     int d;
858     while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
859     return d;
860 }
861 
ImStrnicmp(const char * str1,const char * str2,int count)862 int ImStrnicmp(const char* str1, const char* str2, int count)
863 {
864     int d = 0;
865     while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
866     return d;
867 }
868 
ImStrdup(const char * str)869 char* ImStrdup(const char *str)
870 {
871     size_t len = strlen(str) + 1;
872     void* buff = ImGui::MemAlloc(len);
873     return (char*)memcpy(buff, (const void*)str, len);
874 }
875 
ImStrlenW(const ImWchar * str)876 int ImStrlenW(const ImWchar* str)
877 {
878     int n = 0;
879     while (*str++) n++;
880     return n;
881 }
882 
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)883 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
884 {
885     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
886         buf_mid_line--;
887     return buf_mid_line;
888 }
889 
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)890 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
891 {
892     if (!needle_end)
893         needle_end = needle + strlen(needle);
894 
895     const char un0 = (char)toupper(*needle);
896     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
897     {
898         if (toupper(*haystack) == un0)
899         {
900             const char* b = needle + 1;
901             for (const char* a = haystack + 1; b < needle_end; a++, b++)
902                 if (toupper(*a) != toupper(*b))
903                     break;
904             if (b == needle_end)
905                 return haystack;
906         }
907         haystack++;
908     }
909     return NULL;
910 }
911 
ImFormatString(char * buf,int buf_size,const char * fmt,...)912 int ImFormatString(char* buf, int buf_size, const char* fmt, ...)
913 {
914     va_list args;
915     va_start(args, fmt);
916     int w = vsnprintf(buf, buf_size, fmt, args);
917     va_end(args);
918     buf[buf_size-1] = 0;
919     return (w == -1) ? buf_size : w;
920 }
921 
ImFormatStringV(char * buf,int buf_size,const char * fmt,va_list args)922 int ImFormatStringV(char* buf, int buf_size, const char* fmt, va_list args)
923 {
924     int w = vsnprintf(buf, buf_size, fmt, args);
925     buf[buf_size-1] = 0;
926     return (w == -1) ? buf_size : w;
927 }
928 
929 // Pass data_size==0 for zero-terminated strings
930 // 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)931 ImU32 ImHash(const void* data, int data_size, ImU32 seed)
932 {
933     static ImU32 crc32_lut[256] = { 0 };
934     if (!crc32_lut[1])
935     {
936         const ImU32 polynomial = 0xEDB88320;
937         for (ImU32 i = 0; i < 256; i++)
938         {
939             ImU32 crc = i;
940             for (ImU32 j = 0; j < 8; j++)
941                 crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial);
942             crc32_lut[i] = crc;
943         }
944     }
945 
946     seed = ~seed;
947     ImU32 crc = seed;
948     const unsigned char* current = (const unsigned char*)data;
949 
950     if (data_size > 0)
951     {
952         // Known size
953         while (data_size--)
954             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++];
955     }
956     else
957     {
958         // Zero-terminated string
959         while (unsigned char c = *current++)
960         {
961             // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
962             // Because this syntax is rarely used we are optimizing for the common case.
963             // - If we reach ### in the string we discard the hash so far and reset to the seed.
964             // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller.
965             if (c == '#' && current[0] == '#' && current[1] == '#')
966                 crc = seed;
967 
968             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
969         }
970     }
971     return ~crc;
972 }
973 
974 //-----------------------------------------------------------------------------
975 // ImText* helpers
976 //-----------------------------------------------------------------------------
977 
978 // Convert UTF-8 to 32-bits character, process single character input.
979 // Based on stb_from_utf8() from github.com/nothings/stb/
980 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)981 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
982 {
983     unsigned int c = (unsigned int)-1;
984     const unsigned char* str = (const unsigned char*)in_text;
985     if (!(*str & 0x80))
986     {
987         c = (unsigned int)(*str++);
988         *out_char = c;
989         return 1;
990     }
991     if ((*str & 0xe0) == 0xc0)
992     {
993         *out_char = 0xFFFD; // will be invalid but not end of string
994         if (in_text_end && in_text_end - (const char*)str < 2) return 1;
995         if (*str < 0xc2) return 2;
996         c = (unsigned int)((*str++ & 0x1f) << 6);
997         if ((*str & 0xc0) != 0x80) return 2;
998         c += (*str++ & 0x3f);
999         *out_char = c;
1000         return 2;
1001     }
1002     if ((*str & 0xf0) == 0xe0)
1003     {
1004         *out_char = 0xFFFD; // will be invalid but not end of string
1005         if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1006         if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1007         if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1008         c = (unsigned int)((*str++ & 0x0f) << 12);
1009         if ((*str & 0xc0) != 0x80) return 3;
1010         c += (unsigned int)((*str++ & 0x3f) << 6);
1011         if ((*str & 0xc0) != 0x80) return 3;
1012         c += (*str++ & 0x3f);
1013         *out_char = c;
1014         return 3;
1015     }
1016     if ((*str & 0xf8) == 0xf0)
1017     {
1018         *out_char = 0xFFFD; // will be invalid but not end of string
1019         if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1020         if (*str > 0xf4) return 4;
1021         if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1022         if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1023         c = (unsigned int)((*str++ & 0x07) << 18);
1024         if ((*str & 0xc0) != 0x80) return 4;
1025         c += (unsigned int)((*str++ & 0x3f) << 12);
1026         if ((*str & 0xc0) != 0x80) return 4;
1027         c += (unsigned int)((*str++ & 0x3f) << 6);
1028         if ((*str & 0xc0) != 0x80) return 4;
1029         c += (*str++ & 0x3f);
1030         // utf-8 encodings of values used in surrogate pairs are invalid
1031         if ((c & 0xFFFFF800) == 0xD800) return 4;
1032         *out_char = c;
1033         return 4;
1034     }
1035     *out_char = 0;
1036     return 0;
1037 }
1038 
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1039 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1040 {
1041     ImWchar* buf_out = buf;
1042     ImWchar* buf_end = buf + buf_size;
1043     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1044     {
1045         unsigned int c;
1046         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1047         if (c == 0)
1048             break;
1049         if (c < 0x10000)    // FIXME: Losing characters that don't fit in 2 bytes
1050             *buf_out++ = (ImWchar)c;
1051     }
1052     *buf_out = 0;
1053     if (in_text_remaining)
1054         *in_text_remaining = in_text;
1055     return (int)(buf_out - buf);
1056 }
1057 
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1058 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1059 {
1060     int char_count = 0;
1061     while ((!in_text_end || in_text < in_text_end) && *in_text)
1062     {
1063         unsigned int c;
1064         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1065         if (c == 0)
1066             break;
1067         if (c < 0x10000)
1068             char_count++;
1069     }
1070     return char_count;
1071 }
1072 
1073 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1074 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1075 {
1076     if (c < 0x80)
1077     {
1078         buf[0] = (char)c;
1079         return 1;
1080     }
1081     if (c < 0x800)
1082     {
1083         if (buf_size < 2) return 0;
1084         buf[0] = (char)(0xc0 + (c >> 6));
1085         buf[1] = (char)(0x80 + (c & 0x3f));
1086         return 2;
1087     }
1088     if (c >= 0xdc00 && c < 0xe000)
1089     {
1090         return 0;
1091     }
1092     if (c >= 0xd800 && c < 0xdc00)
1093     {
1094         if (buf_size < 4) return 0;
1095         buf[0] = (char)(0xf0 + (c >> 18));
1096         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1097         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1098         buf[3] = (char)(0x80 + ((c ) & 0x3f));
1099         return 4;
1100     }
1101     //else if (c < 0x10000)
1102     {
1103         if (buf_size < 3) return 0;
1104         buf[0] = (char)(0xe0 + (c >> 12));
1105         buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1106         buf[2] = (char)(0x80 + ((c ) & 0x3f));
1107         return 3;
1108     }
1109 }
1110 
ImTextCountUtf8BytesFromChar(unsigned int c)1111 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1112 {
1113     if (c < 0x80) return 1;
1114     if (c < 0x800) return 2;
1115     if (c >= 0xdc00 && c < 0xe000) return 0;
1116     if (c >= 0xd800 && c < 0xdc00) return 4;
1117     return 3;
1118 }
1119 
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1120 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1121 {
1122     char* buf_out = buf;
1123     const char* buf_end = buf + buf_size;
1124     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1125     {
1126         unsigned int c = (unsigned int)(*in_text++);
1127         if (c < 0x80)
1128             *buf_out++ = (char)c;
1129         else
1130             buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1131     }
1132     *buf_out = 0;
1133     return (int)(buf_out - buf);
1134 }
1135 
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1136 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1137 {
1138     int bytes_count = 0;
1139     while ((!in_text_end || in_text < in_text_end) && *in_text)
1140     {
1141         unsigned int c = (unsigned int)(*in_text++);
1142         if (c < 0x80)
1143             bytes_count++;
1144         else
1145             bytes_count += ImTextCountUtf8BytesFromChar(c);
1146     }
1147     return bytes_count;
1148 }
1149 
ColorConvertU32ToFloat4(ImU32 in)1150 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1151 {
1152     float s = 1.0f/255.0f;
1153     return ImVec4((in & 0xFF) * s, ((in >> 8) & 0xFF) * s, ((in >> 16) & 0xFF) * s, (in >> 24) * s);
1154 }
1155 
ColorConvertFloat4ToU32(const ImVec4 & in)1156 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1157 {
1158     ImU32 out;
1159     out  = ((ImU32)IM_F32_TO_INT8(ImSaturate(in.x)));
1160     out |= ((ImU32)IM_F32_TO_INT8(ImSaturate(in.y))) << 8;
1161     out |= ((ImU32)IM_F32_TO_INT8(ImSaturate(in.z))) << 16;
1162     out |= ((ImU32)IM_F32_TO_INT8(ImSaturate(in.w))) << 24;
1163     return out;
1164 }
1165 
1166 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1167 // 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)1168 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1169 {
1170     float K = 0.f;
1171     if (g < b)
1172     {
1173         const float tmp = g; g = b; b = tmp;
1174         K = -1.f;
1175     }
1176     if (r < g)
1177     {
1178         const float tmp = r; r = g; g = tmp;
1179         K = -2.f / 6.f - K;
1180     }
1181 
1182     const float chroma = r - (g < b ? g : b);
1183     out_h = fabsf(K + (g - b) / (6.f * chroma + 1e-20f));
1184     out_s = chroma / (r + 1e-20f);
1185     out_v = r;
1186 }
1187 
1188 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1189 // 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)1190 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1191 {
1192     if (s == 0.0f)
1193     {
1194         // gray
1195         out_r = out_g = out_b = v;
1196         return;
1197     }
1198 
1199     h = fmodf(h, 1.0f) / (60.0f/360.0f);
1200     int   i = (int)h;
1201     float f = h - (float)i;
1202     float p = v * (1.0f - s);
1203     float q = v * (1.0f - s * f);
1204     float t = v * (1.0f - s * (1.0f - f));
1205 
1206     switch (i)
1207     {
1208     case 0: out_r = v; out_g = t; out_b = p; break;
1209     case 1: out_r = q; out_g = v; out_b = p; break;
1210     case 2: out_r = p; out_g = v; out_b = t; break;
1211     case 3: out_r = p; out_g = q; out_b = v; break;
1212     case 4: out_r = t; out_g = p; out_b = v; break;
1213     case 5: default: out_r = v; out_g = p; out_b = q; break;
1214     }
1215 }
1216 
1217 // Load file content into memory
1218 // Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree()
ImLoadFileToMemory(const char * filename,const char * file_open_mode,int * out_file_size,int padding_bytes)1219 void* ImLoadFileToMemory(const char* filename, const char* file_open_mode, int* out_file_size, int padding_bytes)
1220 {
1221     IM_ASSERT(filename && file_open_mode);
1222     if (out_file_size)
1223         *out_file_size = 0;
1224 
1225     FILE* f;
1226     if ((f = fopen(filename, file_open_mode)) == NULL)
1227         return NULL;
1228 
1229     long file_size_signed;
1230     if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
1231     {
1232         fclose(f);
1233         return NULL;
1234     }
1235 
1236     int file_size = (int)file_size_signed;
1237     void* file_data = ImGui::MemAlloc(file_size + padding_bytes);
1238     if (file_data == NULL)
1239     {
1240         fclose(f);
1241         return NULL;
1242     }
1243     if (fread(file_data, 1, (size_t)file_size, f) != (size_t)file_size)
1244     {
1245         fclose(f);
1246         ImGui::MemFree(file_data);
1247         return NULL;
1248     }
1249     if (padding_bytes > 0)
1250         memset((void *)(((char*)file_data) + file_size), 0, padding_bytes);
1251 
1252     fclose(f);
1253     if (out_file_size)
1254         *out_file_size = file_size;
1255 
1256     return file_data;
1257 }
1258 
1259 //-----------------------------------------------------------------------------
1260 // ImGuiStorage
1261 //-----------------------------------------------------------------------------
1262 
1263 // Helper: Key->value storage
Clear()1264 void ImGuiStorage::Clear()
1265 {
1266     Data.clear();
1267 }
1268 
1269 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::Pair> & data,ImU32 key)1270 static ImVector<ImGuiStorage::Pair>::iterator LowerBound(ImVector<ImGuiStorage::Pair>& data, ImU32 key)
1271 {
1272     ImVector<ImGuiStorage::Pair>::iterator first = data.begin();
1273     ImVector<ImGuiStorage::Pair>::iterator last = data.end();
1274     int count = (int)(last - first);
1275     while (count > 0)
1276     {
1277         int count2 = count / 2;
1278         ImVector<ImGuiStorage::Pair>::iterator mid = first + count2;
1279         if (mid->key < key)
1280         {
1281             first = ++mid;
1282             count -= count2 + 1;
1283         }
1284         else
1285         {
1286             count = count2;
1287         }
1288     }
1289     return first;
1290 }
1291 
GetInt(ImU32 key,int default_val) const1292 int ImGuiStorage::GetInt(ImU32 key, int default_val) const
1293 {
1294     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1295     if (it == Data.end() || it->key != key)
1296         return default_val;
1297     return it->val_i;
1298 }
1299 
GetFloat(ImU32 key,float default_val) const1300 float ImGuiStorage::GetFloat(ImU32 key, float default_val) const
1301 {
1302     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1303     if (it == Data.end() || it->key != key)
1304         return default_val;
1305     return it->val_f;
1306 }
1307 
GetVoidPtr(ImGuiID key) const1308 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1309 {
1310     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1311     if (it == Data.end() || it->key != key)
1312         return NULL;
1313     return it->val_p;
1314 }
1315 
1316 // 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)1317 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1318 {
1319     ImVector<Pair>::iterator it = LowerBound(Data, key);
1320     if (it == Data.end() || it->key != key)
1321         it = Data.insert(it, Pair(key, default_val));
1322     return &it->val_i;
1323 }
1324 
GetFloatRef(ImGuiID key,float default_val)1325 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1326 {
1327     ImVector<Pair>::iterator it = LowerBound(Data, key);
1328     if (it == Data.end() || it->key != key)
1329         it = Data.insert(it, Pair(key, default_val));
1330     return &it->val_f;
1331 }
1332 
GetVoidPtrRef(ImGuiID key,void * default_val)1333 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1334 {
1335     ImVector<Pair>::iterator it = LowerBound(Data, key);
1336     if (it == Data.end() || it->key != key)
1337         it = Data.insert(it, Pair(key, default_val));
1338     return &it->val_p;
1339 }
1340 
1341 // 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(ImU32 key,int val)1342 void ImGuiStorage::SetInt(ImU32 key, int val)
1343 {
1344     ImVector<Pair>::iterator it = LowerBound(Data, key);
1345     if (it == Data.end() || it->key != key)
1346     {
1347         Data.insert(it, Pair(key, val));
1348         return;
1349     }
1350     it->val_i = val;
1351 }
1352 
SetFloat(ImU32 key,float val)1353 void ImGuiStorage::SetFloat(ImU32 key, float val)
1354 {
1355     ImVector<Pair>::iterator it = LowerBound(Data, key);
1356     if (it == Data.end() || it->key != key)
1357     {
1358         Data.insert(it, Pair(key, val));
1359         return;
1360     }
1361     it->val_f = val;
1362 }
1363 
SetVoidPtr(ImU32 key,void * val)1364 void ImGuiStorage::SetVoidPtr(ImU32 key, void* val)
1365 {
1366     ImVector<Pair>::iterator it = LowerBound(Data, key);
1367     if (it == Data.end() || it->key != key)
1368     {
1369         Data.insert(it, Pair(key, val));
1370         return;
1371     }
1372     it->val_p = val;
1373 }
1374 
SetAllInt(int v)1375 void ImGuiStorage::SetAllInt(int v)
1376 {
1377     for (int i = 0; i < Data.Size; i++)
1378         Data[i].val_i = v;
1379 }
1380 
1381 //-----------------------------------------------------------------------------
1382 // ImGuiTextFilter
1383 //-----------------------------------------------------------------------------
1384 
1385 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1386 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1387 {
1388     if (default_filter)
1389     {
1390         ImFormatString(InputBuf, IM_ARRAYSIZE(InputBuf), "%s", default_filter);
1391         Build();
1392     }
1393     else
1394     {
1395         InputBuf[0] = 0;
1396         CountGrep = 0;
1397     }
1398 }
1399 
Draw(const char * label,float width)1400 bool ImGuiTextFilter::Draw(const char* label, float width)
1401 {
1402     if (width != 0.0f)
1403         ImGui::PushItemWidth(width);
1404     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1405     if (width != 0.0f)
1406         ImGui::PopItemWidth();
1407     if (value_changed)
1408         Build();
1409     return value_changed;
1410 }
1411 
split(char separator,ImVector<TextRange> & out)1412 void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>& out)
1413 {
1414     out.resize(0);
1415     const char* wb = b;
1416     const char* we = wb;
1417     while (we < e)
1418     {
1419         if (*we == separator)
1420         {
1421             out.push_back(TextRange(wb, we));
1422             wb = we + 1;
1423         }
1424         we++;
1425     }
1426     if (wb != we)
1427         out.push_back(TextRange(wb, we));
1428 }
1429 
Build()1430 void ImGuiTextFilter::Build()
1431 {
1432     Filters.resize(0);
1433     TextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
1434     input_range.split(',', Filters);
1435 
1436     CountGrep = 0;
1437     for (int i = 0; i != Filters.Size; i++)
1438     {
1439         Filters[i].trim_blanks();
1440         if (Filters[i].empty())
1441             continue;
1442         if (Filters[i].front() != '-')
1443             CountGrep += 1;
1444     }
1445 }
1446 
PassFilter(const char * text,const char * text_end) const1447 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
1448 {
1449     if (Filters.empty())
1450         return true;
1451 
1452     if (text == NULL)
1453         text = "";
1454 
1455     for (int i = 0; i != Filters.Size; i++)
1456     {
1457         const TextRange& f = Filters[i];
1458         if (f.empty())
1459             continue;
1460         if (f.front() == '-')
1461         {
1462             // Subtract
1463             if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL)
1464                 return false;
1465         }
1466         else
1467         {
1468             // Grep
1469             if (ImStristr(text, text_end, f.begin(), f.end()) != NULL)
1470                 return true;
1471         }
1472     }
1473 
1474     // Implicit * grep
1475     if (CountGrep == 0)
1476         return true;
1477 
1478     return false;
1479 }
1480 
1481 //-----------------------------------------------------------------------------
1482 // ImGuiTextBuffer
1483 //-----------------------------------------------------------------------------
1484 
1485 // On some platform vsnprintf() takes va_list by reference and modifies it.
1486 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1487 #ifndef va_copy
1488 #define va_copy(dest, src) (dest = src)
1489 #endif
1490 
1491 // Helper: Text buffer for logging/accumulating text
appendv(const char * fmt,va_list args)1492 void ImGuiTextBuffer::appendv(const char* fmt, va_list args)
1493 {
1494     va_list args_copy;
1495     va_copy(args_copy, args);
1496 
1497     int len = vsnprintf(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
1498     if (len <= 0)
1499         return;
1500 
1501     const int write_off = Buf.Size;
1502     const int needed_sz = write_off + len;
1503     if (write_off + len >= Buf.Capacity)
1504     {
1505         int double_capacity = Buf.Capacity * 2;
1506         Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity);
1507     }
1508 
1509     Buf.resize(needed_sz);
1510     ImFormatStringV(&Buf[write_off] - 1, len+1, fmt, args_copy);
1511 }
1512 
append(const char * fmt,...)1513 void ImGuiTextBuffer::append(const char* fmt, ...)
1514 {
1515     va_list args;
1516     va_start(args, fmt);
1517     appendv(fmt, args);
1518     va_end(args);
1519 }
1520 
1521 //-----------------------------------------------------------------------------
1522 // ImGuiSimpleColumns
1523 //-----------------------------------------------------------------------------
1524 
ImGuiSimpleColumns()1525 ImGuiSimpleColumns::ImGuiSimpleColumns()
1526 {
1527     Count = 0;
1528     Spacing = Width = NextWidth = 0.0f;
1529     memset(Pos, 0, sizeof(Pos));
1530     memset(NextWidths, 0, sizeof(NextWidths));
1531 }
1532 
Update(int count,float spacing,bool clear)1533 void ImGuiSimpleColumns::Update(int count, float spacing, bool clear)
1534 {
1535     IM_ASSERT(Count <= IM_ARRAYSIZE(Pos));
1536     Count = count;
1537     Width = NextWidth = 0.0f;
1538     Spacing = spacing;
1539     if (clear) memset(NextWidths, 0, sizeof(NextWidths));
1540     for (int i = 0; i < Count; i++)
1541     {
1542         if (i > 0 && NextWidths[i] > 0.0f)
1543             Width += Spacing;
1544         Pos[i] = (float)(int)Width;
1545         Width += NextWidths[i];
1546         NextWidths[i] = 0.0f;
1547     }
1548 }
1549 
DeclColumns(float w0,float w1,float w2)1550 float ImGuiSimpleColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double
1551 {
1552     NextWidth = 0.0f;
1553     NextWidths[0] = ImMax(NextWidths[0], w0);
1554     NextWidths[1] = ImMax(NextWidths[1], w1);
1555     NextWidths[2] = ImMax(NextWidths[2], w2);
1556     for (int i = 0; i < 3; i++)
1557         NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f);
1558     return ImMax(Width, NextWidth);
1559 }
1560 
CalcExtraSpace(float avail_w)1561 float ImGuiSimpleColumns::CalcExtraSpace(float avail_w)
1562 {
1563     return ImMax(0.0f, avail_w - Width);
1564 }
1565 
1566 //-----------------------------------------------------------------------------
1567 // ImGuiWindow
1568 //-----------------------------------------------------------------------------
1569 
ImGuiWindow(const char * name)1570 ImGuiWindow::ImGuiWindow(const char* name)
1571 {
1572     Name = ImStrdup(name);
1573     ID = ImHash(name, 0);
1574     IDStack.push_back(ID);
1575     MoveID = GetID("#MOVE");
1576 
1577     Flags = 0;
1578     PosFloat = Pos = ImVec2(0.0f, 0.0f);
1579     Size = SizeFull = ImVec2(0.0f, 0.0f);
1580     SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
1581     WindowPadding = ImVec2(0.0f, 0.0f);
1582     Scroll = ImVec2(0.0f, 0.0f);
1583     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
1584     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
1585     ScrollbarX = ScrollbarY = false;
1586     ScrollbarSizes = ImVec2(0.0f, 0.0f);
1587     BorderSize = 0.0f;
1588     Active = WasActive = false;
1589     Accessed = false;
1590     Collapsed = false;
1591     SkipItems = false;
1592     BeginCount = 0;
1593     PopupID = 0;
1594     AutoFitFramesX = AutoFitFramesY = -1;
1595     AutoFitOnlyGrows = false;
1596     AutoPosLastDirection = -1;
1597     HiddenFrames = 0;
1598     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiSetCond_Always | ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing;
1599     SetWindowPosCenterWanted = false;
1600 
1601     LastFrameActive = -1;
1602     ItemWidthDefault = 0.0f;
1603     FontWindowScale = 1.0f;
1604 
1605     DrawList = (ImDrawList*)ImGui::MemAlloc(sizeof(ImDrawList));
1606     IM_PLACEMENT_NEW(DrawList) ImDrawList();
1607     DrawList->_OwnerName = Name;
1608     RootWindow = NULL;
1609     RootNonPopupWindow = NULL;
1610 
1611     FocusIdxAllCounter = FocusIdxTabCounter = -1;
1612     FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = IM_INT_MAX;
1613     FocusIdxAllRequestNext = FocusIdxTabRequestNext = IM_INT_MAX;
1614 }
1615 
~ImGuiWindow()1616 ImGuiWindow::~ImGuiWindow()
1617 {
1618     DrawList->~ImDrawList();
1619     ImGui::MemFree(DrawList);
1620     DrawList = NULL;
1621     ImGui::MemFree(Name);
1622     Name = NULL;
1623 }
1624 
GetID(const char * str,const char * str_end)1625 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
1626 {
1627     ImGuiID seed = IDStack.back();
1628     ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
1629     ImGui::KeepAliveID(id);
1630     return id;
1631 }
1632 
GetID(const void * ptr)1633 ImGuiID ImGuiWindow::GetID(const void* ptr)
1634 {
1635     ImGuiID seed = IDStack.back();
1636     ImGuiID id = ImHash(&ptr, sizeof(void*), seed);
1637     ImGui::KeepAliveID(id);
1638     return id;
1639 }
1640 
1641 //-----------------------------------------------------------------------------
1642 // Internal API exposed in imgui_internal.h
1643 //-----------------------------------------------------------------------------
1644 
SetCurrentWindow(ImGuiWindow * window)1645 static void SetCurrentWindow(ImGuiWindow* window)
1646 {
1647     ImGuiState& g = *GImGui;
1648     g.CurrentWindow = window;
1649     if (window)
1650         g.FontSize = window->CalcFontSize();
1651 }
1652 
GetParentWindow()1653 ImGuiWindow* ImGui::GetParentWindow()
1654 {
1655     ImGuiState& g = *GImGui;
1656     IM_ASSERT(g.CurrentWindowStack.Size >= 2);
1657     return g.CurrentWindowStack[(unsigned int)g.CurrentWindowStack.Size - 2];
1658 }
1659 
SetActiveID(ImGuiID id,ImGuiWindow * window=NULL)1660 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window = NULL)
1661 {
1662     ImGuiState& g = *GImGui;
1663     g.ActiveId = id;
1664     g.ActiveIdAllowOverlap = false;
1665     g.ActiveIdIsJustActivated = true;
1666     g.ActiveIdWindow = window;
1667 }
1668 
SetHoveredID(ImGuiID id)1669 void ImGui::SetHoveredID(ImGuiID id)
1670 {
1671     ImGuiState& g = *GImGui;
1672     g.HoveredId = id;
1673     g.HoveredIdAllowOverlap = false;
1674 }
1675 
KeepAliveID(ImGuiID id)1676 void ImGui::KeepAliveID(ImGuiID id)
1677 {
1678     ImGuiState& g = *GImGui;
1679     if (g.ActiveId == id)
1680         g.ActiveIdIsAlive = true;
1681 }
1682 
1683 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_offset_y)1684 void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
1685 {
1686     ImGuiWindow* window = GetCurrentWindow();
1687     if (window->SkipItems)
1688         return;
1689 
1690     // Always align ourselves on pixel boundaries
1691     ImGuiState& g = *GImGui;
1692     const float line_height = ImMax(window->DC.CurrentLineHeight, size.y);
1693     const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y);
1694     window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
1695     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));
1696     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
1697     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
1698 
1699     //window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, 0xFF0000FF, 4); // Debug
1700 
1701     window->DC.PrevLineHeight = line_height;
1702     window->DC.PrevLineTextBaseOffset = text_base_offset;
1703     window->DC.CurrentLineHeight = window->DC.CurrentLineTextBaseOffset = 0.0f;
1704 }
1705 
ItemSize(const ImRect & bb,float text_offset_y)1706 void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
1707 {
1708     ItemSize(bb.GetSize(), text_offset_y);
1709 }
1710 
1711 // Declare item bounding box for clipping and interaction.
1712 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
1713 // 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)1714 bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id)
1715 {
1716     ImGuiWindow* window = GetCurrentWindow();
1717     window->DC.LastItemID = id ? *id : 0;
1718     window->DC.LastItemRect = bb;
1719     if (IsClippedEx(bb, id, false))
1720     {
1721         window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = false;
1722         return false;
1723     }
1724 
1725     // This is a sensible default, but widgets are free to override it after calling ItemAdd()
1726     ImGuiState& g = *GImGui;
1727     if (IsMouseHoveringRect(bb.Min, bb.Max))
1728     {
1729         // Matching the behavior of IsHovered() but ignore if ActiveId==window->MoveID (we clicked on the window background)
1730         // So that clicking on items with no active id such as Text() still returns true with IsItemHovered()
1731         window->DC.LastItemHoveredRect = true;
1732         window->DC.LastItemHoveredAndUsable = false;
1733         if (g.HoveredRootWindow == window->RootWindow)
1734             if (g.ActiveId == 0 || (id && g.ActiveId == *id) || g.ActiveIdAllowOverlap || (g.ActiveId == window->MoveID))
1735                 if (IsWindowContentHoverable(window))
1736                     window->DC.LastItemHoveredAndUsable = true;
1737     }
1738     else
1739     {
1740         window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = false;
1741     }
1742 
1743     return true;
1744 }
1745 
IsClippedEx(const ImRect & bb,const ImGuiID * id,bool clip_even_when_logged)1746 bool ImGui::IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged)
1747 {
1748     ImGuiState& g = *GImGui;
1749     ImGuiWindow* window = GetCurrentWindowRead();
1750 
1751     if (!bb.Overlaps(window->ClipRect))
1752     {
1753         if (!id || *id != GImGui->ActiveId)
1754             if (clip_even_when_logged || !g.LogEnabled)
1755                 return true;
1756     }
1757     return false;
1758 }
1759 
IsHovered(const ImRect & bb,ImGuiID id,bool flatten_childs)1760 bool ImGui::IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs)
1761 {
1762     ImGuiState& g = *GImGui;
1763     if (g.HoveredId == 0 || g.HoveredId == id || g.HoveredIdAllowOverlap)
1764     {
1765         ImGuiWindow* window = GetCurrentWindowRead();
1766         if (g.HoveredWindow == window || (flatten_childs && g.HoveredRootWindow == window->RootWindow))
1767             if ((g.ActiveId == 0 || g.ActiveId == id || g.ActiveIdAllowOverlap) && ImGui::IsMouseHoveringRect(bb.Min, bb.Max))
1768                 if (IsWindowContentHoverable(g.HoveredRootWindow))
1769                     return true;
1770     }
1771     return false;
1772 }
1773 
FocusableItemRegister(ImGuiWindow * window,bool is_active,bool tab_stop)1774 bool ImGui::FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_stop)
1775 {
1776     ImGuiState& g = *GImGui;
1777 
1778     const bool allow_keyboard_focus = window->DC.AllowKeyboardFocus;
1779     window->FocusIdxAllCounter++;
1780     if (allow_keyboard_focus)
1781         window->FocusIdxTabCounter++;
1782 
1783     // Process keyboard input at this point: TAB, Shift-TAB switch focus
1784     // We can always TAB out of a widget that doesn't allow tabbing in.
1785     if (tab_stop && window->FocusIdxAllRequestNext == IM_INT_MAX && window->FocusIdxTabRequestNext == IM_INT_MAX && is_active && IsKeyPressedMap(ImGuiKey_Tab))
1786     {
1787         // Modulo on index will be applied at the end of frame once we've got the total counter of items.
1788         window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1);
1789     }
1790 
1791     if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
1792         return true;
1793 
1794     if (allow_keyboard_focus)
1795         if (window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent)
1796             return true;
1797 
1798     return false;
1799 }
1800 
FocusableItemUnregister(ImGuiWindow * window)1801 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
1802 {
1803     window->FocusIdxAllCounter--;
1804     window->FocusIdxTabCounter--;
1805 }
1806 
CalcItemSize(ImVec2 size,float default_x,float default_y)1807 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y)
1808 {
1809     ImGuiState& g = *GImGui;
1810     ImVec2 content_max;
1811     if (size.x < 0.0f || size.y < 0.0f)
1812         content_max = g.CurrentWindow->Pos + ImGui::GetContentRegionMax();
1813     if (size.x <= 0.0f)
1814         size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x;
1815     if (size.y <= 0.0f)
1816         size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y;
1817     return size;
1818 }
1819 
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)1820 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
1821 {
1822     if (wrap_pos_x < 0.0f)
1823         return 0.0f;
1824 
1825     ImGuiWindow* window = GetCurrentWindowRead();
1826     if (wrap_pos_x == 0.0f)
1827         wrap_pos_x = ImGui::GetContentRegionMax().x + window->Pos.x;
1828     else if (wrap_pos_x > 0.0f)
1829         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
1830 
1831     const float wrap_width = wrap_pos_x > 0.0f ? ImMax(wrap_pos_x - pos.x, 0.00001f) : 0.0f;
1832     return wrap_width;
1833 }
1834 
1835 //-----------------------------------------------------------------------------
1836 
MemAlloc(size_t sz)1837 void* ImGui::MemAlloc(size_t sz)
1838 {
1839     GImGui->IO.MetricsAllocs++;
1840     return GImGui->IO.MemAllocFn(sz);
1841 }
1842 
MemFree(void * ptr)1843 void ImGui::MemFree(void* ptr)
1844 {
1845     if (ptr) GImGui->IO.MetricsAllocs--;
1846     return GImGui->IO.MemFreeFn(ptr);
1847 }
1848 
GetClipboardText()1849 const char* ImGui::GetClipboardText()
1850 {
1851     return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn() : "";
1852 }
1853 
SetClipboardText(const char * text)1854 void ImGui::SetClipboardText(const char* text)
1855 {
1856     if (GImGui->IO.SetClipboardTextFn)
1857         GImGui->IO.SetClipboardTextFn(text);
1858 }
1859 
GetVersion()1860 const char* ImGui::GetVersion()
1861 {
1862     return IMGUI_VERSION;
1863 }
1864 
1865 // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
1866 // 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
GetInternalState()1867 void* ImGui::GetInternalState()
1868 {
1869     return GImGui;
1870 }
1871 
GetInternalStateSize()1872 size_t ImGui::GetInternalStateSize()
1873 {
1874     return sizeof(ImGuiState);
1875 }
1876 
SetInternalState(void * state,bool construct)1877 void ImGui::SetInternalState(void* state, bool construct)
1878 {
1879     if (construct)
1880         IM_PLACEMENT_NEW(state) ImGuiState();
1881     GImGui = (ImGuiState*)state;
1882 }
1883 
GetIO()1884 ImGuiIO& ImGui::GetIO()
1885 {
1886     return GImGui->IO;
1887 }
1888 
GetStyle()1889 ImGuiStyle& ImGui::GetStyle()
1890 {
1891     return GImGui->Style;
1892 }
1893 
1894 // Same value as passed to your RenderDrawListsFn() function. valid after Render() and until the next call to NewFrame()
GetDrawData()1895 ImDrawData* ImGui::GetDrawData()
1896 {
1897     return GImGui->RenderDrawData.Valid ? &GImGui->RenderDrawData : NULL;
1898 }
1899 
GetTime()1900 float ImGui::GetTime()
1901 {
1902     return GImGui->Time;
1903 }
1904 
GetFrameCount()1905 int ImGui::GetFrameCount()
1906 {
1907     return GImGui->FrameCount;
1908 }
1909 
NewFrame()1910 void ImGui::NewFrame()
1911 {
1912     ImGuiState& g = *GImGui;
1913 
1914     // Check user data
1915     IM_ASSERT(g.IO.DeltaTime >= 0.0f);               // Need a positive DeltaTime (zero is tolerated but will cause some timing issues)
1916     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f);
1917     IM_ASSERT(g.IO.Fonts->Fonts.Size > 0);           // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
1918     IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded());     // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
1919     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f);  // Invalid style setting
1920 
1921     if (!g.Initialized)
1922     {
1923         // Initialize on first frame
1924         g.LogClipboard = (ImGuiTextBuffer*)ImGui::MemAlloc(sizeof(ImGuiTextBuffer));
1925         IM_PLACEMENT_NEW(g.LogClipboard) ImGuiTextBuffer();
1926 
1927         IM_ASSERT(g.Settings.empty());
1928         LoadSettings();
1929         g.Initialized = true;
1930     }
1931 
1932     SetCurrentFont(g.IO.Fonts->Fonts[0]);
1933 
1934     g.Time += g.IO.DeltaTime;
1935     g.FrameCount += 1;
1936     g.Tooltip[0] = '\0';
1937     g.OverlayDrawList.Clear();
1938     g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID);
1939     g.OverlayDrawList.PushClipRectFullScreen();
1940     g.OverlayDrawList.AddDrawCmd();
1941 
1942     // Mark rendering data as invalid to prevent user who may have a handle on it to use it
1943     g.RenderDrawData.Valid = false;
1944     g.RenderDrawData.CmdLists = NULL;
1945     g.RenderDrawData.CmdListsCount = g.RenderDrawData.TotalVtxCount = g.RenderDrawData.TotalIdxCount = 0;
1946 
1947     // Update inputs state
1948     if (g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0)
1949         g.IO.MousePos = ImVec2(-9999.0f, -9999.0f);
1950     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
1951         g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
1952     else
1953         g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
1954     g.IO.MousePosPrev = g.IO.MousePos;
1955     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
1956     {
1957         g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
1958         g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
1959         g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
1960         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;
1961         g.IO.MouseDoubleClicked[i] = false;
1962         if (g.IO.MouseClicked[i])
1963         {
1964             if (g.Time - g.IO.MouseClickedTime[i] < g.IO.MouseDoubleClickTime)
1965             {
1966                 if (ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
1967                     g.IO.MouseDoubleClicked[i] = true;
1968                 g.IO.MouseClickedTime[i] = -FLT_MAX;    // so the third click isn't turned into a double-click
1969             }
1970             else
1971             {
1972                 g.IO.MouseClickedTime[i] = g.Time;
1973             }
1974             g.IO.MouseClickedPos[i] = g.IO.MousePos;
1975             g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
1976         }
1977         else if (g.IO.MouseDown[i])
1978         {
1979             g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]));
1980         }
1981     }
1982     memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
1983     for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
1984         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;
1985 
1986     // Calculate frame-rate for the user, as a purely luxurious feature
1987     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
1988     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
1989     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
1990     g.IO.Framerate = 1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame));
1991 
1992     // Clear reference to active widget if the widget isn't alive anymore
1993     g.HoveredIdPreviousFrame = g.HoveredId;
1994     g.HoveredId = 0;
1995     g.HoveredIdAllowOverlap = false;
1996     if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
1997         SetActiveID(0);
1998     g.ActiveIdPreviousFrame = g.ActiveId;
1999     g.ActiveIdIsAlive = false;
2000     g.ActiveIdIsJustActivated = false;
2001     if (!g.ActiveId)
2002         g.MovedWindow = NULL;
2003 
2004     // Delay saving settings so we don't spam disk too much
2005     if (g.SettingsDirtyTimer > 0.0f)
2006     {
2007         g.SettingsDirtyTimer -= g.IO.DeltaTime;
2008         if (g.SettingsDirtyTimer <= 0.0f)
2009             SaveSettings();
2010     }
2011 
2012     // Find the window we are hovering. Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow
2013     g.HoveredWindow = FindHoveredWindow(g.IO.MousePos, false);
2014     if (g.HoveredWindow && (g.HoveredWindow->Flags & ImGuiWindowFlags_ChildWindow))
2015         g.HoveredRootWindow = g.HoveredWindow->RootWindow;
2016     else
2017         g.HoveredRootWindow = FindHoveredWindow(g.IO.MousePos, true);
2018 
2019     if (ImGuiWindow* modal_window = GetFrontMostModalRootWindow())
2020     {
2021         g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f);
2022         if (g.HoveredRootWindow != modal_window)
2023             g.HoveredRootWindow = g.HoveredWindow = NULL;
2024     }
2025     else
2026     {
2027         g.ModalWindowDarkeningRatio = 0.0f;
2028     }
2029 
2030     // Are we using inputs? Tell user so they can capture/discard the inputs away from the rest of their application.
2031     // 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.
2032     int mouse_earliest_button_down = -1;
2033     bool mouse_any_down = false;
2034     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
2035     {
2036         if (g.IO.MouseClicked[i])
2037             g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenedPopupStack.empty());
2038         mouse_any_down |= g.IO.MouseDown[i];
2039         if (g.IO.MouseDown[i])
2040             if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[mouse_earliest_button_down] > g.IO.MouseClickedTime[i])
2041                 mouse_earliest_button_down = i;
2042     }
2043     bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
2044     if (g.CaptureMouseNextFrame != -1)
2045         g.IO.WantCaptureMouse = (g.CaptureMouseNextFrame != 0);
2046     else
2047         g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.ActiveId != 0) || (!g.OpenedPopupStack.empty());
2048     g.IO.WantCaptureKeyboard = (g.CaptureKeyboardNextFrame != -1) ? (g.CaptureKeyboardNextFrame != 0) : (g.ActiveId != 0);
2049     g.IO.WantTextInput = (g.ActiveId != 0 && g.InputTextState.Id == g.ActiveId);
2050     g.MouseCursor = ImGuiMouseCursor_Arrow;
2051     g.CaptureMouseNextFrame = g.CaptureKeyboardNextFrame = -1;
2052     g.OsImePosRequest = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
2053 
2054     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
2055     if (!mouse_avail_to_imgui)
2056         g.HoveredWindow = g.HoveredRootWindow = NULL;
2057 
2058     // Scale & Scrolling
2059     if (g.HoveredWindow && g.IO.MouseWheel != 0.0f && !g.HoveredWindow->Collapsed)
2060     {
2061         ImGuiWindow* window = g.HoveredWindow;
2062         if (g.IO.KeyCtrl)
2063         {
2064             if (g.IO.FontAllowUserScaling)
2065             {
2066                 // Zoom / Scale window
2067                 float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
2068                 float scale = new_font_scale / window->FontWindowScale;
2069                 window->FontWindowScale = new_font_scale;
2070 
2071                 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
2072                 window->Pos += offset;
2073                 window->PosFloat += offset;
2074                 window->Size *= scale;
2075                 window->SizeFull *= scale;
2076             }
2077         }
2078         else
2079         {
2080             // Scroll
2081             if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse))
2082             {
2083                 const int scroll_lines = (window->Flags & ImGuiWindowFlags_ComboBox) ? 3 : 5;
2084                 SetWindowScrollY(window, window->Scroll.y - g.IO.MouseWheel * window->CalcFontSize() * scroll_lines);
2085             }
2086         }
2087     }
2088 
2089     // Pressing TAB activate widget focus
2090     // 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.
2091     if (g.ActiveId == 0 && g.FocusedWindow != NULL && g.FocusedWindow->Active && IsKeyPressedMap(ImGuiKey_Tab, false))
2092         g.FocusedWindow->FocusIdxTabRequestNext = 0;
2093 
2094     // Mark all windows as not visible
2095     for (int i = 0; i != g.Windows.Size; i++)
2096     {
2097         ImGuiWindow* window = g.Windows[i];
2098         window->WasActive = window->Active;
2099         window->Active = false;
2100         window->Accessed = false;
2101     }
2102 
2103     // No window should be open at the beginning of the frame.
2104     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
2105     g.CurrentWindowStack.resize(0);
2106     g.CurrentPopupStack.resize(0);
2107     CloseInactivePopups();
2108 
2109     // Create implicit window - we will only render it if the user has added something to it.
2110     ImGui::SetNextWindowSize(ImVec2(400,400), ImGuiSetCond_FirstUseEver);
2111     ImGui::Begin("Debug");
2112 }
2113 
2114 // NB: behavior of ImGui after Shutdown() is not tested/guaranteed at the moment. This function is merely here to free heap allocations.
Shutdown()2115 void ImGui::Shutdown()
2116 {
2117     ImGuiState& g = *GImGui;
2118 
2119     // 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)
2120     if (g.IO.Fonts) // Testing for NULL to allow user to NULLify in case of running Shutdown() on multiple contexts. Bit hacky.
2121         g.IO.Fonts->Clear();
2122 
2123     // Cleanup of other data are conditional on actually having used ImGui.
2124     if (!g.Initialized)
2125         return;
2126 
2127     SaveSettings();
2128 
2129     for (int i = 0; i < g.Windows.Size; i++)
2130     {
2131         g.Windows[i]->~ImGuiWindow();
2132         ImGui::MemFree(g.Windows[i]);
2133     }
2134     g.Windows.clear();
2135     g.WindowsSortBuffer.clear();
2136     g.CurrentWindowStack.clear();
2137     g.FocusedWindow = NULL;
2138     g.HoveredWindow = NULL;
2139     g.HoveredRootWindow = NULL;
2140     for (int i = 0; i < g.Settings.Size; i++)
2141         ImGui::MemFree(g.Settings[i].Name);
2142     g.Settings.clear();
2143     g.ColorModifiers.clear();
2144     g.StyleModifiers.clear();
2145     g.FontStack.clear();
2146     g.OpenedPopupStack.clear();
2147     g.CurrentPopupStack.clear();
2148     for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2149         g.RenderDrawLists[i].clear();
2150     g.OverlayDrawList.ClearFreeMemory();
2151     g.ColorEditModeStorage.Clear();
2152     if (g.PrivateClipboard)
2153     {
2154         ImGui::MemFree(g.PrivateClipboard);
2155         g.PrivateClipboard = NULL;
2156     }
2157     g.InputTextState.Text.clear();
2158     g.InputTextState.InitialText.clear();
2159     g.InputTextState.TempTextBuffer.clear();
2160 
2161     if (g.LogFile && g.LogFile != stdout)
2162     {
2163         fclose(g.LogFile);
2164         g.LogFile = NULL;
2165     }
2166     if (g.LogClipboard)
2167     {
2168         g.LogClipboard->~ImGuiTextBuffer();
2169         ImGui::MemFree(g.LogClipboard);
2170     }
2171 
2172     g.Initialized = false;
2173 }
2174 
FindWindowSettings(const char * name)2175 static ImGuiIniData* FindWindowSettings(const char* name)
2176 {
2177     ImGuiState& g = *GImGui;
2178     ImGuiID id = ImHash(name, 0);
2179     for (int i = 0; i != g.Settings.Size; i++)
2180     {
2181         ImGuiIniData* ini = &g.Settings[i];
2182         if (ini->ID == id)
2183             return ini;
2184     }
2185     return NULL;
2186 }
2187 
AddWindowSettings(const char * name)2188 static ImGuiIniData* AddWindowSettings(const char* name)
2189 {
2190     GImGui->Settings.resize(GImGui->Settings.Size + 1);
2191     ImGuiIniData* ini = &GImGui->Settings.back();
2192     ini->Name = ImStrdup(name);
2193     ini->ID = ImHash(name, 0);
2194     ini->Collapsed = false;
2195     ini->Pos = ImVec2(FLT_MAX,FLT_MAX);
2196     ini->Size = ImVec2(0,0);
2197     return ini;
2198 }
2199 
2200 // Zero-tolerance, poor-man .ini parsing
2201 // FIXME: Write something less rubbish
LoadSettings()2202 static void LoadSettings()
2203 {
2204     ImGuiState& g = *GImGui;
2205     const char* filename = g.IO.IniFilename;
2206     if (!filename)
2207         return;
2208 
2209     int file_size;
2210     char* file_data = (char*)ImLoadFileToMemory(filename, "rb", &file_size, 1);
2211     if (!file_data)
2212         return;
2213 
2214     ImGuiIniData* settings = NULL;
2215     const char* buf_end = file_data + file_size;
2216     for (const char* line_start = file_data; line_start < buf_end; )
2217     {
2218         const char* line_end = line_start;
2219         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
2220             line_end++;
2221 
2222         if (line_start[0] == '[' && line_end > line_start && line_end[-1] == ']')
2223         {
2224             char name[64];
2225             ImFormatString(name, IM_ARRAYSIZE(name), "%.*s", (int)(line_end-line_start-2), line_start+1);
2226             settings = FindWindowSettings(name);
2227             if (!settings)
2228                 settings = AddWindowSettings(name);
2229         }
2230         else if (settings)
2231         {
2232             float x, y;
2233             int i;
2234             if (sscanf(line_start, "Pos=%f,%f", &x, &y) == 2)
2235                 settings->Pos = ImVec2(x, y);
2236             else if (sscanf(line_start, "Size=%f,%f", &x, &y) == 2)
2237                 settings->Size = ImMax(ImVec2(x, y), g.Style.WindowMinSize);
2238             else if (sscanf(line_start, "Collapsed=%d", &i) == 1)
2239                 settings->Collapsed = (i != 0);
2240         }
2241 
2242         line_start = line_end+1;
2243     }
2244 
2245     ImGui::MemFree(file_data);
2246 }
2247 
SaveSettings()2248 static void SaveSettings()
2249 {
2250     ImGuiState& g = *GImGui;
2251     const char* filename = g.IO.IniFilename;
2252     if (!filename)
2253         return;
2254 
2255     // Gather data from windows that were active during this session
2256     for (int i = 0; i != g.Windows.Size; i++)
2257     {
2258         ImGuiWindow* window = g.Windows[i];
2259         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
2260             continue;
2261         ImGuiIniData* settings = FindWindowSettings(window->Name);
2262         settings->Pos = window->Pos;
2263         settings->Size = window->SizeFull;
2264         settings->Collapsed = window->Collapsed;
2265     }
2266 
2267     // Write .ini file
2268     // If a window wasn't opened in this session we preserve its settings
2269     FILE* f = fopen(filename, "wt");
2270     if (!f)
2271         return;
2272     for (int i = 0; i != g.Settings.Size; i++)
2273     {
2274         const ImGuiIniData* settings = &g.Settings[i];
2275         if (settings->Pos.x == FLT_MAX)
2276             continue;
2277         const char* name = settings->Name;
2278         if (const char* p = strstr(name, "###"))  // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
2279             name = p;
2280         fprintf(f, "[%s]\n", name);
2281         fprintf(f, "Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
2282         fprintf(f, "Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
2283         fprintf(f, "Collapsed=%d\n", settings->Collapsed);
2284         fprintf(f, "\n");
2285     }
2286 
2287     fclose(f);
2288 }
2289 
MarkSettingsDirty()2290 static void MarkSettingsDirty()
2291 {
2292     ImGuiState& g = *GImGui;
2293     if (g.SettingsDirtyTimer <= 0.0f)
2294         g.SettingsDirtyTimer = g.IO.IniSavingRate;
2295 }
2296 
2297 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)2298 static int ChildWindowComparer(const void* lhs, const void* rhs)
2299 {
2300     const ImGuiWindow* a = *(const ImGuiWindow**)lhs;
2301     const ImGuiWindow* b = *(const ImGuiWindow**)rhs;
2302     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
2303         return d;
2304     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
2305         return d;
2306     if (int d = (a->Flags & ImGuiWindowFlags_ComboBox) - (b->Flags & ImGuiWindowFlags_ComboBox))
2307         return d;
2308     return 0;
2309 }
2310 
AddWindowToSortedBuffer(ImVector<ImGuiWindow * > & out_sorted_windows,ImGuiWindow * window)2311 static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>& out_sorted_windows, ImGuiWindow* window)
2312 {
2313     out_sorted_windows.push_back(window);
2314     if (window->Active)
2315     {
2316         int count = window->DC.ChildWindows.Size;
2317         if (count > 1)
2318             qsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
2319         for (int i = 0; i < count; i++)
2320         {
2321             ImGuiWindow* child = window->DC.ChildWindows[i];
2322             if (child->Active)
2323                 AddWindowToSortedBuffer(out_sorted_windows, child);
2324         }
2325     }
2326 }
2327 
AddDrawListToRenderList(ImVector<ImDrawList * > & out_render_list,ImDrawList * draw_list)2328 static void AddDrawListToRenderList(ImVector<ImDrawList*>& out_render_list, ImDrawList* draw_list)
2329 {
2330     if (draw_list->CmdBuffer.empty())
2331         return;
2332 
2333     // Remove trailing command if unused
2334     ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
2335     if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
2336     {
2337         draw_list->CmdBuffer.pop_back();
2338         if (draw_list->CmdBuffer.empty())
2339             return;
2340     }
2341 
2342     // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = 2 bytes = 64K vertices)
2343     // 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.
2344     IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);                   // Sanity check. Bug or mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
2345     IM_ASSERT((int64_t)draw_list->_VtxCurrentIdx <= ((int64_t)1L << (sizeof(ImDrawIdx)*8)));  // Too many vertices in same ImDrawList. See comment above.
2346 
2347     out_render_list.push_back(draw_list);
2348     GImGui->IO.MetricsRenderVertices += draw_list->VtxBuffer.Size;
2349     GImGui->IO.MetricsRenderIndices += draw_list->IdxBuffer.Size;
2350 }
2351 
AddWindowToRenderList(ImVector<ImDrawList * > & out_render_list,ImGuiWindow * window)2352 static void AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiWindow* window)
2353 {
2354     AddDrawListToRenderList(out_render_list, window->DrawList);
2355     for (int i = 0; i < window->DC.ChildWindows.Size; i++)
2356     {
2357         ImGuiWindow* child = window->DC.ChildWindows[i];
2358         if (!child->Active) // clipped children may have been marked not active
2359             continue;
2360         if ((child->Flags & ImGuiWindowFlags_Popup) && child->HiddenFrames > 0)
2361             continue;
2362         AddWindowToRenderList(out_render_list, child);
2363     }
2364 }
2365 
PushClipRect(const ImVec2 & clip_rect_min,const ImVec2 & clip_rect_max,bool intersect_with_existing_clip_rect)2366 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_existing_clip_rect)
2367 {
2368     ImGuiWindow* window = GetCurrentWindow();
2369 
2370     ImRect cr(clip_rect_min, clip_rect_max);
2371     if (intersect_with_existing_clip_rect)
2372     {
2373         // Clip our argument with the current clip rect
2374         cr.Clip(window->ClipRect);
2375     }
2376     cr.Max.x = ImMax(cr.Min.x, cr.Max.x);
2377     cr.Max.y = ImMax(cr.Min.y, cr.Max.y);
2378 
2379     IM_ASSERT(cr.Min.x <= cr.Max.x && cr.Min.y <= cr.Max.y);
2380     window->ClipRect = cr;
2381     window->DrawList->PushClipRect(ImVec4(cr.Min.x, cr.Min.y, cr.Max.x, cr.Max.y));
2382 }
2383 
PopClipRect()2384 void ImGui::PopClipRect()
2385 {
2386     ImGuiWindow* window = GetCurrentWindow();
2387     window->DrawList->PopClipRect();
2388     window->ClipRect = window->DrawList->_ClipRectStack.back();
2389 }
2390 
2391 // 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()2392 void ImGui::EndFrame()
2393 {
2394     ImGuiState& g = *GImGui;
2395     IM_ASSERT(g.Initialized);                       // Forgot to call ImGui::NewFrame()
2396     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // ImGui::EndFrame() called multiple times, or forgot to call ImGui::NewFrame() again
2397 
2398     // Render tooltip
2399     if (g.Tooltip[0])
2400     {
2401         ImGui::BeginTooltip();
2402         ImGui::TextUnformatted(g.Tooltip);
2403         ImGui::EndTooltip();
2404     }
2405 
2406     // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
2407     if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.OsImePosRequest - g.OsImePosSet) > 0.0001f)
2408     {
2409         g.IO.ImeSetInputScreenPosFn((int)g.OsImePosRequest.x, (int)g.OsImePosRequest.y);
2410         g.OsImePosSet = g.OsImePosRequest;
2411     }
2412 
2413     // Hide implicit "Debug" window if it hasn't been used
2414     IM_ASSERT(g.CurrentWindowStack.Size == 1);    // Mismatched Begin()/End() calls
2415     if (g.CurrentWindow && !g.CurrentWindow->Accessed)
2416         g.CurrentWindow->Active = false;
2417     ImGui::End();
2418 
2419     // Click to focus window and start moving (after we're done with all our widgets)
2420     if (!g.ActiveId)
2421         g.MovedWindow = NULL;
2422     if (g.ActiveId == 0 && g.HoveredId == 0 && g.IO.MouseClicked[0])
2423     {
2424         if (!(g.FocusedWindow && !g.FocusedWindow->WasActive && g.FocusedWindow->Active)) // Unless we just made a popup appear
2425         {
2426             if (g.HoveredRootWindow != NULL)
2427             {
2428                 FocusWindow(g.HoveredWindow);
2429                 if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove))
2430                 {
2431                     g.MovedWindow = g.HoveredWindow;
2432                     SetActiveID(g.HoveredRootWindow->MoveID, g.HoveredRootWindow);
2433                 }
2434             }
2435             else if (g.FocusedWindow != NULL && GetFrontMostModalRootWindow() == NULL)
2436             {
2437                 // Clicking on void disable focus
2438                 FocusWindow(NULL);
2439             }
2440         }
2441     }
2442 
2443     // Sort the window list so that all child windows are after their parent
2444     // We cannot do that on FocusWindow() because childs may not exist yet
2445     g.WindowsSortBuffer.resize(0);
2446     g.WindowsSortBuffer.reserve(g.Windows.Size);
2447     for (int i = 0; i != g.Windows.Size; i++)
2448     {
2449         ImGuiWindow* window = g.Windows[i];
2450         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
2451             continue;
2452         AddWindowToSortedBuffer(g.WindowsSortBuffer, window);
2453     }
2454     IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size);  // we done something wrong
2455     g.Windows.swap(g.WindowsSortBuffer);
2456 
2457     // Clear Input data for next frame
2458     g.IO.MouseWheel = 0.0f;
2459     memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
2460 
2461     g.FrameCountEnded = g.FrameCount;
2462 }
2463 
Render()2464 void ImGui::Render()
2465 {
2466     ImGuiState& g = *GImGui;
2467     IM_ASSERT(g.Initialized);   // Forgot to call ImGui::NewFrame()
2468 
2469     if (g.FrameCountEnded != g.FrameCount)
2470         ImGui::EndFrame();
2471     g.FrameCountRendered = g.FrameCount;
2472 
2473     // Skip render altogether if alpha is 0.0
2474     // 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.
2475     if (g.Style.Alpha > 0.0f)
2476     {
2477         // Gather windows to render
2478         g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0;
2479         for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2480             g.RenderDrawLists[i].resize(0);
2481         for (int i = 0; i != g.Windows.Size; i++)
2482         {
2483             ImGuiWindow* window = g.Windows[i];
2484             if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0)
2485             {
2486                 // FIXME: Generalize this with a proper layering system so e.g. user can draw in specific layers, below text, ..
2487                 g.IO.MetricsActiveWindows++;
2488                 if (window->Flags & ImGuiWindowFlags_Popup)
2489                     AddWindowToRenderList(g.RenderDrawLists[1], window);
2490                 else if (window->Flags & ImGuiWindowFlags_Tooltip)
2491                     AddWindowToRenderList(g.RenderDrawLists[2], window);
2492                 else
2493                     AddWindowToRenderList(g.RenderDrawLists[0], window);
2494             }
2495         }
2496 
2497         // Flatten layers
2498         int n = g.RenderDrawLists[0].Size;
2499         int flattened_size = n;
2500         for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2501             flattened_size += g.RenderDrawLists[i].Size;
2502         g.RenderDrawLists[0].resize(flattened_size);
2503         for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2504         {
2505             ImVector<ImDrawList*>& layer = g.RenderDrawLists[i];
2506             if (layer.empty())
2507                 continue;
2508             memcpy(&g.RenderDrawLists[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
2509             n += layer.Size;
2510         }
2511 
2512         // Draw software mouse cursor if requested
2513         if (g.IO.MouseDrawCursor)
2514         {
2515             const ImGuiMouseCursorData& cursor_data = g.MouseCursorData[g.MouseCursor];
2516             const ImVec2 pos = g.IO.MousePos - cursor_data.HotOffset;
2517             const ImVec2 size = cursor_data.Size;
2518             const ImTextureID tex_id = g.IO.Fonts->TexID;
2519             g.OverlayDrawList.PushTextureID(tex_id);
2520             g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(1,0), pos+ImVec2(1,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], 0x30000000); // Shadow
2521             g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(2,0), pos+ImVec2(2,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], 0x30000000); // Shadow
2522             g.OverlayDrawList.AddImage(tex_id, pos,             pos + size,             cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], 0xFF000000); // Black border
2523             g.OverlayDrawList.AddImage(tex_id, pos,             pos + size,             cursor_data.TexUvMin[0], cursor_data.TexUvMax[0], 0xFFFFFFFF); // White fill
2524             g.OverlayDrawList.PopTextureID();
2525         }
2526         if (!g.OverlayDrawList.VtxBuffer.empty())
2527             AddDrawListToRenderList(g.RenderDrawLists[0], &g.OverlayDrawList);
2528 
2529         // Setup draw data
2530         g.RenderDrawData.Valid = true;
2531         g.RenderDrawData.CmdLists = (g.RenderDrawLists[0].Size > 0) ? &g.RenderDrawLists[0][0] : NULL;
2532         g.RenderDrawData.CmdListsCount = g.RenderDrawLists[0].Size;
2533         g.RenderDrawData.TotalVtxCount = g.IO.MetricsRenderVertices;
2534         g.RenderDrawData.TotalIdxCount = g.IO.MetricsRenderIndices;
2535 
2536         // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData()
2537         if (g.RenderDrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
2538             g.IO.RenderDrawListsFn(&g.RenderDrawData);
2539     }
2540 }
2541 
FindRenderedTextEnd(const char * text,const char * text_end)2542 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2543 {
2544     const char* text_display_end = text;
2545     if (!text_end)
2546         text_end = (const char*)-1;
2547 
2548     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2549         text_display_end++;
2550     return text_display_end;
2551 }
2552 
2553 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)2554 void ImGui::LogText(const char* fmt, ...)
2555 {
2556     ImGuiState& g = *GImGui;
2557     if (!g.LogEnabled)
2558         return;
2559 
2560     va_list args;
2561     va_start(args, fmt);
2562     if (g.LogFile)
2563     {
2564         vfprintf(g.LogFile, fmt, args);
2565     }
2566     else
2567     {
2568         g.LogClipboard->appendv(fmt, args);
2569     }
2570     va_end(args);
2571 }
2572 
2573 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
2574 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 & ref_pos,const char * text,const char * text_end)2575 static void LogRenderedText(const ImVec2& ref_pos, const char* text, const char* text_end)
2576 {
2577     ImGuiState& g = *GImGui;
2578     ImGuiWindow* window = ImGui::GetCurrentWindowRead();
2579 
2580     if (!text_end)
2581         text_end = ImGui::FindRenderedTextEnd(text, text_end);
2582 
2583     const bool log_new_line = ref_pos.y > window->DC.LogLinePosY+1;
2584     window->DC.LogLinePosY = ref_pos.y;
2585 
2586     const char* text_remaining = text;
2587     if (g.LogStartDepth > window->DC.TreeDepth)  // Re-adjust padding if we have popped out of our starting depth
2588         g.LogStartDepth = window->DC.TreeDepth;
2589     const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth);
2590     for (;;)
2591     {
2592         // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
2593         const char* line_end = text_remaining;
2594         while (line_end < text_end)
2595             if (*line_end == '\n')
2596                 break;
2597             else
2598                 line_end++;
2599         if (line_end >= text_end)
2600             line_end = NULL;
2601 
2602         const bool is_first_line = (text == text_remaining);
2603         bool is_last_line = false;
2604         if (line_end == NULL)
2605         {
2606             is_last_line = true;
2607             line_end = text_end;
2608         }
2609         if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0))
2610         {
2611             const int char_count = (int)(line_end - text_remaining);
2612             if (log_new_line || !is_first_line)
2613                 ImGui::LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining);
2614             else
2615                 ImGui::LogText(" %.*s", char_count, text_remaining);
2616         }
2617 
2618         if (is_last_line)
2619             break;
2620         text_remaining = line_end + 1;
2621     }
2622 }
2623 
2624 // Internal ImGui functions to render text
2625 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2626 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2627 {
2628     ImGuiState& g = *GImGui;
2629     ImGuiWindow* window = GetCurrentWindow();
2630 
2631     // Hide anything after a '##' string
2632     const char* text_display_end;
2633     if (hide_text_after_hash)
2634     {
2635         text_display_end = FindRenderedTextEnd(text, text_end);
2636     }
2637     else
2638     {
2639         if (!text_end)
2640             text_end = text + strlen(text); // FIXME-OPT
2641         text_display_end = text_end;
2642     }
2643 
2644     const int text_len = (int)(text_display_end - text);
2645     if (text_len > 0)
2646     {
2647         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2648         if (g.LogEnabled)
2649             LogRenderedText(pos, text, text_display_end);
2650     }
2651 }
2652 
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2653 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2654 {
2655     ImGuiState& g = *GImGui;
2656     ImGuiWindow* window = GetCurrentWindow();
2657 
2658     if (!text_end)
2659         text_end = text + strlen(text); // FIXME-OPT
2660 
2661     const int text_len = (int)(text_end - text);
2662     if (text_len > 0)
2663     {
2664         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2665         if (g.LogEnabled)
2666             LogRenderedText(pos, text, text_end);
2667     }
2668 }
2669 
2670 // 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,ImGuiAlign align,const ImVec2 * clip_min,const ImVec2 * clip_max)2671 void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, ImGuiAlign align, const ImVec2* clip_min, const ImVec2* clip_max)
2672 {
2673     // Hide anything after a '##' string
2674     const char* text_display_end = FindRenderedTextEnd(text, text_end);
2675     const int text_len = (int)(text_display_end - text);
2676     if (text_len == 0)
2677         return;
2678 
2679     ImGuiState& g = *GImGui;
2680     ImGuiWindow* window = GetCurrentWindow();
2681 
2682     // Perform CPU side clipping for single clipped element to avoid using scissor state
2683     ImVec2 pos = pos_min;
2684     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : ImGui::CalcTextSize(text, text_display_end, false, 0.0f);
2685 
2686     if (!clip_max) clip_max = &pos_max;
2687     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2688     if (!clip_min) clip_min = &pos_min; else need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2689 
2690     // Align
2691     if (align & ImGuiAlign_Center) pos.x = ImMax(pos.x, (pos.x + pos_max.x - text_size.x) * 0.5f);
2692     else if (align & ImGuiAlign_Right) pos.x = ImMax(pos.x, pos_max.x - text_size.x);
2693     if (align & ImGuiAlign_VCenter) pos.y = ImMax(pos.y, (pos.y + pos_max.y - text_size.y) * 0.5f);
2694 
2695     // Render
2696     if (need_clipping)
2697     {
2698         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2699         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2700     }
2701     else
2702     {
2703         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2704     }
2705     if (g.LogEnabled)
2706         LogRenderedText(pos, text, text_display_end);
2707 }
2708 
2709 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2710 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2711 {
2712     ImGuiWindow* window = GetCurrentWindow();
2713 
2714     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2715     if (border && (window->Flags & ImGuiWindowFlags_ShowBorders))
2716     {
2717         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding);
2718         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding);
2719     }
2720 }
2721 
2722 // Render a triangle to denote expanded/collapsed state
RenderCollapseTriangle(ImVec2 p_min,bool opened,float scale,bool shadow)2723 void ImGui::RenderCollapseTriangle(ImVec2 p_min, bool opened, float scale, bool shadow)
2724 {
2725     ImGuiState& g = *GImGui;
2726     ImGuiWindow* window = GetCurrentWindow();
2727 
2728     const float h = g.FontSize * 1.00f;
2729     const float r = h * 0.40f * scale;
2730     ImVec2 center = p_min + ImVec2(h*0.50f, h*0.50f*scale);
2731 
2732     ImVec2 a, b, c;
2733     if (opened)
2734     {
2735         center.y -= r*0.25f;
2736         a = center + ImVec2(0,1)*r;
2737         b = center + ImVec2(-0.866f,-0.5f)*r;
2738         c = center + ImVec2(0.866f,-0.5f)*r;
2739     }
2740     else
2741     {
2742         a = center + ImVec2(1,0)*r;
2743         b = center + ImVec2(-0.500f,0.866f)*r;
2744         c = center + ImVec2(-0.500f,-0.866f)*r;
2745     }
2746 
2747     if (shadow && (window->Flags & ImGuiWindowFlags_ShowBorders) != 0)
2748         window->DrawList->AddTriangleFilled(a+ImVec2(2,2), b+ImVec2(2,2), c+ImVec2(2,2), GetColorU32(ImGuiCol_BorderShadow));
2749     window->DrawList->AddTriangleFilled(a, b, c, GetColorU32(ImGuiCol_Text));
2750 }
2751 
RenderCheckMark(ImVec2 pos,ImU32 col)2752 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col)
2753 {
2754     ImGuiState& g = *GImGui;
2755     ImGuiWindow* window = GetCurrentWindow();
2756 
2757     ImVec2 a, b, c;
2758     float start_x = (float)(int)(g.FontSize * 0.307f + 0.5f);
2759     float rem_third = (float)(int)((g.FontSize - start_x) / 3.0f);
2760     a.x = pos.x + 0.5f + start_x;
2761     b.x = a.x + rem_third;
2762     c.x = a.x + rem_third * 3.0f;
2763     b.y = pos.y - 1.0f + (float)(int)(g.Font->Ascent * (g.FontSize / g.Font->FontSize) + 0.5f) + (float)(int)(g.Font->DisplayOffset.y);
2764     a.y = b.y - rem_third;
2765     c.y = b.y - rem_third * 2.0f;
2766 
2767     window->DrawList->PathLineTo(a);
2768     window->DrawList->PathLineTo(b);
2769     window->DrawList->PathLineTo(c);
2770     window->DrawList->PathStroke(col, false);
2771 }
2772 
2773 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
2774 // 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)2775 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
2776 {
2777     ImGuiState& g = *GImGui;
2778 
2779     const char* text_display_end;
2780     if (hide_text_after_double_hash)
2781         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
2782     else
2783         text_display_end = text_end;
2784 
2785     ImFont* font = g.Font;
2786     const float font_size = g.FontSize;
2787     if (text == text_display_end)
2788         return ImVec2(0.0f, font_size);
2789     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
2790 
2791     // Cancel out character spacing for the last character of a line (it is baked into glyph->XAdvance field)
2792     const float font_scale = font_size / font->FontSize;
2793     const float character_spacing_x = 1.0f * font_scale;
2794     if (text_size.x > 0.0f)
2795         text_size.x -= character_spacing_x;
2796     text_size.x = (float)(int)(text_size.x + 0.95f);
2797 
2798     return text_size;
2799 }
2800 
2801 // Helper to calculate coarse clipping of large list of evenly sized items.
2802 // NB: Prefer using the ImGuiListClipper higher-level helper if you can!
2803 // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX
2804 // If you are displaying thousands of items and you have a random access to the list, you can perform clipping yourself to save on CPU.
2805 // {
2806 //    float item_height = ImGui::GetTextLineHeightWithSpacing();
2807 //    int display_start, display_end;
2808 //    ImGui::CalcListClipping(count, item_height, &display_start, &display_end);            // calculate how many to clip/display
2809 //    ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (display_start) * item_height);         // advance cursor
2810 //    for (int i = display_start; i < display_end; i++)                                     // display only visible items
2811 //        // TODO: display visible item
2812 //    ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (count - display_end) * item_height);   // advance cursor
2813 // }
CalcListClipping(int items_count,float items_height,int * out_items_display_start,int * out_items_display_end)2814 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
2815 {
2816     ImGuiState& g = *GImGui;
2817     ImGuiWindow* window = GetCurrentWindowRead();
2818     if (g.LogEnabled)
2819     {
2820         // If logging is active, do not perform any clipping
2821         *out_items_display_start = 0;
2822         *out_items_display_end = items_count;
2823         return;
2824     }
2825 
2826     const ImVec2 pos = window->DC.CursorPos;
2827     int start = (int)((window->ClipRect.Min.y - pos.y) / items_height);
2828     int end = (int)((window->ClipRect.Max.y - pos.y) / items_height);
2829     start = ImClamp(start, 0, items_count);
2830     end = ImClamp(end + 1, start, items_count);
2831     *out_items_display_start = start;
2832     *out_items_display_end = end;
2833 }
2834 
2835 // Find window given position, search front-to-back
FindHoveredWindow(ImVec2 pos,bool excluding_childs)2836 static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs)
2837 {
2838     ImGuiState& g = *GImGui;
2839     for (int i = g.Windows.Size-1; i >= 0; i--)
2840     {
2841         ImGuiWindow* window = g.Windows[i];
2842         if (!window->Active)
2843             continue;
2844         if (window->Flags & ImGuiWindowFlags_NoInputs)
2845             continue;
2846         if (excluding_childs && (window->Flags & ImGuiWindowFlags_ChildWindow) != 0)
2847             continue;
2848 
2849         // Using the clipped AABB so a child window will typically be clipped by its parent.
2850         ImRect bb(window->ClippedWindowRect.Min - g.Style.TouchExtraPadding, window->ClippedWindowRect.Max + g.Style.TouchExtraPadding);
2851         if (bb.Contains(pos))
2852             return window;
2853     }
2854     return NULL;
2855 }
2856 
2857 // Test if mouse cursor is hovering given rectangle
2858 // NB- Rectangle is clipped by our current clip setting
2859 // 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)2860 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
2861 {
2862     ImGuiState& g = *GImGui;
2863     ImGuiWindow* window = GetCurrentWindowRead();
2864 
2865     // Clip
2866     ImRect rect_clipped(r_min, r_max);
2867     if (clip)
2868         rect_clipped.Clip(window->ClipRect);
2869 
2870     // Expand for touch input
2871     const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
2872     return rect_for_touch.Contains(g.IO.MousePos);
2873 }
2874 
IsMouseHoveringWindow()2875 bool ImGui::IsMouseHoveringWindow()
2876 {
2877     ImGuiState& g = *GImGui;
2878     return g.HoveredWindow == g.CurrentWindow;
2879 }
2880 
IsMouseHoveringAnyWindow()2881 bool ImGui::IsMouseHoveringAnyWindow()
2882 {
2883     ImGuiState& g = *GImGui;
2884     return g.HoveredWindow != NULL;
2885 }
2886 
IsPosHoveringAnyWindow(const ImVec2 & pos)2887 bool ImGui::IsPosHoveringAnyWindow(const ImVec2& pos)
2888 {
2889     return FindHoveredWindow(pos, false) != NULL;
2890 }
2891 
IsKeyPressedMap(ImGuiKey key,bool repeat)2892 static bool IsKeyPressedMap(ImGuiKey key, bool repeat)
2893 {
2894     const int key_index = GImGui->IO.KeyMap[key];
2895     return ImGui::IsKeyPressed(key_index, repeat);
2896 }
2897 
GetKeyIndex(ImGuiKey key)2898 int ImGui::GetKeyIndex(ImGuiKey key)
2899 {
2900     IM_ASSERT(key >= 0 && key < ImGuiKey_COUNT);
2901     return GImGui->IO.KeyMap[key];
2902 }
2903 
IsKeyDown(int key_index)2904 bool ImGui::IsKeyDown(int key_index)
2905 {
2906     if (key_index < 0) return false;
2907     IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown));
2908     return GImGui->IO.KeysDown[key_index];
2909 }
2910 
IsKeyPressed(int key_index,bool repeat)2911 bool ImGui::IsKeyPressed(int key_index, bool repeat)
2912 {
2913     ImGuiState& g = *GImGui;
2914     if (key_index < 0) return false;
2915     IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
2916     const float t = g.IO.KeysDownDuration[key_index];
2917     if (t == 0.0f)
2918         return true;
2919 
2920     if (repeat && t > g.IO.KeyRepeatDelay)
2921     {
2922         float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
2923         if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
2924             return true;
2925     }
2926     return false;
2927 }
2928 
IsKeyReleased(int key_index)2929 bool ImGui::IsKeyReleased(int key_index)
2930 {
2931     ImGuiState& g = *GImGui;
2932     if (key_index < 0) return false;
2933     IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
2934     if (g.IO.KeysDownDurationPrev[key_index] >= 0.0f && !g.IO.KeysDown[key_index])
2935         return true;
2936     return false;
2937 }
2938 
IsMouseDown(int button)2939 bool ImGui::IsMouseDown(int button)
2940 {
2941     ImGuiState& g = *GImGui;
2942     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
2943     return g.IO.MouseDown[button];
2944 }
2945 
IsMouseClicked(int button,bool repeat)2946 bool ImGui::IsMouseClicked(int button, bool repeat)
2947 {
2948     ImGuiState& g = *GImGui;
2949     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
2950     const float t = g.IO.MouseDownDuration[button];
2951     if (t == 0.0f)
2952         return true;
2953 
2954     if (repeat && t > g.IO.KeyRepeatDelay)
2955     {
2956         float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
2957         if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
2958             return true;
2959     }
2960 
2961     return false;
2962 }
2963 
IsMouseReleased(int button)2964 bool ImGui::IsMouseReleased(int button)
2965 {
2966     ImGuiState& g = *GImGui;
2967     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
2968     return g.IO.MouseReleased[button];
2969 }
2970 
IsMouseDoubleClicked(int button)2971 bool ImGui::IsMouseDoubleClicked(int button)
2972 {
2973     ImGuiState& g = *GImGui;
2974     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
2975     return g.IO.MouseDoubleClicked[button];
2976 }
2977 
IsMouseDragging(int button,float lock_threshold)2978 bool ImGui::IsMouseDragging(int button, float lock_threshold)
2979 {
2980     ImGuiState& g = *GImGui;
2981     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
2982     if (!g.IO.MouseDown[button])
2983         return false;
2984     if (lock_threshold < 0.0f)
2985         lock_threshold = g.IO.MouseDragThreshold;
2986     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
2987 }
2988 
GetMousePos()2989 ImVec2 ImGui::GetMousePos()
2990 {
2991     return GImGui->IO.MousePos;
2992 }
2993 
2994 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()2995 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
2996 {
2997     ImGuiState& g = *GImGui;
2998     if (g.CurrentPopupStack.Size > 0)
2999         return g.OpenedPopupStack[g.CurrentPopupStack.Size-1].MousePosOnOpen;
3000     return g.IO.MousePos;
3001 }
3002 
GetMouseDragDelta(int button,float lock_threshold)3003 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
3004 {
3005     ImGuiState& g = *GImGui;
3006     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3007     if (lock_threshold < 0.0f)
3008         lock_threshold = g.IO.MouseDragThreshold;
3009     if (g.IO.MouseDown[button])
3010         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
3011             return g.IO.MousePos - g.IO.MouseClickedPos[button];     // Assume we can only get active with left-mouse button (at the moment).
3012     return ImVec2(0.0f, 0.0f);
3013 }
3014 
ResetMouseDragDelta(int button)3015 void ImGui::ResetMouseDragDelta(int button)
3016 {
3017     ImGuiState& g = *GImGui;
3018     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3019     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
3020     g.IO.MouseClickedPos[button] = g.IO.MousePos;
3021 }
3022 
GetMouseCursor()3023 ImGuiMouseCursor ImGui::GetMouseCursor()
3024 {
3025     return GImGui->MouseCursor;
3026 }
3027 
SetMouseCursor(ImGuiMouseCursor cursor_type)3028 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
3029 {
3030     GImGui->MouseCursor = cursor_type;
3031 }
3032 
CaptureKeyboardFromApp(bool capture)3033 void ImGui::CaptureKeyboardFromApp(bool capture)
3034 {
3035     GImGui->CaptureKeyboardNextFrame = capture ? 1 : 0;
3036 }
3037 
CaptureMouseFromApp(bool capture)3038 void ImGui::CaptureMouseFromApp(bool capture)
3039 {
3040     GImGui->CaptureMouseNextFrame = capture ? 1 : 0;
3041 }
3042 
IsItemHovered()3043 bool ImGui::IsItemHovered()
3044 {
3045     ImGuiWindow* window = GetCurrentWindowRead();
3046     return window->DC.LastItemHoveredAndUsable;
3047 }
3048 
IsItemHoveredRect()3049 bool ImGui::IsItemHoveredRect()
3050 {
3051     ImGuiWindow* window = GetCurrentWindowRead();
3052     return window->DC.LastItemHoveredRect;
3053 }
3054 
IsItemActive()3055 bool ImGui::IsItemActive()
3056 {
3057     ImGuiState& g = *GImGui;
3058     if (g.ActiveId)
3059     {
3060         ImGuiWindow* window = GetCurrentWindowRead();
3061         return g.ActiveId == window->DC.LastItemID;
3062     }
3063     return false;
3064 }
3065 
IsAnyItemHovered()3066 bool ImGui::IsAnyItemHovered()
3067 {
3068     return GImGui->HoveredId != 0 || GImGui->HoveredIdPreviousFrame != 0;
3069 }
3070 
IsAnyItemActive()3071 bool ImGui::IsAnyItemActive()
3072 {
3073     return GImGui->ActiveId != 0;
3074 }
3075 
IsItemVisible()3076 bool ImGui::IsItemVisible()
3077 {
3078     ImGuiWindow* window = GetCurrentWindowRead();
3079     ImRect r(window->ClipRect);
3080     return r.Overlaps(window->DC.LastItemRect);
3081 }
3082 
3083 // 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()3084 void ImGui::SetItemAllowOverlap()
3085 {
3086     ImGuiState& g = *GImGui;
3087     if (g.HoveredId == g.CurrentWindow->DC.LastItemID)
3088         g.HoveredIdAllowOverlap = true;
3089     if (g.ActiveId == g.CurrentWindow->DC.LastItemID)
3090         g.ActiveIdAllowOverlap = true;
3091 }
3092 
GetItemRectMin()3093 ImVec2 ImGui::GetItemRectMin()
3094 {
3095     ImGuiWindow* window = GetCurrentWindowRead();
3096     return window->DC.LastItemRect.Min;
3097 }
3098 
GetItemRectMax()3099 ImVec2 ImGui::GetItemRectMax()
3100 {
3101     ImGuiWindow* window = GetCurrentWindowRead();
3102     return window->DC.LastItemRect.Max;
3103 }
3104 
GetItemRectSize()3105 ImVec2 ImGui::GetItemRectSize()
3106 {
3107     ImGuiWindow* window = GetCurrentWindowRead();
3108     return window->DC.LastItemRect.GetSize();
3109 }
3110 
CalcItemRectClosestPoint(const ImVec2 & pos,bool on_edge,float outward)3111 ImVec2 ImGui::CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge, float outward)
3112 {
3113     ImGuiWindow* window = GetCurrentWindowRead();
3114     ImRect rect = window->DC.LastItemRect;
3115     rect.Expand(outward);
3116     return rect.GetClosestPoint(pos, on_edge);
3117 }
3118 
3119 // 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)3120 void ImGui::SetTooltipV(const char* fmt, va_list args)
3121 {
3122     ImGuiState& g = *GImGui;
3123     ImFormatStringV(g.Tooltip, IM_ARRAYSIZE(g.Tooltip), fmt, args);
3124 }
3125 
SetTooltip(const char * fmt,...)3126 void ImGui::SetTooltip(const char* fmt, ...)
3127 {
3128     va_list args;
3129     va_start(args, fmt);
3130     SetTooltipV(fmt, args);
3131     va_end(args);
3132 }
3133 
GetVisibleRect()3134 static ImRect GetVisibleRect()
3135 {
3136     ImGuiState& g = *GImGui;
3137     if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y)
3138         return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax);
3139     return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3140 }
3141 
BeginTooltip()3142 void ImGui::BeginTooltip()
3143 {
3144     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
3145     ImGui::Begin("##Tooltip", NULL, flags);
3146 }
3147 
EndTooltip()3148 void ImGui::EndTooltip()
3149 {
3150     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
3151     ImGui::End();
3152 }
3153 
IsPopupOpen(ImGuiID id)3154 static bool IsPopupOpen(ImGuiID id)
3155 {
3156     ImGuiState& g = *GImGui;
3157     const bool opened = g.OpenedPopupStack.Size > g.CurrentPopupStack.Size && g.OpenedPopupStack[g.CurrentPopupStack.Size].PopupID == id;
3158     return opened;
3159 }
3160 
3161 // Mark popup as open (toggle toward open state).
3162 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
3163 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
3164 // 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)3165 void ImGui::OpenPopupEx(const char* str_id, bool reopen_existing)
3166 {
3167     ImGuiState& g = *GImGui;
3168     ImGuiWindow* window = g.CurrentWindow;
3169     ImGuiID id = window->GetID(str_id);
3170     int current_stack_size = g.CurrentPopupStack.Size;
3171     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)
3172     if (g.OpenedPopupStack.Size < current_stack_size + 1)
3173         g.OpenedPopupStack.push_back(popup_ref);
3174     else if (reopen_existing || g.OpenedPopupStack[current_stack_size].PopupID != id)
3175     {
3176         g.OpenedPopupStack.resize(current_stack_size+1);
3177         g.OpenedPopupStack[current_stack_size] = popup_ref;
3178     }
3179 }
3180 
OpenPopup(const char * str_id)3181 void ImGui::OpenPopup(const char* str_id)
3182 {
3183     ImGui::OpenPopupEx(str_id, false);
3184 }
3185 
CloseInactivePopups()3186 static void CloseInactivePopups()
3187 {
3188     ImGuiState& g = *GImGui;
3189     if (g.OpenedPopupStack.empty())
3190         return;
3191 
3192     // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
3193     // Don't close our own child popup windows
3194     int n = 0;
3195     if (g.FocusedWindow)
3196     {
3197         for (n = 0; n < g.OpenedPopupStack.Size; n++)
3198         {
3199             ImGuiPopupRef& popup = g.OpenedPopupStack[n];
3200             if (!popup.Window)
3201                 continue;
3202             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
3203             if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
3204                 continue;
3205 
3206             bool has_focus = false;
3207             for (int m = n; m < g.OpenedPopupStack.Size && !has_focus; m++)
3208                 has_focus = (g.OpenedPopupStack[m].Window && g.OpenedPopupStack[m].Window->RootWindow == g.FocusedWindow->RootWindow);
3209             if (!has_focus)
3210                 break;
3211         }
3212     }
3213     if (n < g.OpenedPopupStack.Size)   // This test is not required but it allows to set a useful breakpoint on the line below
3214         g.OpenedPopupStack.resize(n);
3215 }
3216 
GetFrontMostModalRootWindow()3217 static ImGuiWindow* GetFrontMostModalRootWindow()
3218 {
3219     ImGuiState& g = *GImGui;
3220     if (!g.OpenedPopupStack.empty())
3221         if (ImGuiWindow* front_most_popup = g.OpenedPopupStack.back().Window)
3222             if (front_most_popup->Flags & ImGuiWindowFlags_Modal)
3223                 return front_most_popup;
3224     return NULL;
3225 }
3226 
ClosePopupToLevel(int remaining)3227 static void ClosePopupToLevel(int remaining)
3228 {
3229     ImGuiState& g = *GImGui;
3230     if (remaining > 0)
3231         ImGui::FocusWindow(g.OpenedPopupStack[remaining-1].Window);
3232     else
3233         ImGui::FocusWindow(g.OpenedPopupStack[0].ParentWindow);
3234     g.OpenedPopupStack.resize(remaining);
3235 }
3236 
ClosePopup(ImGuiID id)3237 static void ClosePopup(ImGuiID id)
3238 {
3239     if (!IsPopupOpen(id))
3240         return;
3241     ImGuiState& g = *GImGui;
3242     ClosePopupToLevel(g.OpenedPopupStack.Size - 1);
3243 }
3244 
3245 // Close the popup we have begin-ed into.
CloseCurrentPopup()3246 void ImGui::CloseCurrentPopup()
3247 {
3248     ImGuiState& g = *GImGui;
3249     int popup_idx = g.CurrentPopupStack.Size - 1;
3250     if (popup_idx < 0 || popup_idx > g.OpenedPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupID != g.OpenedPopupStack[popup_idx].PopupID)
3251         return;
3252     while (popup_idx > 0 && g.OpenedPopupStack[popup_idx].Window && (g.OpenedPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu))
3253         popup_idx--;
3254     ClosePopupToLevel(popup_idx);
3255 }
3256 
ClearSetNextWindowData()3257 static inline void ClearSetNextWindowData()
3258 {
3259     ImGuiState& g = *GImGui;
3260     g.SetNextWindowPosCond = g.SetNextWindowSizeCond = g.SetNextWindowContentSizeCond = g.SetNextWindowCollapsedCond = 0;
3261     g.SetNextWindowFocus = false;
3262 }
3263 
BeginPopupEx(const char * str_id,ImGuiWindowFlags extra_flags)3264 static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags)
3265 {
3266     ImGuiState& g = *GImGui;
3267     ImGuiWindow* window = g.CurrentWindow;
3268     const ImGuiID id = window->GetID(str_id);
3269     if (!IsPopupOpen(id))
3270     {
3271         ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3272         return false;
3273     }
3274 
3275     ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
3276     ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
3277 
3278     char name[32];
3279     if (flags & ImGuiWindowFlags_ChildMenu)
3280         ImFormatString(name, 20, "##menu_%d", g.CurrentPopupStack.Size);    // Recycle windows based on depth
3281     else
3282         ImFormatString(name, 20, "##popup_%08x", id); // Not recycling, so we can close/open during the same frame
3283 
3284     bool opened = ImGui::Begin(name, NULL, flags);
3285     if (!(window->Flags & ImGuiWindowFlags_ShowBorders))
3286         g.CurrentWindow->Flags &= ~ImGuiWindowFlags_ShowBorders;
3287     if (!opened) // opened can be 'false' when the popup is completely clipped (e.g. zero size display)
3288         ImGui::EndPopup();
3289 
3290     return opened;
3291 }
3292 
BeginPopup(const char * str_id)3293 bool ImGui::BeginPopup(const char* str_id)
3294 {
3295     if (GImGui->OpenedPopupStack.Size <= GImGui->CurrentPopupStack.Size)	// Early out for performance
3296     {
3297         ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3298         return false;
3299     }
3300     return BeginPopupEx(str_id, ImGuiWindowFlags_ShowBorders);
3301 }
3302 
BeginPopupModal(const char * name,bool * p_opened,ImGuiWindowFlags extra_flags)3303 bool ImGui::BeginPopupModal(const char* name, bool* p_opened, ImGuiWindowFlags extra_flags)
3304 {
3305     ImGuiState& g = *GImGui;
3306     ImGuiWindow* window = g.CurrentWindow;
3307     const ImGuiID id = window->GetID(name);
3308     if (!IsPopupOpen(id))
3309     {
3310         ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3311         return false;
3312     }
3313 
3314     ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoSavedSettings;
3315     bool opened = ImGui::Begin(name, p_opened, flags);
3316     if (!opened || (p_opened && !*p_opened)) // Opened can be 'false' when the popup is completely clipped (e.g. zero size display)
3317     {
3318         ImGui::EndPopup();
3319         if (opened)
3320             ClosePopup(id);
3321         return false;
3322     }
3323 
3324     return opened;
3325 }
3326 
EndPopup()3327 void ImGui::EndPopup()
3328 {
3329     ImGuiWindow* window = GetCurrentWindow();
3330     IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
3331     IM_ASSERT(GImGui->CurrentPopupStack.Size > 0);
3332     ImGui::End();
3333     if (!(window->Flags & ImGuiWindowFlags_Modal))
3334         ImGui::PopStyleVar();
3335 }
3336 
3337 // This is a helper to handle the most simple case of associating one named popup to one given widget.
3338 // 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
3339 //    this yourself so you can store data relative to the widget that opened the popup instead of choosing different popup identifiers.
3340 // 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()
3341 //    and passing true to the OpenPopupEx().
3342 //    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
3343 //    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
3344 //    driven by click position.
BeginPopupContextItem(const char * str_id,int mouse_button)3345 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
3346 {
3347     if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(mouse_button))
3348         ImGui::OpenPopupEx(str_id, false);
3349     return ImGui::BeginPopup(str_id);
3350 }
3351 
BeginPopupContextWindow(bool also_over_items,const char * str_id,int mouse_button)3352 bool ImGui::BeginPopupContextWindow(bool also_over_items, const char* str_id, int mouse_button)
3353 {
3354     if (!str_id) str_id = "window_context_menu";
3355     if (ImGui::IsMouseHoveringWindow() && ImGui::IsMouseClicked(mouse_button))
3356         if (also_over_items || !ImGui::IsAnyItemHovered())
3357             ImGui::OpenPopupEx(str_id, true);
3358     return ImGui::BeginPopup(str_id);
3359 }
3360 
BeginPopupContextVoid(const char * str_id,int mouse_button)3361 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
3362 {
3363     if (!str_id) str_id = "void_context_menu";
3364     if (!ImGui::IsMouseHoveringAnyWindow() && ImGui::IsMouseClicked(mouse_button))
3365         ImGui::OpenPopupEx(str_id, true);
3366     return ImGui::BeginPopup(str_id);
3367 }
3368 
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)3369 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
3370 {
3371     ImGuiWindow* window = GetCurrentWindow();
3372     ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
3373 
3374     const ImVec2 content_avail = ImGui::GetContentRegionAvail();
3375     ImVec2 size = ImRound(size_arg);
3376     if (size.x <= 0.0f)
3377     {
3378         if (size.x == 0.0f)
3379             flags |= ImGuiWindowFlags_ChildWindowAutoFitX;
3380         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)
3381     }
3382     if (size.y <= 0.0f)
3383     {
3384         if (size.y == 0.0f)
3385             flags |= ImGuiWindowFlags_ChildWindowAutoFitY;
3386         size.y = ImMax(content_avail.y, 4.0f) - fabsf(size.y);
3387     }
3388     if (border)
3389         flags |= ImGuiWindowFlags_ShowBorders;
3390     flags |= extra_flags;
3391 
3392     char title[256];
3393     ImFormatString(title, IM_ARRAYSIZE(title), "%s.%s", window->Name, str_id);
3394 
3395     bool ret = ImGui::Begin(title, NULL, size, -1.0f, flags);
3396 
3397     if (!(window->Flags & ImGuiWindowFlags_ShowBorders))
3398         GetCurrentWindow()->Flags &= ~ImGuiWindowFlags_ShowBorders;
3399 
3400     return ret;
3401 }
3402 
BeginChild(ImGuiID id,const ImVec2 & size,bool border,ImGuiWindowFlags extra_flags)3403 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size, bool border, ImGuiWindowFlags extra_flags)
3404 {
3405     char str_id[32];
3406     ImFormatString(str_id, IM_ARRAYSIZE(str_id), "child_%08x", id);
3407     bool ret = ImGui::BeginChild(str_id, size, border, extra_flags);
3408     return ret;
3409 }
3410 
EndChild()3411 void ImGui::EndChild()
3412 {
3413     ImGuiWindow* window = GetCurrentWindow();
3414 
3415     IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() callss
3416     if ((window->Flags & ImGuiWindowFlags_ComboBox) || window->BeginCount > 1)
3417     {
3418         ImGui::End();
3419     }
3420     else
3421     {
3422         // 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.
3423         ImVec2 sz = ImGui::GetWindowSize();
3424         if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitX) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
3425             sz.x = ImMax(4.0f, sz.x);
3426         if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitY)
3427             sz.y = ImMax(4.0f, sz.y);
3428 
3429         ImGui::End();
3430 
3431         window = GetCurrentWindow();
3432         ImRect bb(window->DC.CursorPos, window->DC.CursorPos + sz);
3433         ItemSize(sz);
3434         ItemAdd(bb, NULL);
3435     }
3436 }
3437 
3438 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)3439 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
3440 {
3441     ImGuiState& g = *GImGui;
3442     const ImGuiStyle& style = g.Style;
3443     ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, style.Colors[ImGuiCol_FrameBg]);
3444     ImGui::PushStyleVar(ImGuiStyleVar_ChildWindowRounding, style.FrameRounding);
3445     ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
3446     return ImGui::BeginChild(id, size, (g.CurrentWindow->Flags & ImGuiWindowFlags_ShowBorders) ? true : false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
3447 }
3448 
EndChildFrame()3449 void ImGui::EndChildFrame()
3450 {
3451     ImGui::EndChild();
3452     ImGui::PopStyleVar(2);
3453     ImGui::PopStyleColor();
3454 }
3455 
3456 // Save and compare stack sizes on Begin()/End() to detect usage errors
CheckStacksSize(ImGuiWindow * window,bool write)3457 static void CheckStacksSize(ImGuiWindow* window, bool write)
3458 {
3459     // 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)
3460     ImGuiState& g = *GImGui;
3461     int* p_backup = &window->DC.StackSizesBackup[0];
3462     { int current = window->IDStack.Size;       if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; }    // User forgot PopID()
3463     { int current = window->DC.GroupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; }    // User forgot EndGroup()
3464     { int current = g.CurrentPopupStack.Size;   if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; }    // User forgot EndPopup()/EndMenu()
3465     { int current = g.ColorModifiers.Size;      if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; }    // User forgot PopStyleColor()
3466     { int current = g.StyleModifiers.Size;      if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; }    // User forgot PopStyleVar()
3467     { int current = g.FontStack.Size;           if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; }    // User forgot PopFont()
3468     IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
3469 }
3470 
FindBestPopupWindowPos(const ImVec2 & base_pos,const ImVec2 & size,int * last_dir,const ImRect & r_inner)3471 static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& r_inner)
3472 {
3473     const ImGuiStyle& style = GImGui->Style;
3474 
3475     // Clamp into visible area while not overlapping the cursor. Safety padding is optional if our popup size won't fit without it.
3476     ImVec2 safe_padding = style.DisplaySafeAreaPadding;
3477     ImRect r_outer(GetVisibleRect());
3478     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));
3479     ImVec2 base_pos_clamped = ImClamp(base_pos, r_outer.Min, r_outer.Max - size);
3480 
3481     for (int n = (*last_dir != -1) ? -1 : 0; n < 4; n++)   // Last, Right, down, up, left. (Favor last used direction).
3482     {
3483         const int dir = (n == -1) ? *last_dir : n;
3484         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);
3485         if (rect.GetWidth() < size.x || rect.GetHeight() < size.y)
3486             continue;
3487         *last_dir = dir;
3488         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);
3489     }
3490 
3491     // Fallback, try to keep within display
3492     *last_dir = -1;
3493     ImVec2 pos = base_pos;
3494     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
3495     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
3496     return pos;
3497 }
3498 
FindWindowByName(const char * name)3499 ImGuiWindow* ImGui::FindWindowByName(const char* name)
3500 {
3501     // FIXME-OPT: Store sorted hashes -> pointers so we can do a bissection in a contiguous block
3502     ImGuiState& g = *GImGui;
3503     ImGuiID id = ImHash(name, 0);
3504     for (int i = 0; i < g.Windows.Size; i++)
3505         if (g.Windows[i]->ID == id)
3506             return g.Windows[i];
3507     return NULL;
3508 }
3509 
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)3510 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
3511 {
3512     ImGuiState& g = *GImGui;
3513 
3514     // Create window the first time
3515     ImGuiWindow* window = (ImGuiWindow*)ImGui::MemAlloc(sizeof(ImGuiWindow));
3516     IM_PLACEMENT_NEW(window) ImGuiWindow(name);
3517     window->Flags = flags;
3518 
3519     if (flags & ImGuiWindowFlags_NoSavedSettings)
3520     {
3521         // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
3522         window->Size = window->SizeFull = size;
3523     }
3524     else
3525     {
3526         // Retrieve settings from .ini file
3527         // Use SetWindowPos() or SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
3528         window->PosFloat = ImVec2(60, 60);
3529         window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
3530 
3531         ImGuiIniData* settings = FindWindowSettings(name);
3532         if (!settings)
3533         {
3534             settings = AddWindowSettings(name);
3535         }
3536         else
3537         {
3538             window->SetWindowPosAllowFlags &= ~ImGuiSetCond_FirstUseEver;
3539             window->SetWindowSizeAllowFlags &= ~ImGuiSetCond_FirstUseEver;
3540             window->SetWindowCollapsedAllowFlags &= ~ImGuiSetCond_FirstUseEver;
3541         }
3542 
3543         if (settings->Pos.x != FLT_MAX)
3544         {
3545             window->PosFloat = settings->Pos;
3546             window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
3547             window->Collapsed = settings->Collapsed;
3548         }
3549 
3550         if (ImLengthSqr(settings->Size) > 0.00001f && !(flags & ImGuiWindowFlags_NoResize))
3551             size = settings->Size;
3552         window->Size = window->SizeFull = size;
3553     }
3554 
3555     if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
3556     {
3557         window->AutoFitFramesX = window->AutoFitFramesY = 2;
3558         window->AutoFitOnlyGrows = false;
3559     }
3560     else
3561     {
3562         if (window->Size.x <= 0.0f)
3563             window->AutoFitFramesX = 2;
3564         if (window->Size.y <= 0.0f)
3565             window->AutoFitFramesY = 2;
3566         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
3567     }
3568 
3569     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
3570         g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once
3571     else
3572         g.Windows.push_back(window);
3573     return window;
3574 }
3575 
3576 // Push a new ImGui window to add widgets to.
3577 // - 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.
3578 // - Begin/End can be called multiple times during the frame with the same window name to append content.
3579 // - '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.
3580 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
3581 //   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.
3582 // - 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.
3583 // - Passing 'bool* p_opened' 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.
3584 // - Passing non-zero 'size' is roughly equivalent to calling SetNextWindowSize(size, ImGuiSetCond_FirstUseEver) prior to calling Begin().
Begin(const char * name,bool * p_opened,ImGuiWindowFlags flags)3585 bool ImGui::Begin(const char* name, bool* p_opened, ImGuiWindowFlags flags)
3586 {
3587     return ImGui::Begin(name, p_opened, ImVec2(0.f, 0.f), -1.0f, flags);
3588 }
3589 
Begin(const char * name,bool * p_opened,const ImVec2 & size_on_first_use,float bg_alpha,ImGuiWindowFlags flags)3590 bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_use, float bg_alpha, ImGuiWindowFlags flags)
3591 {
3592     ImGuiState& g = *GImGui;
3593     const ImGuiStyle& style = g.Style;
3594     IM_ASSERT(name != NULL);                        // Window name required
3595     IM_ASSERT(g.Initialized);                       // Forgot to call ImGui::NewFrame()
3596     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
3597 
3598     if (flags & ImGuiWindowFlags_NoInputs)
3599         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
3600 
3601     // Find or create
3602     bool window_is_new = false;
3603     ImGuiWindow* window = FindWindowByName(name);
3604     if (!window)
3605     {
3606         window = CreateNewWindow(name, size_on_first_use, flags);
3607         window_is_new = true;
3608     }
3609 
3610     const int current_frame = ImGui::GetFrameCount();
3611     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
3612     if (first_begin_of_the_frame)
3613         window->Flags = (ImGuiWindowFlags)flags;
3614     else
3615         flags = window->Flags;
3616 
3617     // Add to stack
3618     ImGuiWindow* parent_window = !g.CurrentWindowStack.empty() ? g.CurrentWindowStack.back() : NULL;
3619     g.CurrentWindowStack.push_back(window);
3620     SetCurrentWindow(window);
3621     CheckStacksSize(window, true);
3622     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
3623 
3624     bool window_was_active = (window->LastFrameActive == current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
3625     if (flags & ImGuiWindowFlags_Popup)
3626     {
3627         ImGuiPopupRef& popup_ref = g.OpenedPopupStack[g.CurrentPopupStack.Size];
3628         window_was_active &= (window->PopupID == popup_ref.PopupID);
3629         window_was_active &= (window == popup_ref.Window);
3630         popup_ref.Window = window;
3631         g.CurrentPopupStack.push_back(popup_ref);
3632         window->PopupID = popup_ref.PopupID;
3633     }
3634 
3635     const bool window_appearing_after_being_hidden = (window->HiddenFrames == 1);
3636 
3637     // Process SetNextWindow***() calls
3638     bool window_pos_set_by_api = false, window_size_set_by_api = false;
3639     if (g.SetNextWindowPosCond)
3640     {
3641         const ImVec2 backup_cursor_pos = window->DC.CursorPos;                  // FIXME: not sure of the exact reason of this anymore :( need to look into that.
3642         if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowPosAllowFlags |= ImGuiSetCond_Appearing;
3643         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.SetNextWindowPosCond) != 0;
3644         if (window_pos_set_by_api && ImLengthSqr(g.SetNextWindowPosVal - ImVec2(-FLT_MAX,-FLT_MAX)) < 0.001f)
3645         {
3646             window->SetWindowPosCenterWanted = true;                            // May be processed on the next frame if this is our first frame and we are measuring size
3647             window->SetWindowPosAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
3648         }
3649         else
3650         {
3651             SetWindowPos(window, g.SetNextWindowPosVal, g.SetNextWindowPosCond);
3652         }
3653         window->DC.CursorPos = backup_cursor_pos;
3654         g.SetNextWindowPosCond = 0;
3655     }
3656     if (g.SetNextWindowSizeCond)
3657     {
3658         if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowSizeAllowFlags |= ImGuiSetCond_Appearing;
3659         window_size_set_by_api = (window->SetWindowSizeAllowFlags & g.SetNextWindowSizeCond) != 0;
3660         SetWindowSize(window, g.SetNextWindowSizeVal, g.SetNextWindowSizeCond);
3661         g.SetNextWindowSizeCond = 0;
3662     }
3663     if (g.SetNextWindowContentSizeCond)
3664     {
3665         window->SizeContentsExplicit = g.SetNextWindowContentSizeVal;
3666         g.SetNextWindowContentSizeCond = 0;
3667     }
3668     else if (first_begin_of_the_frame)
3669     {
3670         window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
3671     }
3672     if (g.SetNextWindowCollapsedCond)
3673     {
3674         if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowCollapsedAllowFlags |= ImGuiSetCond_Appearing;
3675         SetWindowCollapsed(window, g.SetNextWindowCollapsedVal, g.SetNextWindowCollapsedCond);
3676         g.SetNextWindowCollapsedCond = 0;
3677     }
3678     if (g.SetNextWindowFocus)
3679     {
3680         ImGui::SetWindowFocus();
3681         g.SetNextWindowFocus = false;
3682     }
3683 
3684     // Update known root window (if we are a child window, otherwise window == window->RootWindow)
3685     int root_idx, root_non_popup_idx;
3686     for (root_idx = g.CurrentWindowStack.Size - 1; root_idx > 0; root_idx--)
3687         if (!(g.CurrentWindowStack[root_idx]->Flags & ImGuiWindowFlags_ChildWindow))
3688             break;
3689     for (root_non_popup_idx = root_idx; root_non_popup_idx > 0; root_non_popup_idx--)
3690         if (!(g.CurrentWindowStack[root_non_popup_idx]->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
3691             break;
3692     window->RootWindow = g.CurrentWindowStack[root_idx];
3693     window->RootNonPopupWindow = g.CurrentWindowStack[root_non_popup_idx];      // This is merely for displaying the TitleBgActive color.
3694 
3695     // When reusing window again multiple times a frame, just append content (don't need to setup again)
3696     if (first_begin_of_the_frame)
3697     {
3698         window->Active = true;
3699         window->BeginCount = 0;
3700         window->DrawList->Clear();
3701         window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
3702         window->LastFrameActive = current_frame;
3703         window->IDStack.resize(1);
3704 
3705         // Setup texture, outer clipping rectangle
3706         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
3707         ImRect fullscreen_rect(GetVisibleRect());
3708         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_ComboBox|ImGuiWindowFlags_Popup)))
3709             PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
3710         else
3711             PushClipRect(fullscreen_rect.Min, fullscreen_rect.Max, true);
3712 
3713         // New windows appears in front
3714         if (!window_was_active)
3715         {
3716             window->AutoPosLastDirection = -1;
3717 
3718             if (!(flags & ImGuiWindowFlags_NoFocusOnAppearing))
3719                 if (!(flags & (ImGuiWindowFlags_ChildWindow|ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
3720                     FocusWindow(window);
3721 
3722             // Popup first latch mouse position, will position itself when it appears next frame
3723             if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
3724                 window->PosFloat = g.IO.MousePos;
3725         }
3726 
3727         // Collapse window by double-clicking on title bar
3728         // 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
3729         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
3730         {
3731             ImRect title_bar_rect = window->TitleBarRect();
3732             if (g.HoveredWindow == window && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
3733             {
3734                 window->Collapsed = !window->Collapsed;
3735                 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
3736                     MarkSettingsDirty();
3737                 FocusWindow(window);
3738             }
3739         }
3740         else
3741         {
3742             window->Collapsed = false;
3743         }
3744 
3745         // SIZE
3746 
3747         // Save contents size from last frame for auto-fitting (unless explicitly specified)
3748         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));
3749         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));
3750 
3751         // Hide popup/tooltip window when first appearing while we measure size (because we recycle them)
3752         if (window->HiddenFrames > 0)
3753             window->HiddenFrames--;
3754         if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && !window_was_active)
3755         {
3756             window->HiddenFrames = 1;
3757             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
3758             {
3759                 if (!window_size_set_by_api)
3760                     window->Size = window->SizeFull = ImVec2(0.f, 0.f);
3761                 window->SizeContents = ImVec2(0.f, 0.f);
3762             }
3763         }
3764 
3765         // Lock window padding so that altering the ShowBorders flag for children doesn't have side-effects.
3766         window->WindowPadding = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_ComboBox | ImGuiWindowFlags_Popup))) ? ImVec2(0,0) : style.WindowPadding;
3767 
3768         // Calculate auto-fit size
3769         ImVec2 size_auto_fit;
3770         if ((flags & ImGuiWindowFlags_Tooltip) != 0)
3771         {
3772             // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose.
3773             size_auto_fit = window->SizeContents + window->WindowPadding - ImVec2(0.0f, style.ItemSpacing.y);
3774         }
3775         else
3776         {
3777             size_auto_fit = ImClamp(window->SizeContents + window->WindowPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding));
3778 
3779             // 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.
3780             if (size_auto_fit.x < window->SizeContents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar))
3781                 size_auto_fit.y += style.ScrollbarSize;
3782             if (size_auto_fit.y < window->SizeContents.y && !(flags & ImGuiWindowFlags_NoScrollbar))
3783                 size_auto_fit.x += style.ScrollbarSize;
3784             size_auto_fit.y = ImMax(size_auto_fit.y - style.ItemSpacing.y, 0.0f);
3785         }
3786 
3787         // Handle automatic resize
3788         if (window->Collapsed)
3789         {
3790             // We still process initial auto-fit on collapsed windows to get a window width,
3791             // But otherwise we don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
3792             if (window->AutoFitFramesX > 0)
3793                 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
3794             if (window->AutoFitFramesY > 0)
3795                 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
3796             window->Size = window->TitleBarRect().GetSize();
3797         }
3798         else
3799         {
3800             if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window_size_set_by_api)
3801             {
3802                 window->SizeFull = size_auto_fit;
3803             }
3804             else if ((window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) && !window_size_set_by_api)
3805             {
3806                 // Auto-fit only grows during the first few frames
3807                 if (window->AutoFitFramesX > 0)
3808                     window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
3809                 if (window->AutoFitFramesY > 0)
3810                     window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
3811                 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
3812                     MarkSettingsDirty();
3813             }
3814             window->Size = window->SizeFull;
3815         }
3816 
3817         // Minimum window size
3818         if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
3819         {
3820             window->SizeFull = ImMax(window->SizeFull, style.WindowMinSize);
3821             if (!window->Collapsed)
3822                 window->Size = window->SizeFull;
3823         }
3824 
3825         // POSITION
3826 
3827         // Position child window
3828         if (flags & ImGuiWindowFlags_ChildWindow)
3829             parent_window->DC.ChildWindows.push_back(window);
3830         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup))
3831         {
3832             window->Pos = window->PosFloat = parent_window->DC.CursorPos;
3833             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.
3834         }
3835 
3836         bool window_pos_center = false;
3837         window_pos_center |= (window->SetWindowPosCenterWanted && window->HiddenFrames == 0);
3838         window_pos_center |= ((flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api && window_appearing_after_being_hidden);
3839         if (window_pos_center)
3840         {
3841             // Center (any sort of window)
3842             SetWindowPos(ImMax(style.DisplaySafeAreaPadding, fullscreen_rect.GetCenter() - window->SizeFull * 0.5f));
3843         }
3844         else if (flags & ImGuiWindowFlags_ChildMenu)
3845         {
3846             IM_ASSERT(window_pos_set_by_api);
3847             ImRect rect_to_avoid;
3848             if (parent_window->DC.MenuBarAppending)
3849                 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());
3850             else
3851                 rect_to_avoid = ImRect(parent_window->Pos.x + style.ItemSpacing.x, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - style.ItemSpacing.x - parent_window->ScrollbarSizes.x, FLT_MAX); // We want some overlap to convey the relative depth of each popup (here hard-coded to 4)
3852             window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
3853         }
3854         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_appearing_after_being_hidden)
3855         {
3856             ImRect rect_to_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1);
3857             window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
3858         }
3859 
3860         // Position tooltip (always follows mouse)
3861         if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api)
3862         {
3863             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?
3864             window->PosFloat = FindBestPopupWindowPos(g.IO.MousePos, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
3865             if (window->AutoPosLastDirection == -1)
3866                 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.
3867         }
3868 
3869         // User moving window (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows.
3870         KeepAliveID(window->MoveID);
3871         if (g.ActiveId == window->MoveID)
3872         {
3873             if (g.IO.MouseDown[0])
3874             {
3875                 if (!(flags & ImGuiWindowFlags_NoMove))
3876                 {
3877                     window->PosFloat += g.IO.MouseDelta;
3878                     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
3879                         MarkSettingsDirty();
3880                 }
3881                 IM_ASSERT(g.MovedWindow != NULL);
3882                 FocusWindow(g.MovedWindow);
3883             }
3884             else
3885             {
3886                 SetActiveID(0);
3887                 g.MovedWindow = NULL;   // Not strictly necessary but doing it for sanity.
3888             }
3889         }
3890 
3891         // Clamp position so it stays visible
3892         if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
3893         {
3894             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.
3895             {
3896                 ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
3897                 window->PosFloat = ImMax(window->PosFloat + window->Size, padding) - window->Size;
3898                 window->PosFloat = ImMin(window->PosFloat, g.IO.DisplaySize - padding);
3899             }
3900         }
3901         window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
3902 
3903         // Default item width. Make it proportional to window size if window manually resizes
3904         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
3905             window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
3906         else
3907             window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
3908 
3909         // Prepare for focus requests
3910         window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == IM_INT_MAX || window->FocusIdxAllCounter == -1) ? IM_INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1);
3911         window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == IM_INT_MAX || window->FocusIdxTabCounter == -1) ? IM_INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1);
3912         window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1;
3913         window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = IM_INT_MAX;
3914 
3915         // Apply scrolling
3916         if (window->ScrollTarget.x < FLT_MAX)
3917         {
3918             window->Scroll.x = window->ScrollTarget.x;
3919             window->ScrollTarget.x = FLT_MAX;
3920         }
3921         if (window->ScrollTarget.y < FLT_MAX)
3922         {
3923             float center_ratio = window->ScrollTargetCenterRatio.y;
3924             window->Scroll.y = window->ScrollTarget.y - ((1.0f - center_ratio) * window->TitleBarHeight()) - (center_ratio * window->SizeFull.y);
3925             window->ScrollTarget.y = FLT_MAX;
3926         }
3927         window->Scroll = ImMax(window->Scroll, ImVec2(0.0f, 0.0f));
3928         if (!window->Collapsed && !window->SkipItems)
3929             window->Scroll = ImMin(window->Scroll, ImMax(ImVec2(0.0f, 0.0f), window->SizeContents - window->SizeFull + window->ScrollbarSizes));
3930 
3931         // Modal window darkens what is behind them
3932         if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow())
3933             window->DrawList->AddRectFilled(fullscreen_rect.Min, fullscreen_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio));
3934 
3935         // Draw window + handle manual resize
3936         ImRect title_bar_rect = window->TitleBarRect();
3937         const float window_rounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding;
3938         if (window->Collapsed)
3939         {
3940             // Draw title bar only
3941             RenderFrame(title_bar_rect.GetTL(), title_bar_rect.GetBR(),  GetColorU32(ImGuiCol_TitleBgCollapsed), true, window_rounding);
3942         }
3943         else
3944         {
3945             ImU32 resize_col = 0;
3946             const float resize_corner_size = ImMax(g.FontSize * 1.35f, window_rounding + 1.0f + g.FontSize * 0.2f);
3947             if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && !(flags & ImGuiWindowFlags_NoResize))
3948             {
3949                 // Manual resize
3950                 const ImVec2 br = window->Rect().GetBR();
3951                 const ImRect resize_rect(br - ImVec2(resize_corner_size * 0.75f, resize_corner_size * 0.75f), br);
3952                 const ImGuiID resize_id = window->GetID("#RESIZE");
3953                 bool hovered, held;
3954                 ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds);
3955                 resize_col = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
3956 
3957                 if (hovered || held)
3958                     g.MouseCursor = ImGuiMouseCursor_ResizeNWSE;
3959 
3960                 if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0])
3961                 {
3962                     // Manual auto-fit when double-clicking
3963                     window->SizeFull = size_auto_fit;
3964                     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
3965                         MarkSettingsDirty();
3966                     SetActiveID(0);
3967                 }
3968                 else if (held)
3969                 {
3970                     window->SizeFull = ImMax(window->SizeFull + g.IO.MouseDelta, style.WindowMinSize);
3971                     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
3972                         MarkSettingsDirty();
3973                 }
3974 
3975                 window->Size = window->SizeFull;
3976                 title_bar_rect = window->TitleBarRect();
3977             }
3978 
3979             // Scrollbars
3980             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > window->Size.y + style.ItemSpacing.y) && !(flags & ImGuiWindowFlags_NoScrollbar));
3981             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));
3982             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
3983             window->BorderSize = (flags & ImGuiWindowFlags_ShowBorders) ? 1.0f : 0.0f;
3984 
3985             // Window background
3986             // Default alpha
3987             ImGuiCol bg_color_idx = ImGuiCol_WindowBg;
3988             if ((flags & ImGuiWindowFlags_ComboBox) != 0)
3989                 bg_color_idx = ImGuiCol_ComboBg;
3990             else if ((flags & ImGuiWindowFlags_Tooltip) != 0 || (flags & ImGuiWindowFlags_Popup) != 0)
3991                 bg_color_idx = ImGuiCol_PopupBg;
3992             else if ((flags & ImGuiWindowFlags_ChildWindow) != 0)
3993                 bg_color_idx = ImGuiCol_ChildWindowBg;
3994             ImVec4 bg_color = style.Colors[bg_color_idx];
3995             if (bg_alpha >= 0.0f)
3996                 bg_color.w = bg_alpha;
3997             bg_color.w *= style.Alpha;
3998             if (bg_color.w > 0.0f)
3999                 window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size, ColorConvertFloat4ToU32(bg_color), window_rounding);
4000 
4001             // Title bar
4002             if (!(flags & ImGuiWindowFlags_NoTitleBar))
4003                 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, 1|2);
4004 
4005             // Menu bar
4006             if (flags & ImGuiWindowFlags_MenuBar)
4007             {
4008                 ImRect menu_bar_rect = window->MenuBarRect();
4009                 window->DrawList->AddRectFilled(menu_bar_rect.GetTL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, 1|2);
4010             }
4011 
4012             // Scrollbars
4013             if (window->ScrollbarX)
4014                 Scrollbar(window, true);
4015             if (window->ScrollbarY)
4016                 Scrollbar(window, false);
4017 
4018             // Render resize grip
4019             // (after the input handling so we don't have a frame of latency)
4020             if (!(flags & ImGuiWindowFlags_NoResize))
4021             {
4022                 const ImVec2 br = window->Rect().GetBR();
4023                 window->DrawList->PathLineTo(br + ImVec2(-resize_corner_size, -window->BorderSize));
4024                 window->DrawList->PathLineTo(br + ImVec2(-window->BorderSize, -resize_corner_size));
4025                 window->DrawList->PathArcToFast(ImVec2(br.x - window_rounding - window->BorderSize, br.y - window_rounding - window->BorderSize), window_rounding, 0, 3);
4026                 window->DrawList->PathFill(resize_col);
4027             }
4028 
4029             // Borders
4030             if (flags & ImGuiWindowFlags_ShowBorders)
4031             {
4032                 window->DrawList->AddRect(window->Pos+ImVec2(1,1), window->Pos+window->Size+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), window_rounding);
4033                 window->DrawList->AddRect(window->Pos, window->Pos+window->Size, GetColorU32(ImGuiCol_Border), window_rounding);
4034                 if (!(flags & ImGuiWindowFlags_NoTitleBar))
4035                     window->DrawList->AddLine(title_bar_rect.GetBL()+ImVec2(1,0), title_bar_rect.GetBR()-ImVec2(1,0), GetColorU32(ImGuiCol_Border));
4036             }
4037         }
4038 
4039         // Setup drawing context
4040         window->DC.IndentX = 0.0f + window->WindowPadding.x - window->Scroll.x;
4041         window->DC.ColumnsOffsetX = 0.0f;
4042         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.IndentX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y);
4043         window->DC.CursorPos = window->DC.CursorStartPos;
4044         window->DC.CursorPosPrevLine = window->DC.CursorPos;
4045         window->DC.CursorMaxPos = window->DC.CursorStartPos;
4046         window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f;
4047         window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
4048         window->DC.MenuBarAppending = false;
4049         window->DC.MenuBarOffsetX = ImMax(window->WindowPadding.x, style.ItemSpacing.x);
4050         window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
4051         window->DC.ChildWindows.resize(0);
4052         window->DC.LayoutType = ImGuiLayoutType_Vertical;
4053         window->DC.ItemWidth = window->ItemWidthDefault;
4054         window->DC.TextWrapPos = -1.0f; // disabled
4055         window->DC.AllowKeyboardFocus = true;
4056         window->DC.ButtonRepeat = false;
4057         window->DC.ItemWidthStack.resize(0);
4058         window->DC.TextWrapPosStack.resize(0);
4059         window->DC.AllowKeyboardFocusStack.resize(0);
4060         window->DC.ButtonRepeatStack.resize(0);
4061         window->DC.ColorEditMode = ImGuiColorEditMode_UserSelect;
4062         window->DC.ColumnsCurrent = 0;
4063         window->DC.ColumnsCount = 1;
4064         window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
4065         window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.ColumnsStartPosY;
4066         window->DC.TreeDepth = 0;
4067         window->DC.StateStorage = &window->StateStorage;
4068         window->DC.GroupStack.resize(0);
4069         window->MenuColumns.Update(3, style.ItemSpacing.x, !window_was_active);
4070 
4071         if (window->AutoFitFramesX > 0)
4072             window->AutoFitFramesX--;
4073         if (window->AutoFitFramesY > 0)
4074             window->AutoFitFramesY--;
4075 
4076         // Title bar
4077         if (!(flags & ImGuiWindowFlags_NoTitleBar))
4078         {
4079             if (p_opened != NULL)
4080                 CloseWindowButton(p_opened);
4081 
4082             const ImVec2 text_size = CalcTextSize(name, NULL, true);
4083             if (!(flags & ImGuiWindowFlags_NoCollapse))
4084                 RenderCollapseTriangle(window->Pos + style.FramePadding, !window->Collapsed, 1.0f, true);
4085 
4086             ImVec2 text_min = window->Pos + style.FramePadding;
4087             ImVec2 text_max = window->Pos + ImVec2(window->Size.x - style.FramePadding.x, style.FramePadding.y*2 + text_size.y);
4088             ImVec2 clip_max = ImVec2(window->Pos.x + window->Size.x - (p_opened ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x), text_max.y); // Match the size of CloseWindowButton()
4089             bool pad_left = (flags & ImGuiWindowFlags_NoCollapse) == 0;
4090             bool pad_right = (p_opened != NULL);
4091             if (style.WindowTitleAlign & ImGuiAlign_Center) pad_right = pad_left;
4092             if (pad_left) text_min.x += g.FontSize + style.ItemInnerSpacing.x;
4093             if (pad_right) text_max.x -= g.FontSize + style.ItemInnerSpacing.x;
4094             RenderTextClipped(text_min, text_max, name, NULL, &text_size, style.WindowTitleAlign, NULL, &clip_max);
4095         }
4096 
4097         // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
4098         window->ClippedWindowRect = window->Rect();
4099         window->ClippedWindowRect.Clip(window->ClipRect);
4100 
4101         // Pressing CTRL+C while holding on a window copy its content to the clipboard
4102         // 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.
4103         // Maybe we can support CTRL+C on every element?
4104         /*
4105         if (g.ActiveId == move_id)
4106             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
4107                 ImGui::LogToClipboard();
4108         */
4109     }
4110 
4111     // Inner clipping rectangle
4112     // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
4113     // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior.
4114     const ImRect title_bar_rect = window->TitleBarRect();
4115     const float border_size = window->BorderSize;
4116     ImRect clip_rect;
4117     clip_rect.Min.x = title_bar_rect.Min.x + 0.5f + ImMax(border_size, window->WindowPadding.x*0.5f);
4118     clip_rect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + 0.5f + border_size;
4119     clip_rect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - ImMax(border_size, window->WindowPadding.x*0.5f);
4120     clip_rect.Max.y = window->Pos.y + window->Size.y - border_size - window->ScrollbarSizes.y;
4121     PushClipRect(clip_rect.Min, clip_rect.Max, true);
4122 
4123     // Clear 'accessed' flag last thing
4124     if (first_begin_of_the_frame)
4125         window->Accessed = false;
4126     window->BeginCount++;
4127 
4128     // Child window can be out of sight and have "negative" clip windows.
4129     // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar).
4130     if (flags & ImGuiWindowFlags_ChildWindow)
4131     {
4132         IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
4133         window->Collapsed = parent_window && parent_window->Collapsed;
4134 
4135         if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
4136             window->Collapsed |= (window->ClippedWindowRect.Min.x >= window->ClippedWindowRect.Max.x || window->ClippedWindowRect.Min.y >= window->ClippedWindowRect.Max.y);
4137 
4138         // We also hide the window from rendering because we've already added its border to the command list.
4139         // (we could perform the check earlier in the function but it is simpler at this point)
4140         if (window->Collapsed)
4141             window->Active = false;
4142     }
4143     if (style.Alpha <= 0.0f)
4144         window->Active = false;
4145 
4146     // Return false if we don't intend to display anything to allow user to perform an early out optimization
4147     window->SkipItems = (window->Collapsed || !window->Active) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0;
4148     return !window->SkipItems;
4149 }
4150 
End()4151 void ImGui::End()
4152 {
4153     ImGuiState& g = *GImGui;
4154     ImGuiWindow* window = g.CurrentWindow;
4155 
4156     ImGui::Columns(1, "#CloseColumns");
4157     PopClipRect();   // inner window clip rectangle
4158 
4159     // Stop logging
4160     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
4161         ImGui::LogFinish();
4162 
4163     // Pop
4164     // NB: we don't clear 'window->RootWindow'. The pointer is allowed to live until the next call to Begin().
4165     g.CurrentWindowStack.pop_back();
4166     if (window->Flags & ImGuiWindowFlags_Popup)
4167         g.CurrentPopupStack.pop_back();
4168     CheckStacksSize(window, false);
4169     SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
4170 }
4171 
4172 // Vertical scrollbar
4173 // The entire piece of code below is rather confusing because:
4174 // - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
4175 // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar
4176 // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.
Scrollbar(ImGuiWindow * window,bool horizontal)4177 static void Scrollbar(ImGuiWindow* window, bool horizontal)
4178 {
4179     ImGuiState& g = *GImGui;
4180     const ImGuiStyle& style = g.Style;
4181     const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY");
4182 
4183     // Render background
4184     bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX);
4185     float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f;
4186     const ImRect window_rect = window->Rect();
4187     const float border_size = window->BorderSize;
4188     ImRect bb = horizontal
4189         ? 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)
4190         : 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);
4191     if (!horizontal)
4192         bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() - border_size : 0.0f);
4193 
4194     float window_rounding = (window->Flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding;
4195     int window_rounding_corners;
4196     if (horizontal)
4197         window_rounding_corners = 8 | (other_scrollbar ? 0 : 4);
4198     else
4199         window_rounding_corners = ((window->Flags & ImGuiWindowFlags_NoTitleBar) ? 2 : 0) | (other_scrollbar ? 0 : 4);
4200     window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_ScrollbarBg), window_rounding, window_rounding_corners);
4201     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)));
4202 
4203     // V denote the main axis of the scrollbar
4204     float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight();
4205     float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y;
4206     float win_size_avail_v = (horizontal ? window->Size.x : window->Size.y) - other_scrollbar_size_w;
4207     float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y;
4208 
4209     // The grabable box size generally represent the amount visible (vs the total scrollable amount)
4210     // But we maintain a minimum size in pixel to allow for the user to still aim inside.
4211     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);
4212     const float grab_h_norm = grab_h_pixels / scrollbar_size_v;
4213 
4214     // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().
4215     bool held = false;
4216     bool hovered = false;
4217     const bool previously_held = (g.ActiveId == id);
4218     ImGui::ButtonBehavior(bb, id, &hovered, &held);
4219 
4220     float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v);
4221     float scroll_ratio = ImSaturate(scroll_v / scroll_max);
4222     float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
4223     if (held && grab_h_norm < 1.0f)
4224     {
4225         float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y;
4226         float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
4227         float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y;
4228 
4229         // Click position in scrollbar normalized space (0.0f->1.0f)
4230         const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
4231         ImGui::SetHoveredID(id);
4232 
4233         bool seek_absolute = false;
4234         if (!previously_held)
4235         {
4236             // On initial click calculate the distance between mouse and the center of the grab
4237             if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm)
4238             {
4239                 *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
4240             }
4241             else
4242             {
4243                 seek_absolute = true;
4244                 *click_delta_to_grab_center_v = 0.0f;
4245             }
4246         }
4247 
4248         // Apply scroll
4249         // 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
4250         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));
4251         scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v));
4252         if (horizontal)
4253             window->Scroll.x = scroll_v;
4254         else
4255             window->Scroll.y = scroll_v;
4256 
4257         // Update values for rendering
4258         scroll_ratio = ImSaturate(scroll_v / scroll_max);
4259         grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
4260 
4261         // Update distance to grab now that we have seeked and saturated
4262         if (seek_absolute)
4263             *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
4264     }
4265 
4266     // Render
4267     const ImU32 grab_col = ImGui::GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab);
4268     if (horizontal)
4269         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);
4270     else
4271         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);
4272 }
4273 
4274 // Moving window to front of display (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)4275 void ImGui::FocusWindow(ImGuiWindow* window)
4276 {
4277     ImGuiState& g = *GImGui;
4278 
4279     // Always mark the window we passed as focused. This is used for keyboard interactions such as tabbing.
4280     g.FocusedWindow = window;
4281 
4282     // Passing NULL allow to disable keyboard focus
4283     if (!window)
4284         return;
4285 
4286     // And move its root window to the top of the pile
4287     if (window->RootWindow)
4288         window = window->RootWindow;
4289 
4290     // Steal focus on active widgets
4291     if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
4292         if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
4293             ImGui::SetActiveID(0);
4294 
4295     // Bring to front
4296     if ((window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) || g.Windows.back() == window)
4297         return;
4298     for (int i = 0; i < g.Windows.Size; i++)
4299         if (g.Windows[i] == window)
4300         {
4301             g.Windows.erase(g.Windows.begin() + i);
4302             break;
4303         }
4304     g.Windows.push_back(window);
4305 }
4306 
PushItemWidth(float item_width)4307 void ImGui::PushItemWidth(float item_width)
4308 {
4309     ImGuiWindow* window = GetCurrentWindow();
4310     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
4311     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
4312 }
4313 
PushMultiItemsWidths(int components,float w_full)4314 static void PushMultiItemsWidths(int components, float w_full)
4315 {
4316     ImGuiWindow* window = ImGui::GetCurrentWindow();
4317     const ImGuiStyle& style = GImGui->Style;
4318     if (w_full <= 0.0f)
4319         w_full = ImGui::CalcItemWidth();
4320     const float w_item_one  = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
4321     const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
4322     window->DC.ItemWidthStack.push_back(w_item_last);
4323     for (int i = 0; i < components-1; i++)
4324         window->DC.ItemWidthStack.push_back(w_item_one);
4325     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
4326 }
4327 
PopItemWidth()4328 void ImGui::PopItemWidth()
4329 {
4330     ImGuiWindow* window = GetCurrentWindow();
4331     window->DC.ItemWidthStack.pop_back();
4332     window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
4333 }
4334 
CalcItemWidth()4335 float ImGui::CalcItemWidth()
4336 {
4337     ImGuiWindow* window = GetCurrentWindowRead();
4338     float w = window->DC.ItemWidth;
4339     if (w < 0.0f)
4340     {
4341         // 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.
4342         float width_to_right_edge = ImGui::GetContentRegionAvail().x;
4343         w = ImMax(1.0f, width_to_right_edge + w);
4344     }
4345     w = (float)(int)w;
4346     return w;
4347 }
4348 
SetCurrentFont(ImFont * font)4349 static void SetCurrentFont(ImFont* font)
4350 {
4351     ImGuiState& g = *GImGui;
4352     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
4353     IM_ASSERT(font->Scale > 0.0f);
4354     g.Font = font;
4355     g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale;
4356     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
4357     g.FontTexUvWhitePixel = g.Font->ContainerAtlas->TexUvWhitePixel;
4358 }
4359 
PushFont(ImFont * font)4360 void ImGui::PushFont(ImFont* font)
4361 {
4362     ImGuiState& g = *GImGui;
4363     if (!font)
4364         font = g.IO.Fonts->Fonts[0];
4365     SetCurrentFont(font);
4366     g.FontStack.push_back(font);
4367     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
4368 }
4369 
PopFont()4370 void  ImGui::PopFont()
4371 {
4372     ImGuiState& g = *GImGui;
4373     g.CurrentWindow->DrawList->PopTextureID();
4374     g.FontStack.pop_back();
4375     SetCurrentFont(g.FontStack.empty() ? g.IO.Fonts->Fonts[0] : g.FontStack.back());
4376 }
4377 
PushAllowKeyboardFocus(bool allow_keyboard_focus)4378 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
4379 {
4380     ImGuiWindow* window = GetCurrentWindow();
4381     window->DC.AllowKeyboardFocus = allow_keyboard_focus;
4382     window->DC.AllowKeyboardFocusStack.push_back(allow_keyboard_focus);
4383 }
4384 
PopAllowKeyboardFocus()4385 void ImGui::PopAllowKeyboardFocus()
4386 {
4387     ImGuiWindow* window = GetCurrentWindow();
4388     window->DC.AllowKeyboardFocusStack.pop_back();
4389     window->DC.AllowKeyboardFocus = window->DC.AllowKeyboardFocusStack.empty() ? true : window->DC.AllowKeyboardFocusStack.back();
4390 }
4391 
PushButtonRepeat(bool repeat)4392 void ImGui::PushButtonRepeat(bool repeat)
4393 {
4394     ImGuiWindow* window = GetCurrentWindow();
4395     window->DC.ButtonRepeat = repeat;
4396     window->DC.ButtonRepeatStack.push_back(repeat);
4397 }
4398 
PopButtonRepeat()4399 void ImGui::PopButtonRepeat()
4400 {
4401     ImGuiWindow* window = GetCurrentWindow();
4402     window->DC.ButtonRepeatStack.pop_back();
4403     window->DC.ButtonRepeat = window->DC.ButtonRepeatStack.empty() ? false : window->DC.ButtonRepeatStack.back();
4404 }
4405 
PushTextWrapPos(float wrap_pos_x)4406 void ImGui::PushTextWrapPos(float wrap_pos_x)
4407 {
4408     ImGuiWindow* window = GetCurrentWindow();
4409     window->DC.TextWrapPos = wrap_pos_x;
4410     window->DC.TextWrapPosStack.push_back(wrap_pos_x);
4411 }
4412 
PopTextWrapPos()4413 void ImGui::PopTextWrapPos()
4414 {
4415     ImGuiWindow* window = GetCurrentWindow();
4416     window->DC.TextWrapPosStack.pop_back();
4417     window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
4418 }
4419 
PushStyleColor(ImGuiCol idx,const ImVec4 & col)4420 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
4421 {
4422     ImGuiState& g = *GImGui;
4423     ImGuiColMod backup;
4424     backup.Col = idx;
4425     backup.PreviousValue = g.Style.Colors[idx];
4426     g.ColorModifiers.push_back(backup);
4427     g.Style.Colors[idx] = col;
4428 }
4429 
PopStyleColor(int count)4430 void ImGui::PopStyleColor(int count)
4431 {
4432     ImGuiState& g = *GImGui;
4433     while (count > 0)
4434     {
4435         ImGuiColMod& backup = g.ColorModifiers.back();
4436         g.Style.Colors[backup.Col] = backup.PreviousValue;
4437         g.ColorModifiers.pop_back();
4438         count--;
4439     }
4440 }
4441 
GetStyleVarFloatAddr(ImGuiStyleVar idx)4442 static float* GetStyleVarFloatAddr(ImGuiStyleVar idx)
4443 {
4444     ImGuiState& g = *GImGui;
4445     switch (idx)
4446     {
4447     case ImGuiStyleVar_Alpha: return &g.Style.Alpha;
4448     case ImGuiStyleVar_WindowRounding: return &g.Style.WindowRounding;
4449     case ImGuiStyleVar_ChildWindowRounding: return &g.Style.ChildWindowRounding;
4450     case ImGuiStyleVar_FrameRounding: return &g.Style.FrameRounding;
4451     case ImGuiStyleVar_IndentSpacing: return &g.Style.IndentSpacing;
4452     case ImGuiStyleVar_GrabMinSize: return &g.Style.GrabMinSize;
4453     }
4454     return NULL;
4455 }
4456 
GetStyleVarVec2Addr(ImGuiStyleVar idx)4457 static ImVec2* GetStyleVarVec2Addr(ImGuiStyleVar idx)
4458 {
4459     ImGuiState& g = *GImGui;
4460     switch (idx)
4461     {
4462     case ImGuiStyleVar_WindowPadding: return &g.Style.WindowPadding;
4463     case ImGuiStyleVar_WindowMinSize: return &g.Style.WindowMinSize;
4464     case ImGuiStyleVar_FramePadding: return &g.Style.FramePadding;
4465     case ImGuiStyleVar_ItemSpacing: return &g.Style.ItemSpacing;
4466     case ImGuiStyleVar_ItemInnerSpacing: return &g.Style.ItemInnerSpacing;
4467     }
4468     return NULL;
4469 }
4470 
PushStyleVar(ImGuiStyleVar idx,float val)4471 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
4472 {
4473     ImGuiState& g = *GImGui;
4474     float* pvar = GetStyleVarFloatAddr(idx);
4475     IM_ASSERT(pvar != NULL); // Called function with wrong-type? Variable is not a float.
4476     ImGuiStyleMod backup;
4477     backup.Var = idx;
4478     backup.PreviousValue = ImVec2(*pvar, 0.0f);
4479     g.StyleModifiers.push_back(backup);
4480     *pvar = val;
4481 }
4482 
4483 
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)4484 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
4485 {
4486     ImGuiState& g = *GImGui;
4487     ImVec2* pvar = GetStyleVarVec2Addr(idx);
4488     IM_ASSERT(pvar != NULL); // Called function with wrong-type? Variable is not a ImVec2.
4489     ImGuiStyleMod backup;
4490     backup.Var = idx;
4491     backup.PreviousValue = *pvar;
4492     g.StyleModifiers.push_back(backup);
4493     *pvar = val;
4494 }
4495 
PopStyleVar(int count)4496 void ImGui::PopStyleVar(int count)
4497 {
4498     ImGuiState& g = *GImGui;
4499     while (count > 0)
4500     {
4501         ImGuiStyleMod& backup = g.StyleModifiers.back();
4502         if (float* pvar_f = GetStyleVarFloatAddr(backup.Var))
4503             *pvar_f = backup.PreviousValue.x;
4504         else if (ImVec2* pvar_v = GetStyleVarVec2Addr(backup.Var))
4505             *pvar_v = backup.PreviousValue;
4506         g.StyleModifiers.pop_back();
4507         count--;
4508     }
4509 }
4510 
GetStyleColName(ImGuiCol idx)4511 const char* ImGui::GetStyleColName(ImGuiCol idx)
4512 {
4513     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
4514     switch (idx)
4515     {
4516     case ImGuiCol_Text: return "Text";
4517     case ImGuiCol_TextDisabled: return "TextDisabled";
4518     case ImGuiCol_WindowBg: return "WindowBg";
4519     case ImGuiCol_ChildWindowBg: return "ChildWindowBg";
4520     case ImGuiCol_PopupBg: return "PopupBg";
4521     case ImGuiCol_Border: return "Border";
4522     case ImGuiCol_BorderShadow: return "BorderShadow";
4523     case ImGuiCol_FrameBg: return "FrameBg";
4524     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
4525     case ImGuiCol_FrameBgActive: return "FrameBgActive";
4526     case ImGuiCol_TitleBg: return "TitleBg";
4527     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
4528     case ImGuiCol_TitleBgActive: return "TitleBgActive";
4529     case ImGuiCol_MenuBarBg: return "MenuBarBg";
4530     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
4531     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
4532     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
4533     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
4534     case ImGuiCol_ComboBg: return "ComboBg";
4535     case ImGuiCol_CheckMark: return "CheckMark";
4536     case ImGuiCol_SliderGrab: return "SliderGrab";
4537     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
4538     case ImGuiCol_Button: return "Button";
4539     case ImGuiCol_ButtonHovered: return "ButtonHovered";
4540     case ImGuiCol_ButtonActive: return "ButtonActive";
4541     case ImGuiCol_Header: return "Header";
4542     case ImGuiCol_HeaderHovered: return "HeaderHovered";
4543     case ImGuiCol_HeaderActive: return "HeaderActive";
4544     case ImGuiCol_Column: return "Column";
4545     case ImGuiCol_ColumnHovered: return "ColumnHovered";
4546     case ImGuiCol_ColumnActive: return "ColumnActive";
4547     case ImGuiCol_ResizeGrip: return "ResizeGrip";
4548     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
4549     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
4550     case ImGuiCol_CloseButton: return "CloseButton";
4551     case ImGuiCol_CloseButtonHovered: return "CloseButtonHovered";
4552     case ImGuiCol_CloseButtonActive: return "CloseButtonActive";
4553     case ImGuiCol_PlotLines: return "PlotLines";
4554     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
4555     case ImGuiCol_PlotHistogram: return "PlotHistogram";
4556     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
4557     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
4558     case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening";
4559     }
4560     IM_ASSERT(0);
4561     return "Unknown";
4562 }
4563 
IsWindowHovered()4564 bool ImGui::IsWindowHovered()
4565 {
4566     ImGuiState& g = *GImGui;
4567     return g.HoveredWindow == g.CurrentWindow && IsWindowContentHoverable(g.HoveredRootWindow);
4568 }
4569 
IsWindowFocused()4570 bool ImGui::IsWindowFocused()
4571 {
4572     ImGuiState& g = *GImGui;
4573     return g.FocusedWindow == g.CurrentWindow;
4574 }
4575 
IsRootWindowFocused()4576 bool ImGui::IsRootWindowFocused()
4577 {
4578     ImGuiState& g = *GImGui;
4579     ImGuiWindow* root_window = g.CurrentWindow->RootWindow;
4580     return g.FocusedWindow == root_window;
4581 }
4582 
IsRootWindowOrAnyChildFocused()4583 bool ImGui::IsRootWindowOrAnyChildFocused()
4584 {
4585     ImGuiState& g = *GImGui;
4586     ImGuiWindow* root_window = g.CurrentWindow->RootWindow;
4587     return g.FocusedWindow && g.FocusedWindow->RootWindow == root_window;
4588 }
4589 
GetWindowWidth()4590 float ImGui::GetWindowWidth()
4591 {
4592     ImGuiWindow* window = GImGui->CurrentWindow;
4593     return window->Size.x;
4594 }
4595 
GetWindowHeight()4596 float ImGui::GetWindowHeight()
4597 {
4598     ImGuiWindow* window = GImGui->CurrentWindow;
4599     return window->Size.y;
4600 }
4601 
GetWindowPos()4602 ImVec2 ImGui::GetWindowPos()
4603 {
4604     ImGuiState& g = *GImGui;
4605     ImGuiWindow* window = g.CurrentWindow;
4606     return window->Pos;
4607 }
4608 
SetWindowScrollY(ImGuiWindow * window,float new_scroll_y)4609 static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
4610 {
4611     window->DC.CursorMaxPos.y += window->Scroll.y;
4612     window->Scroll.y = new_scroll_y;
4613     window->DC.CursorMaxPos.y -= window->Scroll.y;
4614 }
4615 
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiSetCond cond)4616 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond)
4617 {
4618     // Test condition (NB: bit 0 is always true) and clear flags for next time
4619     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
4620         return;
4621     window->SetWindowPosAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
4622     window->SetWindowPosCenterWanted = false;
4623 
4624     // Set
4625     const ImVec2 old_pos = window->Pos;
4626     window->PosFloat = pos;
4627     window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
4628     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
4629     window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected.
4630 }
4631 
SetWindowPos(const ImVec2 & pos,ImGuiSetCond cond)4632 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiSetCond cond)
4633 {
4634     ImGuiWindow* window = GetCurrentWindow();
4635     SetWindowPos(window, pos, cond);
4636 }
4637 
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiSetCond cond)4638 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiSetCond cond)
4639 {
4640     ImGuiWindow* window = FindWindowByName(name);
4641     if (window)
4642         SetWindowPos(window, pos, cond);
4643 }
4644 
GetWindowSize()4645 ImVec2 ImGui::GetWindowSize()
4646 {
4647     ImGuiWindow* window = GetCurrentWindowRead();
4648     return window->Size;
4649 }
4650 
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiSetCond cond)4651 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond)
4652 {
4653     // Test condition (NB: bit 0 is always true) and clear flags for next time
4654     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
4655         return;
4656     window->SetWindowSizeAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
4657 
4658     // Set
4659     if (size.x > 0.0f)
4660     {
4661         window->AutoFitFramesX = 0;
4662         window->SizeFull.x = size.x;
4663     }
4664     else
4665     {
4666         window->AutoFitFramesX = 2;
4667         window->AutoFitOnlyGrows = false;
4668     }
4669     if (size.y > 0.0f)
4670     {
4671         window->AutoFitFramesY = 0;
4672         window->SizeFull.y = size.y;
4673     }
4674     else
4675     {
4676         window->AutoFitFramesY = 2;
4677         window->AutoFitOnlyGrows = false;
4678     }
4679 }
4680 
SetWindowSize(const ImVec2 & size,ImGuiSetCond cond)4681 void ImGui::SetWindowSize(const ImVec2& size, ImGuiSetCond cond)
4682 {
4683     SetWindowSize(GImGui->CurrentWindow, size, cond);
4684 }
4685 
SetWindowSize(const char * name,const ImVec2 & size,ImGuiSetCond cond)4686 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiSetCond cond)
4687 {
4688     ImGuiWindow* window = FindWindowByName(name);
4689     if (window)
4690         SetWindowSize(window, size, cond);
4691 }
4692 
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiSetCond cond)4693 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiSetCond cond)
4694 {
4695     // Test condition (NB: bit 0 is always true) and clear flags for next time
4696     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
4697         return;
4698     window->SetWindowCollapsedAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
4699 
4700     // Set
4701     window->Collapsed = collapsed;
4702 }
4703 
SetWindowCollapsed(bool collapsed,ImGuiSetCond cond)4704 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiSetCond cond)
4705 {
4706     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
4707 }
4708 
IsWindowCollapsed()4709 bool ImGui::IsWindowCollapsed()
4710 {
4711     return GImGui->CurrentWindow->Collapsed;
4712 }
4713 
SetWindowCollapsed(const char * name,bool collapsed,ImGuiSetCond cond)4714 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiSetCond cond)
4715 {
4716     ImGuiWindow* window = FindWindowByName(name);
4717     if (window)
4718         SetWindowCollapsed(window, collapsed, cond);
4719 }
4720 
SetWindowFocus()4721 void ImGui::SetWindowFocus()
4722 {
4723     FocusWindow(GImGui->CurrentWindow);
4724 }
4725 
SetWindowFocus(const char * name)4726 void ImGui::SetWindowFocus(const char* name)
4727 {
4728     if (name)
4729     {
4730         ImGuiWindow* window = FindWindowByName(name);
4731         if (window)
4732             FocusWindow(window);
4733     }
4734     else
4735     {
4736         FocusWindow(NULL);
4737     }
4738 }
4739 
SetNextWindowPos(const ImVec2 & pos,ImGuiSetCond cond)4740 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiSetCond cond)
4741 {
4742     ImGuiState& g = *GImGui;
4743     g.SetNextWindowPosVal = pos;
4744     g.SetNextWindowPosCond = cond ? cond : ImGuiSetCond_Always;
4745 }
4746 
SetNextWindowPosCenter(ImGuiSetCond cond)4747 void ImGui::SetNextWindowPosCenter(ImGuiSetCond cond)
4748 {
4749     ImGuiState& g = *GImGui;
4750     g.SetNextWindowPosVal = ImVec2(-FLT_MAX, -FLT_MAX);
4751     g.SetNextWindowPosCond = cond ? cond : ImGuiSetCond_Always;
4752 }
4753 
SetNextWindowSize(const ImVec2 & size,ImGuiSetCond cond)4754 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiSetCond cond)
4755 {
4756     ImGuiState& g = *GImGui;
4757     g.SetNextWindowSizeVal = size;
4758     g.SetNextWindowSizeCond = cond ? cond : ImGuiSetCond_Always;
4759 }
4760 
SetNextWindowContentSize(const ImVec2 & size)4761 void ImGui::SetNextWindowContentSize(const ImVec2& size)
4762 {
4763     ImGuiState& g = *GImGui;
4764     g.SetNextWindowContentSizeVal = size;
4765     g.SetNextWindowContentSizeCond = ImGuiSetCond_Always;
4766 }
4767 
SetNextWindowContentWidth(float width)4768 void ImGui::SetNextWindowContentWidth(float width)
4769 {
4770     ImGuiState& g = *GImGui;
4771     g.SetNextWindowContentSizeVal = ImVec2(width, g.SetNextWindowContentSizeCond ? g.SetNextWindowContentSizeVal.y : 0.0f);
4772     g.SetNextWindowContentSizeCond = ImGuiSetCond_Always;
4773 }
4774 
SetNextWindowCollapsed(bool collapsed,ImGuiSetCond cond)4775 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiSetCond cond)
4776 {
4777     ImGuiState& g = *GImGui;
4778     g.SetNextWindowCollapsedVal = collapsed;
4779     g.SetNextWindowCollapsedCond = cond ? cond : ImGuiSetCond_Always;
4780 }
4781 
SetNextWindowFocus()4782 void ImGui::SetNextWindowFocus()
4783 {
4784     ImGuiState& g = *GImGui;
4785     g.SetNextWindowFocus = true;
4786 }
4787 
4788 // In window space (not screen space!)
4789 // FIXME-OPT: Could cache and maintain it (pretty much only change on columns change)
GetContentRegionMax()4790 ImVec2 ImGui::GetContentRegionMax()
4791 {
4792     ImGuiWindow* window = GetCurrentWindowRead();
4793     ImVec2 content_region_size = ImVec2(window->SizeContentsExplicit.x ? window->SizeContentsExplicit.x : window->Size.x - window->ScrollbarSizes.x, window->SizeContentsExplicit.y ? window->SizeContentsExplicit.y : window->Size.y - window->ScrollbarSizes.y);
4794     ImVec2 mx = content_region_size - window->Scroll - window->WindowPadding;
4795     if (window->DC.ColumnsCount != 1)
4796         mx.x = ImGui::GetColumnOffset(window->DC.ColumnsCurrent + 1) - window->WindowPadding.x;
4797     return mx;
4798 }
4799 
GetContentRegionAvail()4800 ImVec2 ImGui::GetContentRegionAvail()
4801 {
4802     ImGuiWindow* window = GetCurrentWindowRead();
4803     return GetContentRegionMax() - (window->DC.CursorPos - window->Pos);
4804 }
4805 
GetContentRegionAvailWidth()4806 float ImGui::GetContentRegionAvailWidth()
4807 {
4808     return GetContentRegionAvail().x;
4809 }
4810 
4811 // In window space (not screen space!)
GetWindowContentRegionMin()4812 ImVec2 ImGui::GetWindowContentRegionMin()
4813 {
4814     ImGuiWindow* window = GetCurrentWindowRead();
4815     return ImVec2(-window->Scroll.x, -window->Scroll.y + window->TitleBarHeight() + window->MenuBarHeight()) + window->WindowPadding;
4816 }
4817 
GetWindowContentRegionMax()4818 ImVec2 ImGui::GetWindowContentRegionMax()
4819 {
4820     ImGuiWindow* window = GetCurrentWindowRead();
4821     ImVec2 content_region_size = ImVec2(window->SizeContentsExplicit.x ? window->SizeContentsExplicit.x : window->Size.x, window->SizeContentsExplicit.y ? window->SizeContentsExplicit.y : window->Size.y);
4822     ImVec2 m = content_region_size - window->Scroll - window->WindowPadding - window->ScrollbarSizes;
4823     return m;
4824 }
4825 
GetWindowContentRegionWidth()4826 float ImGui::GetWindowContentRegionWidth()
4827 {
4828     return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x;
4829 }
4830 
GetTextLineHeight()4831 float ImGui::GetTextLineHeight()
4832 {
4833     ImGuiState& g = *GImGui;
4834     return g.FontSize;
4835 }
4836 
GetTextLineHeightWithSpacing()4837 float ImGui::GetTextLineHeightWithSpacing()
4838 {
4839     ImGuiState& g = *GImGui;
4840     return g.FontSize + g.Style.ItemSpacing.y;
4841 }
4842 
GetItemsLineHeightWithSpacing()4843 float ImGui::GetItemsLineHeightWithSpacing()
4844 {
4845     ImGuiState& g = *GImGui;
4846     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
4847 }
4848 
GetWindowDrawList()4849 ImDrawList* ImGui::GetWindowDrawList()
4850 {
4851     ImGuiWindow* window = GetCurrentWindow();
4852     return window->DrawList;
4853 }
4854 
GetFont()4855 ImFont* ImGui::GetFont()
4856 {
4857     return GImGui->Font;
4858 }
4859 
GetFontSize()4860 float ImGui::GetFontSize()
4861 {
4862     return GImGui->FontSize;
4863 }
4864 
GetFontTexUvWhitePixel()4865 ImVec2 ImGui::GetFontTexUvWhitePixel()
4866 {
4867     return GImGui->FontTexUvWhitePixel;
4868 }
4869 
SetWindowFontScale(float scale)4870 void ImGui::SetWindowFontScale(float scale)
4871 {
4872     ImGuiState& g = *GImGui;
4873     ImGuiWindow* window = GetCurrentWindow();
4874     window->FontWindowScale = scale;
4875     g.FontSize = window->CalcFontSize();
4876 }
4877 
4878 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
4879 // 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()4880 ImVec2 ImGui::GetCursorPos()
4881 {
4882     ImGuiWindow* window = GetCurrentWindowRead();
4883     return window->DC.CursorPos - window->Pos + window->Scroll;
4884 }
4885 
GetCursorPosX()4886 float ImGui::GetCursorPosX()
4887 {
4888     ImGuiWindow* window = GetCurrentWindow();
4889     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
4890 }
4891 
GetCursorPosY()4892 float ImGui::GetCursorPosY()
4893 {
4894     ImGuiWindow* window = GetCurrentWindow();
4895     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
4896 }
4897 
SetCursorPos(const ImVec2 & local_pos)4898 void ImGui::SetCursorPos(const ImVec2& local_pos)
4899 {
4900     ImGuiWindow* window = GetCurrentWindow();
4901     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
4902     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
4903 }
4904 
SetCursorPosX(float x)4905 void ImGui::SetCursorPosX(float x)
4906 {
4907     ImGuiWindow* window = GetCurrentWindow();
4908     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
4909     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
4910 }
4911 
SetCursorPosY(float y)4912 void ImGui::SetCursorPosY(float y)
4913 {
4914     ImGuiWindow* window = GetCurrentWindow();
4915     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
4916     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
4917 }
4918 
GetCursorStartPos()4919 ImVec2 ImGui::GetCursorStartPos()
4920 {
4921     ImGuiWindow* window = GetCurrentWindowRead();
4922     return window->DC.CursorStartPos - window->Pos;
4923 }
4924 
GetCursorScreenPos()4925 ImVec2 ImGui::GetCursorScreenPos()
4926 {
4927     ImGuiWindow* window = GetCurrentWindowRead();
4928     return window->DC.CursorPos;
4929 }
4930 
SetCursorScreenPos(const ImVec2 & screen_pos)4931 void ImGui::SetCursorScreenPos(const ImVec2& screen_pos)
4932 {
4933     ImGuiWindow* window = GetCurrentWindow();
4934     window->DC.CursorPos = screen_pos;
4935 }
4936 
GetScrollX()4937 float ImGui::GetScrollX()
4938 {
4939     return GImGui->CurrentWindow->Scroll.x;
4940 }
4941 
GetScrollY()4942 float ImGui::GetScrollY()
4943 {
4944     return GImGui->CurrentWindow->Scroll.y;
4945 }
4946 
GetScrollMaxX()4947 float ImGui::GetScrollMaxX()
4948 {
4949     ImGuiWindow* window = GetCurrentWindowRead();
4950     return window->SizeContents.x - window->SizeFull.x - window->ScrollbarSizes.x;
4951 }
4952 
GetScrollMaxY()4953 float ImGui::GetScrollMaxY()
4954 {
4955     ImGuiWindow* window = GetCurrentWindowRead();
4956     return window->SizeContents.y - window->SizeFull.y - window->ScrollbarSizes.y;
4957 }
4958 
SetScrollX(float scroll_x)4959 void ImGui::SetScrollX(float scroll_x)
4960 {
4961     ImGuiWindow* window = GetCurrentWindow();
4962     window->ScrollTarget.x = scroll_x;
4963     window->ScrollTargetCenterRatio.x = 0.0f;
4964 }
4965 
SetScrollY(float scroll_y)4966 void ImGui::SetScrollY(float scroll_y)
4967 {
4968     ImGuiWindow* window = GetCurrentWindow();
4969     window->ScrollTarget.y = scroll_y + window->TitleBarHeight(); // title bar height canceled out when using ScrollTargetRelY
4970     window->ScrollTargetCenterRatio.y = 0.0f;
4971 }
4972 
SetScrollFromPosY(float pos_y,float center_y_ratio)4973 void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio)
4974 {
4975     // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
4976     ImGuiWindow* window = GetCurrentWindow();
4977     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
4978     window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y);
4979     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)
4980         window->ScrollTarget.y = 0.0f;
4981     window->ScrollTargetCenterRatio.y = center_y_ratio;
4982 }
4983 
4984 // 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)4985 void ImGui::SetScrollHere(float center_y_ratio)
4986 {
4987     ImGuiWindow* window = GetCurrentWindow();
4988     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.
4989     ImGui::SetScrollFromPosY(target_y - window->Pos.y, center_y_ratio);
4990 }
4991 
SetKeyboardFocusHere(int offset)4992 void ImGui::SetKeyboardFocusHere(int offset)
4993 {
4994     ImGuiWindow* window = GetCurrentWindow();
4995     window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
4996     window->FocusIdxTabRequestNext = IM_INT_MAX;
4997 }
4998 
SetStateStorage(ImGuiStorage * tree)4999 void ImGui::SetStateStorage(ImGuiStorage* tree)
5000 {
5001     ImGuiWindow* window = GetCurrentWindow();
5002     window->DC.StateStorage = tree ? tree : &window->StateStorage;
5003 }
5004 
GetStateStorage()5005 ImGuiStorage* ImGui::GetStateStorage()
5006 {
5007     ImGuiWindow* window = GetCurrentWindowRead();
5008     return window->DC.StateStorage;
5009 }
5010 
TextV(const char * fmt,va_list args)5011 void ImGui::TextV(const char* fmt, va_list args)
5012 {
5013     ImGuiWindow* window = GetCurrentWindow();
5014     if (window->SkipItems)
5015         return;
5016 
5017     ImGuiState& g = *GImGui;
5018     const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5019     TextUnformatted(g.TempBuffer, text_end);
5020 }
5021 
Text(const char * fmt,...)5022 void ImGui::Text(const char* fmt, ...)
5023 {
5024     va_list args;
5025     va_start(args, fmt);
5026     TextV(fmt, args);
5027     va_end(args);
5028 }
5029 
TextColoredV(const ImVec4 & col,const char * fmt,va_list args)5030 void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args)
5031 {
5032     ImGui::PushStyleColor(ImGuiCol_Text, col);
5033     TextV(fmt, args);
5034     ImGui::PopStyleColor();
5035 }
5036 
TextColored(const ImVec4 & col,const char * fmt,...)5037 void ImGui::TextColored(const ImVec4& col, const char* fmt, ...)
5038 {
5039     va_list args;
5040     va_start(args, fmt);
5041     TextColoredV(col, fmt, args);
5042     va_end(args);
5043 }
5044 
TextDisabledV(const char * fmt,va_list args)5045 void ImGui::TextDisabledV(const char* fmt, va_list args)
5046 {
5047     ImGui::PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]);
5048     TextV(fmt, args);
5049     ImGui::PopStyleColor();
5050 }
5051 
TextDisabled(const char * fmt,...)5052 void ImGui::TextDisabled(const char* fmt, ...)
5053 {
5054     va_list args;
5055     va_start(args, fmt);
5056     TextDisabledV(fmt, args);
5057     va_end(args);
5058 }
5059 
TextWrappedV(const char * fmt,va_list args)5060 void ImGui::TextWrappedV(const char* fmt, va_list args)
5061 {
5062     ImGui::PushTextWrapPos(0.0f);
5063     TextV(fmt, args);
5064     ImGui::PopTextWrapPos();
5065 }
5066 
TextWrapped(const char * fmt,...)5067 void ImGui::TextWrapped(const char* fmt, ...)
5068 {
5069     va_list args;
5070     va_start(args, fmt);
5071     TextWrappedV(fmt, args);
5072     va_end(args);
5073 }
5074 
TextUnformatted(const char * text,const char * text_end)5075 void ImGui::TextUnformatted(const char* text, const char* text_end)
5076 {
5077     ImGuiWindow* window = GetCurrentWindow();
5078     if (window->SkipItems)
5079         return;
5080 
5081     ImGuiState& g = *GImGui;
5082     IM_ASSERT(text != NULL);
5083     const char* text_begin = text;
5084     if (text_end == NULL)
5085         text_end = text + strlen(text); // FIXME-OPT
5086 
5087     const float wrap_pos_x = window->DC.TextWrapPos;
5088     const bool wrap_enabled = wrap_pos_x >= 0.0f;
5089     if (text_end - text > 2000 && !wrap_enabled)
5090     {
5091         // Long text!
5092         // Perform manual coarse clipping to optimize for long multi-line text
5093         // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.
5094         // 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.
5095         const char* line = text;
5096         const float line_height = ImGui::GetTextLineHeight();
5097         const ImVec2 text_pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrentLineTextBaseOffset);
5098         const ImRect clip_rect = window->ClipRect;
5099         ImVec2 text_size(0,0);
5100 
5101         if (text_pos.y <= clip_rect.Max.y)
5102         {
5103             ImVec2 pos = text_pos;
5104 
5105             // Lines to skip (can't skip when logging text)
5106             if (!g.LogEnabled)
5107             {
5108                 int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height);
5109                 if (lines_skippable > 0)
5110                 {
5111                     int lines_skipped = 0;
5112                     while (line < text_end && lines_skipped < lines_skippable)
5113                     {
5114                         const char* line_end = strchr(line, '\n');
5115                         if (!line_end)
5116                             line_end = text_end;
5117                         line = line_end + 1;
5118                         lines_skipped++;
5119                     }
5120                     pos.y += lines_skipped * line_height;
5121                 }
5122             }
5123 
5124             // Lines to render
5125             if (line < text_end)
5126             {
5127                 ImRect line_rect(pos, pos + ImVec2(ImGui::GetWindowWidth(), line_height));
5128                 while (line < text_end)
5129                 {
5130                     const char* line_end = strchr(line, '\n');
5131                     if (IsClippedEx(line_rect, NULL, false))
5132                         break;
5133 
5134                     const ImVec2 line_size = CalcTextSize(line, line_end, false);
5135                     text_size.x = ImMax(text_size.x, line_size.x);
5136                     RenderText(pos, line, line_end, false);
5137                     if (!line_end)
5138                         line_end = text_end;
5139                     line = line_end + 1;
5140                     line_rect.Min.y += line_height;
5141                     line_rect.Max.y += line_height;
5142                     pos.y += line_height;
5143                 }
5144 
5145                 // Count remaining lines
5146                 int lines_skipped = 0;
5147                 while (line < text_end)
5148                 {
5149                     const char* line_end = strchr(line, '\n');
5150                     if (!line_end)
5151                         line_end = text_end;
5152                     line = line_end + 1;
5153                     lines_skipped++;
5154                 }
5155                 pos.y += lines_skipped * line_height;
5156             }
5157 
5158             text_size.y += (pos - text_pos).y;
5159         }
5160 
5161         ImRect bb(text_pos, text_pos + text_size);
5162         ItemSize(bb);
5163         ItemAdd(bb, NULL);
5164     }
5165     else
5166     {
5167         const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;
5168         const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);
5169 
5170         // Account of baseline offset
5171         ImVec2 text_pos = window->DC.CursorPos;
5172         text_pos.y += window->DC.CurrentLineTextBaseOffset;
5173 
5174         ImRect bb(text_pos, text_pos + text_size);
5175         ItemSize(text_size);
5176         if (!ItemAdd(bb, NULL))
5177             return;
5178 
5179         // Render (we don't hide text after ## in this end-user function)
5180         RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width);
5181     }
5182 }
5183 
AlignFirstTextHeightToWidgets()5184 void ImGui::AlignFirstTextHeightToWidgets()
5185 {
5186     ImGuiWindow* window = GetCurrentWindow();
5187     if (window->SkipItems)
5188         return;
5189 
5190     // Declare a dummy item size to that upcoming items that are smaller will center-align on the newly expanded line height.
5191     ImGuiState& g = *GImGui;
5192     ItemSize(ImVec2(0, g.FontSize + g.Style.FramePadding.y*2), g.Style.FramePadding.y);
5193     ImGui::SameLine(0, 0);
5194 }
5195 
5196 // Add a label+text combo aligned to other label+value widgets
LabelTextV(const char * label,const char * fmt,va_list args)5197 void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
5198 {
5199     ImGuiWindow* window = GetCurrentWindow();
5200     if (window->SkipItems)
5201         return;
5202 
5203     ImGuiState& g = *GImGui;
5204     const ImGuiStyle& style = g.Style;
5205     const float w = CalcItemWidth();
5206 
5207     const ImVec2 label_size = CalcTextSize(label, NULL, true);
5208     const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2));
5209     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);
5210     ItemSize(total_bb, style.FramePadding.y);
5211     if (!ItemAdd(total_bb, NULL))
5212         return;
5213 
5214     // Render
5215     const char* value_text_begin = &g.TempBuffer[0];
5216     const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5217     RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImGuiAlign_VCenter);
5218     if (label_size.x > 0.0f)
5219         RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
5220 }
5221 
LabelText(const char * label,const char * fmt,...)5222 void ImGui::LabelText(const char* label, const char* fmt, ...)
5223 {
5224     va_list args;
5225     va_start(args, fmt);
5226     LabelTextV(label, fmt, args);
5227     va_end(args);
5228 }
5229 
IsWindowContentHoverable(ImGuiWindow * window)5230 static inline bool IsWindowContentHoverable(ImGuiWindow* window)
5231 {
5232     // An active popup disable hovering on other windows (apart from its own children)
5233     ImGuiState& g = *GImGui;
5234     if (ImGuiWindow* focused_window = g.FocusedWindow)
5235         if (ImGuiWindow* focused_root_window = focused_window->RootWindow)
5236             if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) != 0 && focused_root_window->WasActive && focused_root_window != window->RootWindow)
5237                 return false;
5238 
5239     return true;
5240 }
5241 
ButtonBehavior(const ImRect & bb,ImGuiID id,bool * out_hovered,bool * out_held,ImGuiButtonFlags flags)5242 bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)
5243 {
5244     ImGuiState& g = *GImGui;
5245     ImGuiWindow* window = GetCurrentWindow();
5246 
5247     if (flags & ImGuiButtonFlags_Disabled)
5248     {
5249         if (out_hovered) *out_hovered = false;
5250         if (out_held) *out_held = false;
5251         if (g.ActiveId == id) SetActiveID(0);
5252         return false;
5253     }
5254 
5255     bool pressed = false;
5256     const bool hovered = IsHovered(bb, id, (flags & ImGuiButtonFlags_FlattenChilds) != 0);
5257     if (hovered)
5258     {
5259         SetHoveredID(id);
5260         if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
5261         {
5262             if (g.IO.MouseDoubleClicked[0] && (flags & ImGuiButtonFlags_PressedOnDoubleClick))
5263             {
5264                 pressed = true;
5265             }
5266             else if (g.IO.MouseClicked[0])
5267             {
5268                 if (flags & ImGuiButtonFlags_PressedOnClick)
5269                 {
5270                     pressed = true;
5271                     SetActiveID(0);
5272                 }
5273                 else
5274                 {
5275                     SetActiveID(id, window);
5276                 }
5277                 FocusWindow(window);
5278             }
5279             else if (g.IO.MouseReleased[0] && (flags & ImGuiButtonFlags_PressedOnRelease))
5280             {
5281                 pressed = true;
5282                 SetActiveID(0);
5283             }
5284             else if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && ImGui::IsMouseClicked(0, true))
5285             {
5286                 pressed = true;
5287             }
5288         }
5289     }
5290 
5291     bool held = false;
5292     if (g.ActiveId == id)
5293     {
5294         if (g.IO.MouseDown[0])
5295         {
5296             held = true;
5297         }
5298         else
5299         {
5300             if (hovered)
5301                 pressed = true;
5302             SetActiveID(0);
5303         }
5304     }
5305 
5306     if (out_hovered) *out_hovered = hovered;
5307     if (out_held) *out_held = held;
5308 
5309     return pressed;
5310 }
5311 
ButtonEx(const char * label,const ImVec2 & size_arg,ImGuiButtonFlags flags)5312 bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags)
5313 {
5314     ImGuiWindow* window = GetCurrentWindow();
5315     if (window->SkipItems)
5316         return false;
5317 
5318     ImGuiState& g = *GImGui;
5319     const ImGuiStyle& style = g.Style;
5320     const ImGuiID id = window->GetID(label);
5321     const ImVec2 label_size = CalcTextSize(label, NULL, true);
5322 
5323     ImVec2 pos = window->DC.CursorPos;
5324     if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset)
5325         pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y;
5326     ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);
5327 
5328     const ImRect bb(pos, pos + size);
5329     ItemSize(bb, style.FramePadding.y);
5330     if (!ItemAdd(bb, &id))
5331         return false;
5332 
5333     if (window->DC.ButtonRepeat) flags |= ImGuiButtonFlags_Repeat;
5334     bool hovered, held;
5335     bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
5336 
5337     // Render
5338     const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
5339     RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
5340     RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, ImGuiAlign_Center | ImGuiAlign_VCenter);
5341 
5342     // Automatically close popups
5343     //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
5344     //    ImGui::CloseCurrentPopup();
5345 
5346     return pressed;
5347 }
5348 
Button(const char * label,const ImVec2 & size_arg)5349 bool ImGui::Button(const char* label, const ImVec2& size_arg)
5350 {
5351     return ButtonEx(label, size_arg, 0);
5352 }
5353 
5354 // Small buttons fits within text without additional vertical spacing.
SmallButton(const char * label)5355 bool ImGui::SmallButton(const char* label)
5356 {
5357     ImGuiState& g = *GImGui;
5358     float backup_padding_y = g.Style.FramePadding.y;
5359     g.Style.FramePadding.y = 0.0f;
5360     bool pressed = ButtonEx(label, ImVec2(0,0), ImGuiButtonFlags_AlignTextBaseLine);
5361     g.Style.FramePadding.y = backup_padding_y;
5362     return pressed;
5363 }
5364 
5365 // Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack.
5366 // 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)5367 bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg)
5368 {
5369     ImGuiWindow* window = GetCurrentWindow();
5370     if (window->SkipItems)
5371         return false;
5372 
5373     const ImGuiID id = window->GetID(str_id);
5374     ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f);
5375     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
5376     ItemSize(bb);
5377     if (!ItemAdd(bb, &id))
5378         return false;
5379 
5380     bool hovered, held;
5381     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
5382 
5383     return pressed;
5384 }
5385 
5386 // Upper-right button to close a window.
CloseWindowButton(bool * p_opened)5387 static bool CloseWindowButton(bool* p_opened)
5388 {
5389     ImGuiWindow* window = ImGui::GetCurrentWindow();
5390 
5391     const ImGuiID id = window->GetID("#CLOSE");
5392     const float size = window->TitleBarHeight() - 4.0f;
5393     const ImRect bb(window->Rect().GetTR() + ImVec2(-2.0f-size,2.0f), window->Rect().GetTR() + ImVec2(-2.0f,2.0f+size));
5394 
5395     bool hovered, held;
5396     bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held);
5397 
5398     // Render
5399     const ImU32 col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton);
5400     const ImVec2 center = bb.GetCenter();
5401     window->DrawList->AddCircleFilled(center, ImMax(2.0f,size*0.5f), col, 16);
5402 
5403     const float cross_extent = (size * 0.5f * 0.7071f) - 1.0f;
5404     if (hovered)
5405     {
5406         window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), ImGui::GetColorU32(ImGuiCol_Text));
5407         window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), ImGui::GetColorU32(ImGuiCol_Text));
5408     }
5409 
5410     if (p_opened != NULL && pressed)
5411         *p_opened = false;
5412 
5413     return pressed;
5414 }
5415 
Image(ImTextureID user_texture_id,const ImVec2 & size,const ImVec2 & uv0,const ImVec2 & uv1,const ImVec4 & tint_col,const ImVec4 & border_col)5416 void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
5417 {
5418     ImGuiWindow* window = GetCurrentWindow();
5419     if (window->SkipItems)
5420         return;
5421 
5422     ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
5423     if (border_col.w > 0.0f)
5424         bb.Max += ImVec2(2,2);
5425     ItemSize(bb);
5426     if (!ItemAdd(bb, NULL))
5427         return;
5428 
5429     if (border_col.w > 0.0f)
5430     {
5431         window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f);
5432         window->DrawList->AddImage(user_texture_id, bb.Min+ImVec2(1,1), bb.Max-ImVec2(1,1), uv0, uv1, GetColorU32(tint_col));
5433     }
5434     else
5435     {
5436         window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col));
5437     }
5438 }
5439 
5440 // frame_padding < 0: uses FramePadding from style (default)
5441 // frame_padding = 0: no framing
5442 // frame_padding > 0: set framing size
5443 // 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)5444 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)
5445 {
5446     ImGuiWindow* window = GetCurrentWindow();
5447     if (window->SkipItems)
5448         return false;
5449 
5450     ImGuiState& g = *GImGui;
5451     const ImGuiStyle& style = g.Style;
5452 
5453     // Default to using texture ID as ID. User can still push string/integer prefixes.
5454     // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV.
5455     ImGui::PushID((void *)user_texture_id);
5456     const ImGuiID id = window->GetID("#image");
5457     ImGui::PopID();
5458 
5459     const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding;
5460     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding*2);
5461     const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size);
5462     ItemSize(bb);
5463     if (!ItemAdd(bb, &id))
5464         return false;
5465 
5466     bool hovered, held;
5467     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
5468 
5469     // Render
5470     const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
5471     RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding));
5472     if (bg_col.w > 0.0f)
5473         window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col));
5474     window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col));
5475 
5476     return pressed;
5477 }
5478 
5479 // Start logging ImGui output to TTY
LogToTTY(int max_depth)5480 void ImGui::LogToTTY(int max_depth)
5481 {
5482     ImGuiState& g = *GImGui;
5483     if (g.LogEnabled)
5484         return;
5485     ImGuiWindow* window = GetCurrentWindowRead();
5486 
5487     g.LogEnabled = true;
5488     g.LogFile = stdout;
5489     g.LogStartDepth = window->DC.TreeDepth;
5490     if (max_depth >= 0)
5491         g.LogAutoExpandMaxDepth = max_depth;
5492 }
5493 
5494 // Start logging ImGui output to given file
LogToFile(int max_depth,const char * filename)5495 void ImGui::LogToFile(int max_depth, const char* filename)
5496 {
5497     ImGuiState& g = *GImGui;
5498     if (g.LogEnabled)
5499         return;
5500     ImGuiWindow* window = GetCurrentWindowRead();
5501 
5502     if (!filename)
5503     {
5504         filename = g.IO.LogFilename;
5505         if (!filename)
5506             return;
5507     }
5508 
5509     g.LogFile = fopen(filename, "ab");
5510     if (!g.LogFile)
5511     {
5512         IM_ASSERT(g.LogFile != NULL); // Consider this an error
5513         return;
5514     }
5515     g.LogEnabled = true;
5516     g.LogStartDepth = window->DC.TreeDepth;
5517     if (max_depth >= 0)
5518         g.LogAutoExpandMaxDepth = max_depth;
5519 }
5520 
5521 // Start logging ImGui output to clipboard
LogToClipboard(int max_depth)5522 void ImGui::LogToClipboard(int max_depth)
5523 {
5524     ImGuiState& g = *GImGui;
5525     if (g.LogEnabled)
5526         return;
5527     ImGuiWindow* window = GetCurrentWindowRead();
5528 
5529     g.LogEnabled = true;
5530     g.LogFile = NULL;
5531     g.LogStartDepth = window->DC.TreeDepth;
5532     if (max_depth >= 0)
5533         g.LogAutoExpandMaxDepth = max_depth;
5534 }
5535 
LogFinish()5536 void ImGui::LogFinish()
5537 {
5538     ImGuiState& g = *GImGui;
5539     if (!g.LogEnabled)
5540         return;
5541 
5542     ImGui::LogText(IM_NEWLINE);
5543     g.LogEnabled = false;
5544     if (g.LogFile != NULL)
5545     {
5546         if (g.LogFile == stdout)
5547             fflush(g.LogFile);
5548         else
5549             fclose(g.LogFile);
5550         g.LogFile = NULL;
5551     }
5552     if (g.LogClipboard->size() > 1)
5553     {
5554         if (g.IO.SetClipboardTextFn)
5555             g.IO.SetClipboardTextFn(g.LogClipboard->begin());
5556         g.LogClipboard->clear();
5557     }
5558 }
5559 
5560 // Helper to display logging buttons
LogButtons()5561 void ImGui::LogButtons()
5562 {
5563     ImGuiState& g = *GImGui;
5564 
5565     ImGui::PushID("LogButtons");
5566     const bool log_to_tty = ImGui::Button("Log To TTY");
5567     ImGui::SameLine();
5568     const bool log_to_file = ImGui::Button("Log To File");
5569     ImGui::SameLine();
5570     const bool log_to_clipboard = ImGui::Button("Log To Clipboard");
5571     ImGui::SameLine();
5572 
5573     ImGui::PushItemWidth(80.0f);
5574     ImGui::PushAllowKeyboardFocus(false);
5575     ImGui::SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL);
5576     ImGui::PopAllowKeyboardFocus();
5577     ImGui::PopItemWidth();
5578     ImGui::PopID();
5579 
5580     // Start logging at the end of the function so that the buttons don't appear in the log
5581     if (log_to_tty)
5582         LogToTTY(g.LogAutoExpandMaxDepth);
5583     if (log_to_file)
5584         LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename);
5585     if (log_to_clipboard)
5586         LogToClipboard(g.LogAutoExpandMaxDepth);
5587 }
5588 
TreeNodeBehaviorIsOpened(ImGuiID id,ImGuiTreeNodeFlags flags)5589 bool ImGui::TreeNodeBehaviorIsOpened(ImGuiID id, ImGuiTreeNodeFlags flags)
5590 {
5591     // We only write to the tree storage if the user clicks (or explicitely use SetNextTreeNode*** functions)
5592     ImGuiState& g = *GImGui;
5593     ImGuiWindow* window = g.CurrentWindow;
5594     ImGuiStorage* storage = window->DC.StateStorage;
5595 
5596     bool opened;
5597     if (g.SetNextTreeNodeOpenedCond != 0)
5598     {
5599         if (g.SetNextTreeNodeOpenedCond & ImGuiSetCond_Always)
5600         {
5601             opened = g.SetNextTreeNodeOpenedVal;
5602             storage->SetInt(id, opened);
5603         }
5604         else
5605         {
5606             // We treat ImGuiSetCondition_Once and ImGuiSetCondition_FirstUseEver the same because tree node state are not saved persistently.
5607             const int stored_value = storage->GetInt(id, -1);
5608             if (stored_value == -1)
5609             {
5610                 opened = g.SetNextTreeNodeOpenedVal;
5611                 storage->SetInt(id, opened);
5612             }
5613             else
5614             {
5615                 opened = stored_value != 0;
5616             }
5617         }
5618         g.SetNextTreeNodeOpenedCond = 0;
5619     }
5620     else
5621     {
5622         opened = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;
5623     }
5624 
5625     // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior).
5626     // NB- If we are above max depth we still allow manually opened nodes to be logged.
5627     if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoExpandOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth)
5628         opened = true;
5629 
5630     return opened;
5631 }
5632 
5633 // FIXME: Split into CollapsingHeader(label, default_open?) and TreeNodeBehavior(label), obsolete the 4 parameters function.
CollapsingHeader(const char * label,const char * str_id,bool display_frame,bool default_open)5634 bool ImGui::CollapsingHeader(const char* label, const char* str_id, bool display_frame, bool default_open)
5635 {
5636     ImGuiWindow* window = GetCurrentWindow();
5637     if (window->SkipItems)
5638         return false;
5639 
5640     ImGuiState& g = *GImGui;
5641     const ImGuiStyle& style = g.Style;
5642     const ImVec2 padding = display_frame ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f);
5643 
5644     IM_ASSERT(str_id != NULL || label != NULL);
5645     if (str_id == NULL)
5646         str_id = label;
5647     if (label == NULL)
5648         label = str_id;
5649     const bool label_hide_text_after_double_hash = (label == str_id); // Only search and hide text after ## if we have passed label and ID separately, otherwise allow "##" within format string.
5650     const ImGuiID id = window->GetID(str_id);
5651     const ImVec2 label_size = CalcTextSize(label, NULL, label_hide_text_after_double_hash);
5652 
5653     // We vertically grow up to current line height up the typical widget height.
5654     const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset - padding.y); // Latch before ItemSize changes it
5655     const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), label_size.y + padding.y*2);
5656     ImRect bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height));
5657     if (display_frame)
5658     {
5659         // Framed header expand a little outside the default padding
5660         bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1;
5661         bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1;
5662     }
5663 
5664     const float collapser_width = g.FontSize + (display_frame ? padding.x*2 : padding.x);
5665     const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f);   // Include collapser
5666     ItemSize(ImVec2(text_width, frame_height), text_base_offset_y);
5667 
5668     // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
5669     // (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)
5670     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);
5671     bool opened = TreeNodeBehaviorIsOpened(id, (default_open ? ImGuiTreeNodeFlags_DefaultOpen : 0) | (display_frame ? ImGuiTreeNodeFlags_NoAutoExpandOnLog : 0));
5672     if (!ItemAdd(interact_bb, &id))
5673         return opened;
5674 
5675     bool hovered, held;
5676     bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, ImGuiButtonFlags_NoKeyModifiers);
5677     if (pressed)
5678     {
5679         opened = !opened;
5680         window->DC.StateStorage->SetInt(id, opened);
5681     }
5682 
5683     // Render
5684     const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
5685     const ImVec2 text_pos = bb.Min + padding + ImVec2(collapser_width, text_base_offset_y);
5686     if (display_frame)
5687     {
5688         // Framed type
5689         RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
5690         RenderCollapseTriangle(bb.Min + padding + ImVec2(0.0f, text_base_offset_y), opened, 1.0f, true);
5691         if (g.LogEnabled)
5692         {
5693             // 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.
5694             const char log_prefix[] = "\n##";
5695             const char log_suffix[] = "##";
5696             LogRenderedText(text_pos, log_prefix, log_prefix+3);
5697             RenderTextClipped(text_pos, bb.Max, label, NULL, &label_size);
5698             LogRenderedText(text_pos, log_suffix+1, log_suffix+3);
5699         }
5700         else
5701         {
5702             RenderTextClipped(text_pos, bb.Max, label, NULL, &label_size);
5703         }
5704     }
5705     else
5706     {
5707         // Unframed typed for tree nodes
5708         if (hovered)
5709             RenderFrame(bb.Min, bb.Max, col, false);
5710 
5711         RenderCollapseTriangle(bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), opened, 0.70f, false);
5712         if (g.LogEnabled)
5713             LogRenderedText(text_pos, ">");
5714         RenderText(text_pos, label, NULL, label_hide_text_after_double_hash);
5715     }
5716 
5717     return opened;
5718 }
5719 
Bullet()5720 void ImGui::Bullet()
5721 {
5722     ImGuiWindow* window = GetCurrentWindow();
5723     if (window->SkipItems)
5724         return;
5725 
5726     ImGuiState& g = *GImGui;
5727     const ImGuiStyle& style = g.Style;
5728     const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
5729     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height));
5730     ItemSize(bb);
5731     if (!ItemAdd(bb, NULL))
5732     {
5733         ImGui::SameLine(0, style.FramePadding.x*2);
5734         return;
5735     }
5736 
5737     // Render
5738     const float bullet_size = g.FontSize*0.15f;
5739     window->DrawList->AddCircleFilled(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f), bullet_size, GetColorU32(ImGuiCol_Text));
5740 
5741     // Stay on same line
5742     ImGui::SameLine(0, style.FramePadding.x*2);
5743 }
5744 
5745 // Text with a little bullet aligned to the typical tree node.
BulletTextV(const char * fmt,va_list args)5746 void ImGui::BulletTextV(const char* fmt, va_list args)
5747 {
5748     ImGuiWindow* window = GetCurrentWindow();
5749     if (window->SkipItems)
5750         return;
5751 
5752     ImGuiState& g = *GImGui;
5753     const ImGuiStyle& style = g.Style;
5754 
5755     const char* text_begin = g.TempBuffer;
5756     const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5757     const ImVec2 label_size = CalcTextSize(text_begin, text_end, true);
5758     const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
5759     const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
5760     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
5761     ItemSize(bb);
5762     if (!ItemAdd(bb, NULL))
5763         return;
5764 
5765     // Render
5766     const float bullet_size = g.FontSize*0.15f;
5767     window->DrawList->AddCircleFilled(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f), bullet_size, GetColorU32(ImGuiCol_Text));
5768     RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end);
5769 }
5770 
BulletText(const char * fmt,...)5771 void ImGui::BulletText(const char* fmt, ...)
5772 {
5773     va_list args;
5774     va_start(args, fmt);
5775     BulletTextV(fmt, args);
5776     va_end(args);
5777 }
5778 
5779 // If returning 'true' the node is open and the user is responsible for calling TreePop
TreeNodeV(const char * str_id,const char * fmt,va_list args)5780 bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)
5781 {
5782     ImGuiWindow* window = GetCurrentWindow();
5783     if (window->SkipItems)
5784         return false;
5785 
5786     ImGuiState& g = *GImGui;
5787     ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5788     if (!str_id || !str_id[0])
5789         str_id = fmt;
5790 
5791     ImGui::PushID(str_id);
5792     const bool opened = ImGui::CollapsingHeader(g.TempBuffer, "", false);
5793     ImGui::PopID();
5794 
5795     if (opened)
5796         ImGui::TreePush(str_id);
5797 
5798     return opened;
5799 }
5800 
TreeNode(const char * str_id,const char * fmt,...)5801 bool ImGui::TreeNode(const char* str_id, const char* fmt, ...)
5802 {
5803     va_list args;
5804     va_start(args, fmt);
5805     bool s = TreeNodeV(str_id, fmt, args);
5806     va_end(args);
5807     return s;
5808 }
5809 
5810 // If returning 'true' the node is open and the user is responsible for calling TreePop
TreeNodeV(const void * ptr_id,const char * fmt,va_list args)5811 bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)
5812 {
5813     ImGuiWindow* window = GetCurrentWindow();
5814     if (window->SkipItems)
5815         return false;
5816 
5817     ImGuiState& g = *GImGui;
5818     ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5819 
5820     if (!ptr_id)
5821         ptr_id = fmt;
5822 
5823     ImGui::PushID(ptr_id);
5824     const bool opened = ImGui::CollapsingHeader(g.TempBuffer, "", false);
5825     ImGui::PopID();
5826 
5827     if (opened)
5828         ImGui::TreePush(ptr_id);
5829 
5830     return opened;
5831 }
5832 
TreeNode(const void * ptr_id,const char * fmt,...)5833 bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)
5834 {
5835     va_list args;
5836     va_start(args, fmt);
5837     bool s = TreeNodeV(ptr_id, fmt, args);
5838     va_end(args);
5839     return s;
5840 }
5841 
TreeNode(const char * str_label_id)5842 bool ImGui::TreeNode(const char* str_label_id)
5843 {
5844     return TreeNode(str_label_id, "%s", str_label_id);
5845 }
5846 
SetNextTreeNodeOpened(bool opened,ImGuiSetCond cond)5847 void ImGui::SetNextTreeNodeOpened(bool opened, ImGuiSetCond cond)
5848 {
5849     ImGuiState& g = *GImGui;
5850     g.SetNextTreeNodeOpenedVal = opened;
5851     g.SetNextTreeNodeOpenedCond = cond ? cond : ImGuiSetCond_Always;
5852 }
5853 
PushID(const char * str_id)5854 void ImGui::PushID(const char* str_id)
5855 {
5856     ImGuiWindow* window = GetCurrentWindow();
5857     window->IDStack.push_back(window->GetID(str_id));
5858 }
5859 
PushID(const char * str_id_begin,const char * str_id_end)5860 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
5861 {
5862     ImGuiWindow* window = GetCurrentWindow();
5863     window->IDStack.push_back(window->GetID(str_id_begin, str_id_end));
5864 }
5865 
PushID(const void * ptr_id)5866 void ImGui::PushID(const void* ptr_id)
5867 {
5868     ImGuiWindow* window = GetCurrentWindow();
5869     window->IDStack.push_back(window->GetID(ptr_id));
5870 }
5871 
PushID(int int_id)5872 void ImGui::PushID(int int_id)
5873 {
5874     const void* ptr_id = (void*)(intptr_t)int_id;
5875     ImGuiWindow* window = GetCurrentWindow();
5876     window->IDStack.push_back(window->GetID(ptr_id));
5877 }
5878 
PopID()5879 void ImGui::PopID()
5880 {
5881     ImGuiWindow* window = GetCurrentWindow();
5882     window->IDStack.pop_back();
5883 }
5884 
GetID(const char * str_id)5885 ImGuiID ImGui::GetID(const char* str_id)
5886 {
5887     return GImGui->CurrentWindow->GetID(str_id);
5888 }
5889 
GetID(const char * str_id_begin,const char * str_id_end)5890 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
5891 {
5892     return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end);
5893 }
5894 
GetID(const void * ptr_id)5895 ImGuiID ImGui::GetID(const void* ptr_id)
5896 {
5897     return GImGui->CurrentWindow->GetID(ptr_id);
5898 }
5899 
DataTypeFormatString(ImGuiDataType data_type,void * data_ptr,const char * display_format,char * buf,int buf_size)5900 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size)
5901 {
5902     if (data_type == ImGuiDataType_Int)
5903         ImFormatString(buf, buf_size, display_format, *(int*)data_ptr);
5904     else if (data_type == ImGuiDataType_Float)
5905         ImFormatString(buf, buf_size, display_format, *(float*)data_ptr);
5906 }
5907 
DataTypeFormatString(ImGuiDataType data_type,void * data_ptr,int decimal_precision,char * buf,int buf_size)5908 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size)
5909 {
5910     if (data_type == ImGuiDataType_Int)
5911     {
5912         if (decimal_precision < 0)
5913             ImFormatString(buf, buf_size, "%d", *(int*)data_ptr);
5914         else
5915             ImFormatString(buf, buf_size, "%.*d", decimal_precision, *(int*)data_ptr);
5916     }
5917     else if (data_type == ImGuiDataType_Float)
5918     {
5919         if (decimal_precision < 0)
5920             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?
5921         else
5922             ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(float*)data_ptr);
5923     }
5924 }
5925 
DataTypeApplyOp(ImGuiDataType data_type,int op,void * value1,const void * value2)5926 static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2)// Store into value1
5927 {
5928     if (data_type == ImGuiDataType_Int)
5929     {
5930         if (op == '+')
5931             *(int*)value1 = *(int*)value1 + *(const int*)value2;
5932         else if (op == '-')
5933             *(int*)value1 = *(int*)value1 - *(const int*)value2;
5934     }
5935     else if (data_type == ImGuiDataType_Float)
5936     {
5937         if (op == '+')
5938             *(float*)value1 = *(float*)value1 + *(const float*)value2;
5939         else if (op == '-')
5940             *(float*)value1 = *(float*)value1 - *(const float*)value2;
5941     }
5942 }
5943 
5944 // 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)5945 static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format)
5946 {
5947     while (ImCharIsSpace(*buf))
5948         buf++;
5949 
5950     // We don't support '-' op because it would conflict with inputing negative value.
5951     // Instead you can use +-100 to subtract from an existing value
5952     char op = buf[0];
5953     if (op == '+' || op == '*' || op == '/')
5954     {
5955         buf++;
5956         while (ImCharIsSpace(*buf))
5957             buf++;
5958     }
5959     else
5960     {
5961         op = 0;
5962     }
5963     if (!buf[0])
5964         return false;
5965 
5966     if (data_type == ImGuiDataType_Int)
5967     {
5968         if (!scalar_format)
5969             scalar_format = "%d";
5970         int* v = (int*)data_ptr;
5971         const int old_v = *v;
5972         int arg0 = *v;
5973         if (op && sscanf(initial_value_buf, scalar_format, &arg0) < 1)
5974             return false;
5975 
5976         // 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
5977         float arg1 = 0.0f;
5978         if (op == '+')      { if (sscanf(buf, "%f", &arg1) == 1) *v = (int)(arg0 + arg1); }                // Add (use "+-" to subtract)
5979         else if (op == '*') { if (sscanf(buf, "%f", &arg1) == 1) *v = (int)(arg0 * arg1); }                // Multiply
5980         else if (op == '/') { if (sscanf(buf, "%f", &arg1) == 1 && arg1 != 0.0f) *v = (int)(arg0 / arg1); }// Divide
5981         else                { if (sscanf(buf, scalar_format, &arg0) == 1) *v = arg0; }                     // Assign constant
5982         return (old_v != *v);
5983     }
5984     else if (data_type == ImGuiDataType_Float)
5985     {
5986         // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in
5987         scalar_format = "%f";
5988         float* v = (float*)data_ptr;
5989         const float old_v = *v;
5990         float arg0 = *v;
5991         if (op && sscanf(initial_value_buf, scalar_format, &arg0) < 1)
5992             return false;
5993 
5994         float arg1 = 0.0f;
5995         if (sscanf(buf, scalar_format, &arg1) < 1)
5996             return false;
5997         if (op == '+')      { *v = arg0 + arg1; }                    // Add (use "+-" to subtract)
5998         else if (op == '*') { *v = arg0 * arg1; }                    // Multiply
5999         else if (op == '/') { if (arg1 != 0.0f) *v = arg0 / arg1; }  // Divide
6000         else                { *v = arg1; }                           // Assign constant
6001         return (old_v != *v);
6002     }
6003 
6004     return false;
6005 }
6006 
6007 // 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)6008 bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision)
6009 {
6010     ImGuiState& g = *GImGui;
6011     ImGuiWindow* window = GetCurrentWindow();
6012 
6013     // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen)
6014     SetActiveID(g.ScalarAsInputTextId, window);
6015     SetHoveredID(0);
6016     FocusableItemUnregister(window);
6017 
6018     char buf[32];
6019     DataTypeFormatString(data_type, data_ptr, decimal_precision, buf, IM_ARRAYSIZE(buf));
6020     bool text_value_changed = InputTextEx(label, buf, IM_ARRAYSIZE(buf), aabb.GetSize(), ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll);
6021     if (g.ScalarAsInputTextId == 0)
6022     {
6023         // First frame
6024         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)
6025         g.ScalarAsInputTextId = g.ActiveId;
6026         SetHoveredID(id);
6027     }
6028     else if (g.ActiveId != g.ScalarAsInputTextId)
6029     {
6030         // Release
6031         g.ScalarAsInputTextId = 0;
6032     }
6033     if (text_value_changed)
6034         return DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, NULL);
6035     return false;
6036 }
6037 
6038 // Parse display precision back from the display format string
ParseFormatPrecision(const char * fmt,int default_precision)6039 int ImGui::ParseFormatPrecision(const char* fmt, int default_precision)
6040 {
6041     int precision = default_precision;
6042     while ((fmt = strchr(fmt, '%')) != NULL)
6043     {
6044         fmt++;
6045         if (fmt[0] == '%') { fmt++; continue; } // Ignore "%%"
6046         while (*fmt >= '0' && *fmt <= '9')
6047             fmt++;
6048         if (*fmt == '.')
6049         {
6050             precision = atoi(fmt + 1);
6051             if (precision < 0 || precision > 10)
6052                 precision = default_precision;
6053         }
6054         break;
6055     }
6056     return precision;
6057 }
6058 
RoundScalar(float value,int decimal_precision)6059 float ImGui::RoundScalar(float value, int decimal_precision)
6060 {
6061     // Round past decimal precision
6062     // So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0
6063     // FIXME: Investigate better rounding methods
6064     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 };
6065     float min_step = (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision);
6066     bool negative = value < 0.0f;
6067     value = fabsf(value);
6068     float remainder = fmodf(value, min_step);
6069     if (remainder <= min_step*0.5f)
6070         value -= remainder;
6071     else
6072         value += (min_step - remainder);
6073     return negative ? -value : value;
6074 }
6075 
SliderBehavior(const ImRect & frame_bb,ImGuiID id,float * v,float v_min,float v_max,float power,int decimal_precision,ImGuiSliderFlags flags)6076 bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags)
6077 {
6078     ImGuiState& g = *GImGui;
6079     ImGuiWindow* window = GetCurrentWindow();
6080     const ImGuiStyle& style = g.Style;
6081 
6082     // Draw frame
6083     RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
6084 
6085     const bool is_non_linear = fabsf(power - 1.0f) > 0.0001f;
6086     const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0;
6087 
6088     const float grab_padding = 2.0f;
6089     const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f);
6090     float grab_sz;
6091     if (decimal_precision > 0)
6092         grab_sz = ImMin(style.GrabMinSize, slider_sz);
6093     else
6094         grab_sz = ImMin(ImMax(1.0f * (slider_sz / (v_max-v_min+1.0f)), style.GrabMinSize), slider_sz);  // Integer sliders, if possible have the grab size represent 1 unit
6095     const float slider_usable_sz = slider_sz - grab_sz;
6096     const float slider_usable_pos_min = (is_horizontal ? frame_bb.Min.x : frame_bb.Min.y) + grab_padding + grab_sz*0.5f;
6097     const float slider_usable_pos_max = (is_horizontal ? frame_bb.Max.x : frame_bb.Max.y) - grab_padding - grab_sz*0.5f;
6098 
6099     // For logarithmic sliders that cross over sign boundary we want the exponential increase to be symmetric around 0.0f
6100     float linear_zero_pos = 0.0f;   // 0.0->1.0f
6101     if (v_min * v_max < 0.0f)
6102     {
6103         // Different sign
6104         const float linear_dist_min_to_0 = powf(fabsf(0.0f - v_min), 1.0f/power);
6105         const float linear_dist_max_to_0 = powf(fabsf(v_max - 0.0f), 1.0f/power);
6106         linear_zero_pos = linear_dist_min_to_0 / (linear_dist_min_to_0+linear_dist_max_to_0);
6107     }
6108     else
6109     {
6110         // Same sign
6111         linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f;
6112     }
6113 
6114     // Process clicking on the slider
6115     bool value_changed = false;
6116     if (g.ActiveId == id)
6117     {
6118         if (g.IO.MouseDown[0])
6119         {
6120             const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
6121             float normalized_pos = ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f);
6122             if (!is_horizontal)
6123                 normalized_pos = 1.0f - normalized_pos;
6124 
6125             float new_value;
6126             if (is_non_linear)
6127             {
6128                 // Account for logarithmic scale on both sides of the zero
6129                 if (normalized_pos < linear_zero_pos)
6130                 {
6131                     // Negative: rescale to the negative range before powering
6132                     float a = 1.0f - (normalized_pos / linear_zero_pos);
6133                     a = powf(a, power);
6134                     new_value = ImLerp(ImMin(v_max,0.0f), v_min, a);
6135                 }
6136                 else
6137                 {
6138                     // Positive: rescale to the positive range before powering
6139                     float a;
6140                     if (fabsf(linear_zero_pos - 1.0f) > 1.e-6)
6141                         a = (normalized_pos - linear_zero_pos) / (1.0f - linear_zero_pos);
6142                     else
6143                         a = normalized_pos;
6144                     a = powf(a, power);
6145                     new_value = ImLerp(ImMax(v_min,0.0f), v_max, a);
6146                 }
6147             }
6148             else
6149             {
6150                 // Linear slider
6151                 new_value = ImLerp(v_min, v_max, normalized_pos);
6152             }
6153 
6154             // Round past decimal precision
6155             new_value = RoundScalar(new_value, decimal_precision);
6156             if (*v != new_value)
6157             {
6158                 *v = new_value;
6159                 value_changed = true;
6160             }
6161         }
6162         else
6163         {
6164             SetActiveID(0);
6165         }
6166     }
6167 
6168     // Calculate slider grab positioning
6169     float grab_t;
6170     if (is_non_linear)
6171     {
6172         float v_clamped = ImClamp(*v, v_min, v_max);
6173         if (v_clamped < 0.0f)
6174         {
6175             const float f = 1.0f - (v_clamped - v_min) / (ImMin(0.0f,v_max) - v_min);
6176             grab_t = (1.0f - powf(f, 1.0f/power)) * linear_zero_pos;
6177         }
6178         else
6179         {
6180             const float f = (v_clamped - ImMax(0.0f,v_min)) / (v_max - ImMax(0.0f,v_min));
6181             grab_t = linear_zero_pos + powf(f, 1.0f/power) * (1.0f - linear_zero_pos);
6182         }
6183     }
6184     else
6185     {
6186         // Linear slider
6187         grab_t = (ImClamp(*v, v_min, v_max) - v_min) / (v_max - v_min);
6188     }
6189 
6190     // Draw
6191     if (!is_horizontal)
6192         grab_t = 1.0f - grab_t;
6193     const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
6194     ImRect grab_bb;
6195     if (is_horizontal)
6196         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));
6197     else
6198         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));
6199     window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
6200 
6201     return value_changed;
6202 }
6203 
6204 // Use power!=1.0 for logarithmic sliders.
6205 // Adjust display_format to decorate the value with a prefix or a suffix.
6206 //   "%.3f"         1.234
6207 //   "%5.2f secs"   01.23 secs
6208 //   "Gold: %.0f"   Gold: 1
SliderFloat(const char * label,float * v,float v_min,float v_max,const char * display_format,float power)6209 bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format, float power)
6210 {
6211     ImGuiWindow* window = GetCurrentWindow();
6212     if (window->SkipItems)
6213         return false;
6214 
6215     ImGuiState& g = *GImGui;
6216     const ImGuiStyle& style = g.Style;
6217     const ImGuiID id = window->GetID(label);
6218     const float w = CalcItemWidth();
6219 
6220     const ImVec2 label_size = CalcTextSize(label, NULL, true);
6221     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
6222     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));
6223 
6224     // NB- we don't call ItemSize() yet because we may turn into a text edit box below
6225     if (!ItemAdd(total_bb, &id))
6226     {
6227         ItemSize(total_bb, style.FramePadding.y);
6228         return false;
6229     }
6230 
6231     const bool hovered = IsHovered(frame_bb, id);
6232     if (hovered)
6233         SetHoveredID(id);
6234 
6235     if (!display_format)
6236         display_format = "%.3f";
6237     int decimal_precision = ParseFormatPrecision(display_format, 3);
6238 
6239     // Tabbing or CTRL-clicking on Slider turns it into an input box
6240     bool start_text_input = false;
6241     const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id);
6242     if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]))
6243     {
6244         SetActiveID(id, window);
6245         FocusWindow(window);
6246 
6247         if (tab_focus_requested || g.IO.KeyCtrl)
6248         {
6249             start_text_input = true;
6250             g.ScalarAsInputTextId = 0;
6251         }
6252     }
6253     if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
6254         return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
6255 
6256     ItemSize(total_bb, style.FramePadding.y);
6257 
6258     // Actual slider behavior + render grab
6259     const bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision);
6260 
6261     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
6262     char value_buf[64];
6263     const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
6264     RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImGuiAlign_Center|ImGuiAlign_VCenter);
6265 
6266     if (label_size.x > 0.0f)
6267         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
6268 
6269     return value_changed;
6270 }
6271 
VSliderFloat(const char * label,const ImVec2 & size,float * v,float v_min,float v_max,const char * display_format,float power)6272 bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format, float power)
6273 {
6274     ImGuiWindow* window = GetCurrentWindow();
6275     if (window->SkipItems)
6276         return false;
6277 
6278     ImGuiState& g = *GImGui;
6279     const ImGuiStyle& style = g.Style;
6280     const ImGuiID id = window->GetID(label);
6281 
6282     const ImVec2 label_size = CalcTextSize(label, NULL, true);
6283     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
6284     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));
6285 
6286     ItemSize(bb, style.FramePadding.y);
6287     if (!ItemAdd(frame_bb, &id))
6288         return false;
6289 
6290     const bool hovered = IsHovered(frame_bb, id);
6291     if (hovered)
6292         SetHoveredID(id);
6293 
6294     if (!display_format)
6295         display_format = "%.3f";
6296     int decimal_precision = ParseFormatPrecision(display_format, 3);
6297 
6298     if (hovered && g.IO.MouseClicked[0])
6299     {
6300         SetActiveID(id, window);
6301         FocusWindow(window);
6302     }
6303 
6304     // Actual slider behavior + render grab
6305     bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision, ImGuiSliderFlags_Vertical);
6306 
6307     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
6308     // For the vertical slider we allow centered text to overlap the frame padding
6309     char value_buf[64];
6310     char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
6311     RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImGuiAlign_Center);
6312     if (label_size.x > 0.0f)
6313         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
6314 
6315     return value_changed;
6316 }
6317 
SliderAngle(const char * label,float * v_rad,float v_degrees_min,float v_degrees_max)6318 bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max)
6319 {
6320     float v_deg = (*v_rad) * 360.0f / (2*IM_PI);
6321     bool value_changed = ImGui::SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f);
6322     *v_rad = v_deg * (2*IM_PI) / 360.0f;
6323     return value_changed;
6324 }
6325 
SliderInt(const char * label,int * v,int v_min,int v_max,const char * display_format)6326 bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format)
6327 {
6328     if (!display_format)
6329         display_format = "%.0f";
6330     float v_f = (float)*v;
6331     bool value_changed = ImGui::SliderFloat(label, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
6332     *v = (int)v_f;
6333     return value_changed;
6334 }
6335 
VSliderInt(const char * label,const ImVec2 & size,int * v,int v_min,int v_max,const char * display_format)6336 bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format)
6337 {
6338     if (!display_format)
6339         display_format = "%.0f";
6340     float v_f = (float)*v;
6341     bool value_changed = ImGui::VSliderFloat(label, size, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
6342     *v = (int)v_f;
6343     return value_changed;
6344 }
6345 
6346 // 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)6347 bool ImGui::SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power)
6348 {
6349     ImGuiWindow* window = GetCurrentWindow();
6350     if (window->SkipItems)
6351         return false;
6352 
6353     ImGuiState& g = *GImGui;
6354     bool value_changed = false;
6355     ImGui::BeginGroup();
6356     ImGui::PushID(label);
6357     PushMultiItemsWidths(components);
6358     for (int i = 0; i < components; i++)
6359     {
6360         ImGui::PushID(i);
6361         value_changed |= ImGui::SliderFloat("##v", &v[i], v_min, v_max, display_format, power);
6362         ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
6363         ImGui::PopID();
6364         ImGui::PopItemWidth();
6365     }
6366     ImGui::PopID();
6367 
6368     ImGui::TextUnformatted(label, FindRenderedTextEnd(label));
6369     ImGui::EndGroup();
6370 
6371     return value_changed;
6372 }
6373 
SliderFloat2(const char * label,float v[2],float v_min,float v_max,const char * display_format,float power)6374 bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format, float power)
6375 {
6376     return SliderFloatN(label, v, 2, v_min, v_max, display_format, power);
6377 }
6378 
SliderFloat3(const char * label,float v[3],float v_min,float v_max,const char * display_format,float power)6379 bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format, float power)
6380 {
6381     return SliderFloatN(label, v, 3, v_min, v_max, display_format, power);
6382 }
6383 
SliderFloat4(const char * label,float v[4],float v_min,float v_max,const char * display_format,float power)6384 bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format, float power)
6385 {
6386     return SliderFloatN(label, v, 4, v_min, v_max, display_format, power);
6387 }
6388 
SliderIntN(const char * label,int * v,int components,int v_min,int v_max,const char * display_format)6389 bool ImGui::SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format)
6390 {
6391     ImGuiWindow* window = GetCurrentWindow();
6392     if (window->SkipItems)
6393         return false;
6394 
6395     ImGuiState& g = *GImGui;
6396     bool value_changed = false;
6397     ImGui::BeginGroup();
6398     ImGui::PushID(label);
6399     PushMultiItemsWidths(components);
6400     for (int i = 0; i < components; i++)
6401     {
6402         ImGui::PushID(i);
6403         value_changed |= ImGui::SliderInt("##v", &v[i], v_min, v_max, display_format);
6404         ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
6405         ImGui::PopID();
6406         ImGui::PopItemWidth();
6407     }
6408     ImGui::PopID();
6409 
6410     ImGui::TextUnformatted(label, FindRenderedTextEnd(label));
6411     ImGui::EndGroup();
6412 
6413     return value_changed;
6414 }
6415 
SliderInt2(const char * label,int v[2],int v_min,int v_max,const char * display_format)6416 bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format)
6417 {
6418     return SliderIntN(label, v, 2, v_min, v_max, display_format);
6419 }
6420 
SliderInt3(const char * label,int v[3],int v_min,int v_max,const char * display_format)6421 bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format)
6422 {
6423     return SliderIntN(label, v, 3, v_min, v_max, display_format);
6424 }
6425 
SliderInt4(const char * label,int v[4],int v_min,int v_max,const char * display_format)6426 bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format)
6427 {
6428     return SliderIntN(label, v, 4, v_min, v_max, display_format);
6429 }
6430 
DragBehavior(const ImRect & frame_bb,ImGuiID id,float * v,float v_speed,float v_min,float v_max,int decimal_precision,float power)6431 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)
6432 {
6433     ImGuiState& g = *GImGui;
6434     const ImGuiStyle& style = g.Style;
6435 
6436     // Draw frame
6437     const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
6438     RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
6439 
6440     bool value_changed = false;
6441 
6442     // Process clicking on the drag
6443     if (g.ActiveId == id)
6444     {
6445         if (g.IO.MouseDown[0])
6446         {
6447             if (g.ActiveIdIsJustActivated)
6448             {
6449                 // Lock current value on click
6450                 g.DragCurrentValue = *v;
6451                 g.DragLastMouseDelta = ImVec2(0.f, 0.f);
6452             }
6453 
6454             float v_cur = g.DragCurrentValue;
6455             const ImVec2 mouse_drag_delta = ImGui::GetMouseDragDelta(0, 1.0f);
6456             if (fabsf(mouse_drag_delta.x - g.DragLastMouseDelta.x) > 0.0f)
6457             {
6458                 float speed = v_speed;
6459                 if (speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX)
6460                     speed = (v_max - v_min) * g.DragSpeedDefaultRatio;
6461                 if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f)
6462                     speed = speed * g.DragSpeedScaleFast;
6463                 if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f)
6464                     speed = speed * g.DragSpeedScaleSlow;
6465 
6466                 float delta = (mouse_drag_delta.x - g.DragLastMouseDelta.x) * speed;
6467                 if (fabsf(power - 1.0f) > 0.001f)
6468                 {
6469                     // Logarithmic curve on both side of 0.0
6470                     float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur;
6471                     float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f;
6472                     float v1 = powf(v0_abs, 1.0f / power) + (delta * v0_sign);
6473                     float v1_abs = v1 >= 0.0f ? v1 : -v1;
6474                     float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f;          // Crossed sign line
6475                     v_cur = powf(v1_abs, power) * v0_sign * v1_sign;    // Reapply sign
6476                 }
6477                 else
6478                 {
6479                     v_cur += delta;
6480                 }
6481                 g.DragLastMouseDelta.x = mouse_drag_delta.x;
6482 
6483                 // Clamp
6484                 if (v_min < v_max)
6485                     v_cur = ImClamp(v_cur, v_min, v_max);
6486                 g.DragCurrentValue = v_cur;
6487             }
6488 
6489             // Round to user desired precision, then apply
6490             v_cur = RoundScalar(v_cur, decimal_precision);
6491             if (*v != v_cur)
6492             {
6493                 *v = v_cur;
6494                 value_changed = true;
6495             }
6496         }
6497         else
6498         {
6499             SetActiveID(0);
6500         }
6501     }
6502 
6503     return value_changed;
6504 }
6505 
DragFloat(const char * label,float * v,float v_speed,float v_min,float v_max,const char * display_format,float power)6506 bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* display_format, float power)
6507 {
6508     ImGuiWindow* window = GetCurrentWindow();
6509     if (window->SkipItems)
6510         return false;
6511 
6512     ImGuiState& g = *GImGui;
6513     const ImGuiStyle& style = g.Style;
6514     const ImGuiID id = window->GetID(label);
6515     const float w = CalcItemWidth();
6516 
6517     const ImVec2 label_size = CalcTextSize(label, NULL, true);
6518     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
6519     const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
6520     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));
6521 
6522     // NB- we don't call ItemSize() yet because we may turn into a text edit box below
6523     if (!ItemAdd(total_bb, &id))
6524     {
6525         ItemSize(total_bb, style.FramePadding.y);
6526         return false;
6527     }
6528 
6529     const bool hovered = IsHovered(frame_bb, id);
6530     if (hovered)
6531         SetHoveredID(id);
6532 
6533     if (!display_format)
6534         display_format = "%.3f";
6535     int decimal_precision = ParseFormatPrecision(display_format, 3);
6536 
6537     // Tabbing or CTRL-clicking on Drag turns it into an input box
6538     bool start_text_input = false;
6539     const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id);
6540     if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] | g.IO.MouseDoubleClicked[0])))
6541     {
6542         SetActiveID(id, window);
6543         FocusWindow(window);
6544 
6545         if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0])
6546         {
6547             start_text_input = true;
6548             g.ScalarAsInputTextId = 0;
6549         }
6550     }
6551     if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
6552         return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
6553 
6554     // Actual drag behavior
6555     ItemSize(total_bb, style.FramePadding.y);
6556     const bool value_changed = DragBehavior(frame_bb, id, v, v_speed, v_min, v_max, decimal_precision, power);
6557 
6558     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
6559     char value_buf[64];
6560     const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
6561     RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImGuiAlign_Center|ImGuiAlign_VCenter);
6562 
6563     if (label_size.x > 0.0f)
6564         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
6565 
6566     return value_changed;
6567 }
6568 
DragFloatN(const char * label,float * v,int components,float v_speed,float v_min,float v_max,const char * display_format,float power)6569 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)
6570 {
6571     ImGuiWindow* window = GetCurrentWindow();
6572     if (window->SkipItems)
6573         return false;
6574 
6575     ImGuiState& g = *GImGui;
6576     bool value_changed = false;
6577     ImGui::BeginGroup();
6578     ImGui::PushID(label);
6579     PushMultiItemsWidths(components);
6580     for (int i = 0; i < components; i++)
6581     {
6582         ImGui::PushID(i);
6583         value_changed |= ImGui::DragFloat("##v", &v[i], v_speed, v_min, v_max, display_format, power);
6584         ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
6585         ImGui::PopID();
6586         ImGui::PopItemWidth();
6587     }
6588     ImGui::PopID();
6589 
6590     ImGui::TextUnformatted(label, FindRenderedTextEnd(label));
6591     ImGui::EndGroup();
6592 
6593     return value_changed;
6594 }
6595 
DragFloat2(const char * label,float v[2],float v_speed,float v_min,float v_max,const char * display_format,float power)6596 bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power)
6597 {
6598     return DragFloatN(label, v, 2, v_speed, v_min, v_max, display_format, power);
6599 }
6600 
DragFloat3(const char * label,float v[3],float v_speed,float v_min,float v_max,const char * display_format,float power)6601 bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* display_format, float power)
6602 {
6603     return DragFloatN(label, v, 3, v_speed, v_min, v_max, display_format, power);
6604 }
6605 
DragFloat4(const char * label,float v[4],float v_speed,float v_min,float v_max,const char * display_format,float power)6606 bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* display_format, float power)
6607 {
6608     return DragFloatN(label, v, 4, v_speed, v_min, v_max, display_format, power);
6609 }
6610 
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)6611 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)
6612 {
6613     ImGuiWindow* window = GetCurrentWindow();
6614     if (window->SkipItems)
6615         return false;
6616 
6617     ImGuiState& g = *GImGui;
6618     ImGui::PushID(label);
6619     ImGui::BeginGroup();
6620     PushMultiItemsWidths(2);
6621 
6622     bool value_changed = ImGui::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);
6623     ImGui::PopItemWidth();
6624     ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
6625     value_changed |= ImGui::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);
6626     ImGui::PopItemWidth();
6627     ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
6628 
6629     ImGui::TextUnformatted(label, FindRenderedTextEnd(label));
6630     ImGui::EndGroup();
6631     ImGui::PopID();
6632 
6633     return value_changed;
6634 }
6635 
6636 // 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)6637 bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* display_format)
6638 {
6639     if (!display_format)
6640         display_format = "%.0f";
6641     float v_f = (float)*v;
6642     bool value_changed = ImGui::DragFloat(label, &v_f, v_speed, (float)v_min, (float)v_max, display_format);
6643     *v = (int)v_f;
6644     return value_changed;
6645 }
6646 
DragIntN(const char * label,int * v,int components,float v_speed,int v_min,int v_max,const char * display_format)6647 bool ImGui::DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format)
6648 {
6649     ImGuiWindow* window = GetCurrentWindow();
6650     if (window->SkipItems)
6651         return false;
6652 
6653     ImGuiState& g = *GImGui;
6654     bool value_changed = false;
6655     ImGui::BeginGroup();
6656     ImGui::PushID(label);
6657     PushMultiItemsWidths(components);
6658     for (int i = 0; i < components; i++)
6659     {
6660         ImGui::PushID(i);
6661         value_changed |= ImGui::DragInt("##v", &v[i], v_speed, v_min, v_max, display_format);
6662         ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
6663         ImGui::PopID();
6664         ImGui::PopItemWidth();
6665     }
6666     ImGui::PopID();
6667 
6668     ImGui::TextUnformatted(label, FindRenderedTextEnd(label));
6669     ImGui::EndGroup();
6670 
6671     return value_changed;
6672 }
6673 
DragInt2(const char * label,int v[2],float v_speed,int v_min,int v_max,const char * display_format)6674 bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* display_format)
6675 {
6676     return DragIntN(label, v, 2, v_speed, v_min, v_max, display_format);
6677 }
6678 
DragInt3(const char * label,int v[3],float v_speed,int v_min,int v_max,const char * display_format)6679 bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* display_format)
6680 {
6681     return DragIntN(label, v, 3, v_speed, v_min, v_max, display_format);
6682 }
6683 
DragInt4(const char * label,int v[4],float v_speed,int v_min,int v_max,const char * display_format)6684 bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* display_format)
6685 {
6686     return DragIntN(label, v, 4, v_speed, v_min, v_max, display_format);
6687 }
6688 
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)6689 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)
6690 {
6691     ImGuiWindow* window = GetCurrentWindow();
6692     if (window->SkipItems)
6693         return false;
6694 
6695     ImGuiState& g = *GImGui;
6696     ImGui::PushID(label);
6697     ImGui::BeginGroup();
6698     PushMultiItemsWidths(2);
6699 
6700     bool value_changed = ImGui::DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? IM_INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format);
6701     ImGui::PopItemWidth();
6702     ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
6703     value_changed |= ImGui::DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? IM_INT_MAX : v_max, display_format_max ? display_format_max : display_format);
6704     ImGui::PopItemWidth();
6705     ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
6706 
6707     ImGui::TextUnformatted(label, FindRenderedTextEnd(label));
6708     ImGui::EndGroup();
6709     ImGui::PopID();
6710 
6711     return value_changed;
6712 }
6713 
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)6714 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)
6715 {
6716     ImGuiWindow* window = GetCurrentWindow();
6717     if (window->SkipItems)
6718         return;
6719 
6720     ImGuiState& g = *GImGui;
6721     const ImGuiStyle& style = g.Style;
6722 
6723     const ImVec2 label_size = CalcTextSize(label, NULL, true);
6724     if (graph_size.x == 0.0f)
6725         graph_size.x = CalcItemWidth();
6726     if (graph_size.y == 0.0f)
6727         graph_size.y = label_size.y + (style.FramePadding.y * 2);
6728 
6729     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y));
6730     const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
6731     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));
6732     ItemSize(total_bb, style.FramePadding.y);
6733     if (!ItemAdd(total_bb, NULL))
6734         return;
6735 
6736     // Determine scale from values if not specified
6737     if (scale_min == FLT_MAX || scale_max == FLT_MAX)
6738     {
6739         float v_min = FLT_MAX;
6740         float v_max = -FLT_MAX;
6741         for (int i = 0; i < values_count; i++)
6742         {
6743             const float v = values_getter(data, i);
6744             v_min = ImMin(v_min, v);
6745             v_max = ImMax(v_max, v);
6746         }
6747         if (scale_min == FLT_MAX)
6748             scale_min = v_min;
6749         if (scale_max == FLT_MAX)
6750             scale_max = v_max;
6751     }
6752 
6753     RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
6754 
6755     int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
6756     int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
6757 
6758     // Tooltip on hover
6759     int v_hovered = -1;
6760     if (IsHovered(inner_bb, 0))
6761     {
6762         const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);
6763         const int v_idx = (int)(t * item_count);
6764         IM_ASSERT(v_idx >= 0 && v_idx < values_count);
6765 
6766         const float v0 = values_getter(data, (v_idx + values_offset) % values_count);
6767         const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count);
6768         if (plot_type == ImGuiPlotType_Lines)
6769             ImGui::SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1);
6770         else if (plot_type == ImGuiPlotType_Histogram)
6771             ImGui::SetTooltip("%d: %8.4g", v_idx, v0);
6772         v_hovered = v_idx;
6773     }
6774 
6775     const float t_step = 1.0f / (float)res_w;
6776 
6777     float v0 = values_getter(data, (0 + values_offset) % values_count);
6778     float t0 = 0.0f;
6779     ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) / (scale_max - scale_min)) );    // Point in the normalized space of our target rectangle
6780 
6781     const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram);
6782     const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered);
6783 
6784     for (int n = 0; n < res_w; n++)
6785     {
6786         const float t1 = t0 + t_step;
6787         const int v1_idx = (int)(t0 * item_count + 0.5f);
6788         IM_ASSERT(v1_idx >= 0 && v1_idx < values_count);
6789         const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count);
6790         const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) / (scale_max - scale_min)) );
6791 
6792         // 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.
6793         ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0);
6794         ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, 1.0f));
6795         if (plot_type == ImGuiPlotType_Lines)
6796         {
6797             window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
6798         }
6799         else if (plot_type == ImGuiPlotType_Histogram)
6800         {
6801             if (pos1.x >= pos0.x + 2.0f)
6802                 pos1.x -= 1.0f;
6803             window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
6804         }
6805 
6806         t0 = t1;
6807         tp0 = tp1;
6808     }
6809 
6810     // Text overlay
6811     if (overlay_text)
6812         RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImGuiAlign_Center);
6813 
6814     if (label_size.x > 0.0f)
6815         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
6816 }
6817 
6818 struct ImGuiPlotArrayGetterData
6819 {
6820     const float* Values;
6821     int Stride;
6822 
ImGuiPlotArrayGetterDataImGuiPlotArrayGetterData6823     ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; }
6824 };
6825 
Plot_ArrayGetter(void * data,int idx)6826 static float Plot_ArrayGetter(void* data, int idx)
6827 {
6828     ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data;
6829     const float v = *(float*)(void*)((unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);
6830     return v;
6831 }
6832 
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)6833 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)
6834 {
6835     ImGuiPlotArrayGetterData data(values, stride);
6836     PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
6837 }
6838 
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)6839 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)
6840 {
6841     PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
6842 }
6843 
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)6844 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)
6845 {
6846     ImGuiPlotArrayGetterData data(values, stride);
6847     PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
6848 }
6849 
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)6850 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)
6851 {
6852     PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
6853 }
6854 
6855 // 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)6856 void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay)
6857 {
6858     ImGuiWindow* window = GetCurrentWindow();
6859     if (window->SkipItems)
6860         return;
6861 
6862     ImGuiState& g = *GImGui;
6863     const ImGuiStyle& style = g.Style;
6864 
6865     ImVec2 pos = window->DC.CursorPos;
6866     ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f));
6867     ItemSize(bb, style.FramePadding.y);
6868     if (!ItemAdd(bb, NULL))
6869         return;
6870 
6871     // Render
6872     fraction = ImSaturate(fraction);
6873     RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
6874     bb.Reduce(ImVec2(window->BorderSize, window->BorderSize));
6875     const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y);
6876     RenderFrame(bb.Min, fill_br, GetColorU32(ImGuiCol_PlotHistogram), false, style.FrameRounding);
6877 
6878     // Default displaying the fraction as percentage string, but user can override it
6879     char overlay_buf[32];
6880     if (!overlay)
6881     {
6882         ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f);
6883         overlay = overlay_buf;
6884     }
6885 
6886     ImVec2 overlay_size = CalcTextSize(overlay, NULL);
6887     if (overlay_size.x > 0.0f)
6888         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, ImGuiAlign_Left|ImGuiAlign_VCenter, &bb.Min, &bb.Max);
6889 }
6890 
Checkbox(const char * label,bool * v)6891 bool ImGui::Checkbox(const char* label, bool* v)
6892 {
6893     ImGuiWindow* window = GetCurrentWindow();
6894     if (window->SkipItems)
6895         return false;
6896 
6897     ImGuiState& g = *GImGui;
6898     const ImGuiStyle& style = g.Style;
6899     const ImGuiID id = window->GetID(label);
6900     const ImVec2 label_size = CalcTextSize(label, NULL, true);
6901 
6902     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));
6903     ItemSize(check_bb, style.FramePadding.y);
6904 
6905     ImRect total_bb = check_bb;
6906     if (label_size.x > 0)
6907         SameLine(0, style.ItemInnerSpacing.x);
6908     const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size);
6909     if (label_size.x > 0)
6910     {
6911         ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
6912         total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max));
6913     }
6914 
6915     if (!ItemAdd(total_bb, &id))
6916         return false;
6917 
6918     bool hovered, held;
6919     bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
6920     if (pressed)
6921         *v = !(*v);
6922 
6923     RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
6924     if (*v)
6925     {
6926         const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
6927         const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
6928         window->DrawList->AddRectFilled(check_bb.Min+ImVec2(pad,pad), check_bb.Max-ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), style.FrameRounding);
6929     }
6930 
6931     if (g.LogEnabled)
6932         LogRenderedText(text_bb.GetTL(), *v ? "[x]" : "[ ]");
6933     if (label_size.x > 0.0f)
6934         RenderText(text_bb.GetTL(), label);
6935 
6936     return pressed;
6937 }
6938 
CheckboxFlags(const char * label,unsigned int * flags,unsigned int flags_value)6939 bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value)
6940 {
6941     bool v = ((*flags & flags_value) == flags_value);
6942     bool pressed = ImGui::Checkbox(label, &v);
6943     if (pressed)
6944     {
6945         if (v)
6946             *flags |= flags_value;
6947         else
6948             *flags &= ~flags_value;
6949     }
6950 
6951     return pressed;
6952 }
6953 
RadioButton(const char * label,bool active)6954 bool ImGui::RadioButton(const char* label, bool active)
6955 {
6956     ImGuiWindow* window = GetCurrentWindow();
6957     if (window->SkipItems)
6958         return false;
6959 
6960     ImGuiState& g = *GImGui;
6961     const ImGuiStyle& style = g.Style;
6962     const ImGuiID id = window->GetID(label);
6963     const ImVec2 label_size = CalcTextSize(label, NULL, true);
6964 
6965     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));
6966     ItemSize(check_bb, style.FramePadding.y);
6967 
6968     ImRect total_bb = check_bb;
6969     if (label_size.x > 0)
6970         SameLine(0, style.ItemInnerSpacing.x);
6971     const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size);
6972     if (label_size.x > 0)
6973     {
6974         ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
6975         total_bb.Add(text_bb);
6976     }
6977 
6978     if (!ItemAdd(total_bb, &id))
6979         return false;
6980 
6981     ImVec2 center = check_bb.GetCenter();
6982     center.x = (float)(int)center.x + 0.5f;
6983     center.y = (float)(int)center.y + 0.5f;
6984     const float radius = check_bb.GetHeight() * 0.5f;
6985 
6986     bool hovered, held;
6987     bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
6988 
6989     window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16);
6990     if (active)
6991     {
6992         const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
6993         const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
6994         window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16);
6995     }
6996 
6997     if (window->Flags & ImGuiWindowFlags_ShowBorders)
6998     {
6999         window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16);
7000         window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16);
7001     }
7002 
7003     if (g.LogEnabled)
7004         LogRenderedText(text_bb.GetTL(), active ? "(x)" : "( )");
7005     if (label_size.x > 0.0f)
7006         RenderText(text_bb.GetTL(), label);
7007 
7008     return pressed;
7009 }
7010 
RadioButton(const char * label,int * v,int v_button)7011 bool ImGui::RadioButton(const char* label, int* v, int v_button)
7012 {
7013     const bool pressed = ImGui::RadioButton(label, *v == v_button);
7014     if (pressed)
7015     {
7016         *v = v_button;
7017     }
7018     return pressed;
7019 }
7020 
InputTextCalcTextLenAndLineCount(const char * text_begin,const char ** out_text_end)7021 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
7022 {
7023     int line_count = 0;
7024     const char* s = text_begin;
7025     while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding
7026         if (c == '\n')
7027             line_count++;
7028     s--;
7029     if (s[0] != '\n' && s[0] != '\r')
7030         line_count++;
7031     *out_text_end = s;
7032     return line_count;
7033 }
7034 
InputTextCalcTextSizeW(const ImWchar * text_begin,const ImWchar * text_end,const ImWchar ** remaining,ImVec2 * out_offset,bool stop_on_new_line)7035 static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line)
7036 {
7037     ImFont* font = GImGui->Font;
7038     const float line_height = GImGui->FontSize;
7039     const float scale = line_height / font->FontSize;
7040 
7041     ImVec2 text_size = ImVec2(0,0);
7042     float line_width = 0.0f;
7043 
7044     const ImWchar* s = text_begin;
7045     while (s < text_end)
7046     {
7047         unsigned int c = (unsigned int)(*s++);
7048         if (c == '\n')
7049         {
7050             text_size.x = ImMax(text_size.x, line_width);
7051             text_size.y += line_height;
7052             line_width = 0.0f;
7053             if (stop_on_new_line)
7054                 break;
7055             continue;
7056         }
7057         if (c == '\r')
7058             continue;
7059 
7060         const float char_width = font->GetCharAdvance((unsigned short)c) * scale;
7061         line_width += char_width;
7062     }
7063 
7064     if (text_size.x < line_width)
7065         text_size.x = line_width;
7066 
7067     if (out_offset)
7068         *out_offset = ImVec2(line_width, text_size.y + line_height);  // offset allow for the possibility of sitting after a trailing \n
7069 
7070     if (line_width > 0 || text_size.y == 0.0f)                        // whereas size.y will ignore the trailing \n
7071         text_size.y += line_height;
7072 
7073     if (remaining)
7074         *remaining = s;
7075 
7076     return text_size;
7077 }
7078 
7079 // 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)
7080 namespace ImGuiStb
7081 {
7082 
STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING * obj)7083 static int     STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj)                             { return obj->CurLenW; }
STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING * obj,int idx)7084 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)7085 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)7086 static int     STB_TEXTEDIT_KEYTOTEXT(int key)                                                    { return key >= 0x10000 ? 0 : key; }
7087 static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
STB_TEXTEDIT_LAYOUTROW(StbTexteditRow * r,STB_TEXTEDIT_STRING * obj,int line_start_idx)7088 static void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
7089 {
7090     const ImWchar* text = obj->Text.Data;
7091     const ImWchar* text_remaining = NULL;
7092     const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true);
7093     r->x0 = 0.0f;
7094     r->x1 = size.x;
7095     r->baseline_y_delta = size.y;
7096     r->ymin = 0.0f;
7097     r->ymax = size.y;
7098     r->num_chars = (int)(text_remaining - (text + line_start_idx));
7099 }
7100 
is_separator(unsigned int c)7101 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)7102 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)7103 static int  STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)   { while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
7104 #ifdef __APPLE__    // FIXME: Move setting to IO structure
is_word_boundary_from_left(STB_TEXTEDIT_STRING * obj,int idx)7105 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)7106 static int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)  { int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
7107 #else
STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING * obj,int idx)7108 static int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)  { int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
7109 #endif
7110 #define STB_TEXTEDIT_MOVEWORDLEFT   STB_TEXTEDIT_MOVEWORDLEFT_IMPL    // They need to be #define for stb_textedit.h
7111 #define STB_TEXTEDIT_MOVEWORDRIGHT  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
7112 
STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING * obj,int pos,int n)7113 static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
7114 {
7115     ImWchar* dst = obj->Text.Data + pos;
7116 
7117     // We maintain our buffer length in both UTF-8 and wchar formats
7118     obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);
7119     obj->CurLenW -= n;
7120 
7121     // Offset remaining text
7122     const ImWchar* src = obj->Text.Data + pos + n;
7123     while (ImWchar c = *src++)
7124         *dst++ = c;
7125     *dst = '\0';
7126 }
7127 
STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING * obj,int pos,const ImWchar * new_text,int new_text_len)7128 static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len)
7129 {
7130     const int text_len = obj->CurLenW;
7131     if (new_text_len + text_len + 1 > obj->Text.Size)
7132         return false;
7133 
7134     const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
7135     if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA)
7136         return false;
7137 
7138     ImWchar* text = obj->Text.Data;
7139     if (pos != text_len)
7140         memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));
7141     memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
7142 
7143     obj->CurLenW += new_text_len;
7144     obj->CurLenA += new_text_len_utf8;
7145     obj->Text[obj->CurLenW] = '\0';
7146 
7147     return true;
7148 }
7149 
7150 // 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)
7151 #define STB_TEXTEDIT_K_LEFT         0x10000 // keyboard input to move cursor left
7152 #define STB_TEXTEDIT_K_RIGHT        0x10001 // keyboard input to move cursor right
7153 #define STB_TEXTEDIT_K_UP           0x10002 // keyboard input to move cursor up
7154 #define STB_TEXTEDIT_K_DOWN         0x10003 // keyboard input to move cursor down
7155 #define STB_TEXTEDIT_K_LINESTART    0x10004 // keyboard input to move cursor to start of line
7156 #define STB_TEXTEDIT_K_LINEEND      0x10005 // keyboard input to move cursor to end of line
7157 #define STB_TEXTEDIT_K_TEXTSTART    0x10006 // keyboard input to move cursor to start of text
7158 #define STB_TEXTEDIT_K_TEXTEND      0x10007 // keyboard input to move cursor to end of text
7159 #define STB_TEXTEDIT_K_DELETE       0x10008 // keyboard input to delete selection or character under cursor
7160 #define STB_TEXTEDIT_K_BACKSPACE    0x10009 // keyboard input to delete selection or character left of cursor
7161 #define STB_TEXTEDIT_K_UNDO         0x1000A // keyboard input to perform undo
7162 #define STB_TEXTEDIT_K_REDO         0x1000B // keyboard input to perform redo
7163 #define STB_TEXTEDIT_K_WORDLEFT     0x1000C // keyboard input to move cursor left one word
7164 #define STB_TEXTEDIT_K_WORDRIGHT    0x1000D // keyboard input to move cursor right one word
7165 #define STB_TEXTEDIT_K_SHIFT        0x20000
7166 
7167 #define STB_TEXTEDIT_IMPLEMENTATION
7168 #include "stb_textedit.h"
7169 
7170 }
7171 
OnKeyPressed(int key)7172 void ImGuiTextEditState::OnKeyPressed(int key)
7173 {
7174     stb_textedit_key(this, &StbState, key);
7175     CursorFollow = true;
7176     CursorAnimReset();
7177 }
7178 
7179 // Public API to manipulate UTF-8 text
7180 // We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar)
7181 // FIXME: The existence of this rarely exercised code path is a bit of a nuisance.
DeleteChars(int pos,int bytes_count)7182 void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count)
7183 {
7184     IM_ASSERT(pos + bytes_count <= BufTextLen);
7185     char* dst = Buf + pos;
7186     const char* src = Buf + pos + bytes_count;
7187     while (char c = *src++)
7188         *dst++ = c;
7189     *dst = '\0';
7190 
7191     if (CursorPos + bytes_count >= pos)
7192         CursorPos -= bytes_count;
7193     else if (CursorPos >= pos)
7194         CursorPos = pos;
7195     SelectionStart = SelectionEnd = CursorPos;
7196     BufDirty = true;
7197     BufTextLen -= bytes_count;
7198 }
7199 
InsertChars(int pos,const char * new_text,const char * new_text_end)7200 void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)
7201 {
7202     const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
7203     if (new_text_len + BufTextLen + 1 >= BufSize)
7204         return;
7205 
7206     if (BufTextLen != pos)
7207         memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos));
7208     memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char));
7209     Buf[BufTextLen + new_text_len] = '\0';
7210 
7211     if (CursorPos >= pos)
7212         CursorPos += new_text_len;
7213     SelectionStart = SelectionEnd = CursorPos;
7214     BufDirty = true;
7215     BufTextLen += new_text_len;
7216 }
7217 
7218 // Return false to discard a character.
InputTextFilterCharacter(unsigned int * p_char,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)7219 static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
7220 {
7221     unsigned int c = *p_char;
7222 
7223     if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF)))
7224     {
7225         bool pass = false;
7226         pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline));
7227         pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput));
7228         if (!pass)
7229             return false;
7230     }
7231 
7232     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.
7233         return false;
7234 
7235     if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank))
7236     {
7237         if (flags & ImGuiInputTextFlags_CharsDecimal)
7238             if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/'))
7239                 return false;
7240 
7241         if (flags & ImGuiInputTextFlags_CharsHexadecimal)
7242             if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F'))
7243                 return false;
7244 
7245         if (flags & ImGuiInputTextFlags_CharsUppercase)
7246             if (c >= 'a' && c <= 'z')
7247                 *p_char = (c += (unsigned int)('A'-'a'));
7248 
7249         if (flags & ImGuiInputTextFlags_CharsNoBlank)
7250             if (ImCharIsSpace(c))
7251                 return false;
7252     }
7253 
7254     if (flags & ImGuiInputTextFlags_CallbackCharFilter)
7255     {
7256         ImGuiTextEditCallbackData callback_data;
7257         memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
7258         callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;
7259         callback_data.EventChar = (ImWchar)c;
7260         callback_data.Flags = flags;
7261         callback_data.UserData = user_data;
7262         if (callback(&callback_data) != 0)
7263             return false;
7264         *p_char = callback_data.EventChar;
7265         if (!callback_data.EventChar)
7266             return false;
7267     }
7268 
7269     return true;
7270 }
7271 
7272 // Edit a string of text
7273 // 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.
7274 // 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)7275 bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
7276 {
7277     ImGuiWindow* window = GetCurrentWindow();
7278     if (window->SkipItems)
7279         return false;
7280 
7281     IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys)
7282     IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)
7283 
7284     ImGuiState& g = *GImGui;
7285     const ImGuiIO& io = g.IO;
7286     const ImGuiStyle& style = g.Style;
7287 
7288     const ImGuiID id = window->GetID(label);
7289     const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;
7290     const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0;
7291     const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
7292 
7293     const ImVec2 label_size = CalcTextSize(label, NULL, true);
7294     ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? ImGui::GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line
7295     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
7296     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));
7297 
7298     ImGuiWindow* draw_window = window;
7299     if (is_multiline)
7300     {
7301         ImGui::BeginGroup();
7302         if (!ImGui::BeginChildFrame(id, frame_bb.GetSize()))
7303         {
7304             ImGui::EndChildFrame();
7305             ImGui::EndGroup();
7306             return false;
7307         }
7308         draw_window = GetCurrentWindow();
7309         size.x -= draw_window->ScrollbarSizes.x;
7310     }
7311     else
7312     {
7313         ItemSize(total_bb, style.FramePadding.y);
7314         if (!ItemAdd(total_bb, &id))
7315             return false;
7316     }
7317 
7318     // Password pushes a temporary font with only a fallback glyph
7319     if (is_password)
7320     {
7321         const ImFont::Glyph* glyph = g.Font->FindGlyph('*');
7322         ImFont* password_font = &g.InputTextPasswordFont;
7323         password_font->FontSize = g.Font->FontSize;
7324         password_font->Scale = g.Font->Scale;
7325         password_font->DisplayOffset = g.Font->DisplayOffset;
7326         password_font->Ascent = g.Font->Ascent;
7327         password_font->Descent = g.Font->Descent;
7328         password_font->ContainerAtlas = g.Font->ContainerAtlas;
7329         password_font->FallbackGlyph = glyph;
7330         password_font->FallbackXAdvance = glyph->XAdvance;
7331         IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexXAdvance.empty() && password_font->IndexLookup.empty());
7332         ImGui::PushFont(password_font);
7333     }
7334 
7335     // NB: we are only allowed to access 'edit_state' if we are the active widget.
7336     ImGuiTextEditState& edit_state = g.InputTextState;
7337 
7338     const bool is_ctrl_down = io.KeyCtrl;
7339     const bool is_shift_down = io.KeyShift;
7340     const bool is_alt_down = io.KeyAlt;
7341     const bool is_super_down = io.KeySuper;
7342     const bool focus_requested = FocusableItemRegister(window, g.ActiveId == id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0);    // Using completion callback disable keyboard tabbing
7343     const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent);
7344     const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code;
7345 
7346     const bool hovered = IsHovered(frame_bb, id);
7347     if (hovered)
7348     {
7349         SetHoveredID(id);
7350         g.MouseCursor = ImGuiMouseCursor_TextInput;
7351     }
7352     const bool user_clicked = hovered && io.MouseClicked[0];
7353     const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetID("#SCROLLY");
7354 
7355     bool select_all = (g.ActiveId != id) && (flags & ImGuiInputTextFlags_AutoSelectAll) != 0;
7356     if (focus_requested || user_clicked || user_scrolled)
7357     {
7358         if (g.ActiveId != id)
7359         {
7360             // Start edition
7361             // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)
7362             // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode)
7363             const int prev_len_w = edit_state.CurLenW;
7364             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.
7365             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.
7366             ImFormatString(edit_state.InitialText.Data, edit_state.InitialText.Size, "%s", buf);
7367             const char* buf_end = NULL;
7368             edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
7369             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.
7370             edit_state.CursorAnimReset();
7371 
7372             // Preserve cursor position and undo/redo stack if we come back to same widget
7373             // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar).
7374             const bool recycle_state = (edit_state.Id == id) && (prev_len_w == edit_state.CurLenW);
7375             if (recycle_state)
7376             {
7377                 // Recycle existing cursor/selection/undo stack but clamp position
7378                 // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
7379                 edit_state.CursorClamp();
7380             }
7381             else
7382             {
7383                 edit_state.Id = id;
7384                 edit_state.ScrollX = 0.0f;
7385                 stb_textedit_initialize_state(&edit_state.StbState, !is_multiline);
7386                 if (!is_multiline && focus_requested_by_code)
7387                     select_all = true;
7388             }
7389             if (flags & ImGuiInputTextFlags_AlwaysInsertMode)
7390                 edit_state.StbState.insert_mode = true;
7391             if (!is_multiline && (focus_requested_by_tab || (user_clicked && is_ctrl_down)))
7392                 select_all = true;
7393         }
7394         SetActiveID(id, window);
7395         FocusWindow(window);
7396     }
7397     else if (io.MouseClicked[0])
7398     {
7399         // Release focus when we click outside
7400         if (g.ActiveId == id)
7401             SetActiveID(0);
7402     }
7403 
7404     bool value_changed = false;
7405     bool enter_pressed = false;
7406 
7407     if (g.ActiveId == id)
7408     {
7409         if (!is_editable && !g.ActiveIdIsJustActivated)
7410         {
7411             // When read-only we always use the live data passed to the function
7412             edit_state.Text.resize(buf_size+1);
7413             const char* buf_end = NULL;
7414             edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
7415             edit_state.CurLenA = (int)(buf_end - buf);
7416             edit_state.CursorClamp();
7417         }
7418 
7419         edit_state.BufSizeA = buf_size;
7420 
7421         // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
7422         // Down the line we should have a cleaner library-wide concept of Selected vs Active.
7423         g.ActiveIdAllowOverlap = !io.MouseDown[0];
7424 
7425         // Edit in progress
7426         const float mouse_x = (g.IO.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX;
7427         const float mouse_y = (is_multiline ? (g.IO.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f));
7428 
7429         if (select_all || (hovered && !io.DoubleClickSelectsWord && io.MouseDoubleClicked[0]))
7430         {
7431             edit_state.SelectAll();
7432             edit_state.SelectedAllMouseLock = true;
7433         }
7434         else if (hovered && io.DoubleClickSelectsWord && io.MouseDoubleClicked[0])
7435         {
7436             // Select a word only, OS X style (by simulating keystrokes)
7437             edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT);
7438             edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);
7439         }
7440         else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock)
7441         {
7442             stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
7443             edit_state.CursorAnimReset();
7444         }
7445         else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f))
7446         {
7447             stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
7448             edit_state.CursorAnimReset();
7449             edit_state.CursorFollow = true;
7450         }
7451         if (edit_state.SelectedAllMouseLock && !io.MouseDown[0])
7452             edit_state.SelectedAllMouseLock = false;
7453 
7454         if (g.IO.InputCharacters[0])
7455         {
7456             // Process text input (before we check for Return because using some IME will effectively send a Return?)
7457             // 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.
7458             if (!(is_ctrl_down && !is_alt_down) && is_editable)
7459             {
7460                 for (int n = 0; n < IM_ARRAYSIZE(g.IO.InputCharacters) && g.IO.InputCharacters[n]; n++)
7461                     if (unsigned int c = (unsigned int)g.IO.InputCharacters[n])
7462                     {
7463                         // Insert character if they pass filtering
7464                         if (!InputTextFilterCharacter(&c, flags, callback, user_data))
7465                             continue;
7466                         edit_state.OnKeyPressed((int)c);
7467                     }
7468             }
7469 
7470             // Consume characters
7471             memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
7472         }
7473 
7474         // Handle various key-presses
7475         bool cancel_edit = false;
7476         const int k_mask = (is_shift_down ? STB_TEXTEDIT_K_SHIFT : 0);
7477         const bool is_shortcutkey_only = (io.ShortcutsUseSuperKey ? (is_super_down && !is_alt_down && !is_shift_down && !is_ctrl_down) : (is_ctrl_down && !is_alt_down && !is_shift_down && !is_super_down));
7478         const bool is_wordmove_key_down = (io.WordMovementUsesAltKey ? io.KeyAlt : io.KeyCtrl);
7479 
7480         if (IsKeyPressedMap(ImGuiKey_LeftArrow))                        { edit_state.OnKeyPressed(is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT | k_mask : STB_TEXTEDIT_K_LEFT | k_mask); }
7481         else if (IsKeyPressedMap(ImGuiKey_RightArrow))                  { edit_state.OnKeyPressed(is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT | k_mask  : STB_TEXTEDIT_K_RIGHT | k_mask); }
7482         else if (is_multiline && IsKeyPressedMap(ImGuiKey_UpArrow))     { if (is_ctrl_down) SetWindowScrollY(draw_window, draw_window->Scroll.y - g.FontSize); else edit_state.OnKeyPressed(STB_TEXTEDIT_K_UP | k_mask); }
7483         else if (is_multiline && IsKeyPressedMap(ImGuiKey_DownArrow))   { if (is_ctrl_down) SetWindowScrollY(draw_window, draw_window->Scroll.y + g.FontSize); else edit_state.OnKeyPressed(STB_TEXTEDIT_K_DOWN| k_mask); }
7484         else if (IsKeyPressedMap(ImGuiKey_Home))                        { edit_state.OnKeyPressed(is_ctrl_down ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
7485         else if (IsKeyPressedMap(ImGuiKey_End))                         { edit_state.OnKeyPressed(is_ctrl_down ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
7486         else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable)       { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
7487         else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable)    { edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); }
7488         else if (IsKeyPressedMap(ImGuiKey_Enter))
7489         {
7490             bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;
7491             if (!is_multiline || (ctrl_enter_for_new_line && !is_ctrl_down) || (!ctrl_enter_for_new_line && is_ctrl_down))
7492             {
7493                 SetActiveID(0);
7494                 enter_pressed = true;
7495             }
7496             else if (is_editable)
7497             {
7498                 unsigned int c = '\n'; // Insert new line
7499                 if (InputTextFilterCharacter(&c, flags, callback, user_data))
7500                     edit_state.OnKeyPressed((int)c);
7501             }
7502         }
7503         else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !is_ctrl_down && !is_shift_down && !is_alt_down && is_editable)
7504         {
7505             unsigned int c = '\t'; // Insert TAB
7506             if (InputTextFilterCharacter(&c, flags, callback, user_data))
7507                 edit_state.OnKeyPressed((int)c);
7508         }
7509         else if (IsKeyPressedMap(ImGuiKey_Escape))                                     { SetActiveID(0); cancel_edit = true; }
7510         else if (is_shortcutkey_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable)    { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); }
7511         else if (is_shortcutkey_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable)    { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); }
7512         else if (is_shortcutkey_only && IsKeyPressedMap(ImGuiKey_A))                   { edit_state.SelectAll(); edit_state.CursorFollow = true; }
7513         else if (is_shortcutkey_only && !is_password && ((IsKeyPressedMap(ImGuiKey_X) && is_editable) || IsKeyPressedMap(ImGuiKey_C)) && (!is_multiline || edit_state.HasSelection()))
7514         {
7515             // Cut, Copy
7516             const bool cut = IsKeyPressedMap(ImGuiKey_X);
7517             if (cut && !edit_state.HasSelection())
7518                 edit_state.SelectAll();
7519 
7520             if (g.IO.SetClipboardTextFn)
7521             {
7522                 const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0;
7523                 const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW;
7524                 edit_state.TempTextBuffer.resize((ie-ib) * 4 + 1);
7525                 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data+ib, edit_state.Text.Data+ie);
7526                 g.IO.SetClipboardTextFn(edit_state.TempTextBuffer.Data);
7527             }
7528 
7529             if (cut)
7530             {
7531                 edit_state.CursorFollow = true;
7532                 stb_textedit_cut(&edit_state, &edit_state.StbState);
7533             }
7534         }
7535         else if (is_shortcutkey_only && IsKeyPressedMap(ImGuiKey_V) && is_editable)
7536         {
7537             // Paste
7538             if (g.IO.GetClipboardTextFn)
7539             {
7540                 if (const char* clipboard = g.IO.GetClipboardTextFn())
7541                 {
7542                     // Remove new-line from pasted buffer
7543                     const int clipboard_len = (int)strlen(clipboard);
7544                     ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar));
7545                     int clipboard_filtered_len = 0;
7546                     for (const char* s = clipboard; *s; )
7547                     {
7548                         unsigned int c;
7549                         s += ImTextCharFromUtf8(&c, s, NULL);
7550                         if (c == 0)
7551                             break;
7552                         if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, user_data))
7553                             continue;
7554                         clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;
7555                     }
7556                     clipboard_filtered[clipboard_filtered_len] = 0;
7557                     if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation
7558                     {
7559                         stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len);
7560                         edit_state.CursorFollow = true;
7561                     }
7562                     ImGui::MemFree(clipboard_filtered);
7563                 }
7564             }
7565         }
7566 
7567         if (cancel_edit)
7568         {
7569             // Restore initial value
7570             if (is_editable)
7571             {
7572                 ImFormatString(buf, buf_size, "%s", edit_state.InitialText.Data);
7573                 value_changed = true;
7574             }
7575         }
7576         else
7577         {
7578             // Apply new value immediately - copy modified buffer back
7579             // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer
7580             // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect.
7581             // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks.
7582             if (is_editable)
7583             {
7584                 edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4);
7585                 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL);
7586             }
7587 
7588             // User callback
7589             if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0)
7590             {
7591                 IM_ASSERT(callback != NULL);
7592 
7593                 // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
7594                 ImGuiInputTextFlags event_flag = 0;
7595                 ImGuiKey event_key = ImGuiKey_COUNT;
7596                 if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab))
7597                 {
7598                     event_flag = ImGuiInputTextFlags_CallbackCompletion;
7599                     event_key = ImGuiKey_Tab;
7600                 }
7601                 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow))
7602                 {
7603                     event_flag = ImGuiInputTextFlags_CallbackHistory;
7604                     event_key = ImGuiKey_UpArrow;
7605                 }
7606                 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow))
7607                 {
7608                     event_flag = ImGuiInputTextFlags_CallbackHistory;
7609                     event_key = ImGuiKey_DownArrow;
7610                 }
7611                 else if (flags & ImGuiInputTextFlags_CallbackAlways)
7612                     event_flag = ImGuiInputTextFlags_CallbackAlways;
7613 
7614                 if (event_flag)
7615                 {
7616                     ImGuiTextEditCallbackData callback_data;
7617                     memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
7618                     callback_data.EventFlag = event_flag;
7619                     callback_data.Flags = flags;
7620                     callback_data.UserData = user_data;
7621                     callback_data.ReadOnly = !is_editable;
7622 
7623                     callback_data.EventKey = event_key;
7624                     callback_data.Buf = edit_state.TempTextBuffer.Data;
7625                     callback_data.BufTextLen = edit_state.CurLenA;
7626                     callback_data.BufSize = edit_state.BufSizeA;
7627                     callback_data.BufDirty = false;
7628 
7629                     // 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)
7630                     ImWchar* text = edit_state.Text.Data;
7631                     const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor);
7632                     const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start);
7633                     const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end);
7634 
7635                     // Call user code
7636                     callback(&callback_data);
7637 
7638                     // Read back what user may have modified
7639                     IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data);  // Invalid to modify those fields
7640                     IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA);
7641                     IM_ASSERT(callback_data.Flags == flags);
7642                     if (callback_data.CursorPos != utf8_cursor_pos)            edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos);
7643                     if (callback_data.SelectionStart != utf8_selection_start)  edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart);
7644                     if (callback_data.SelectionEnd != utf8_selection_end)      edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd);
7645                     if (callback_data.BufDirty)
7646                     {
7647                         IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
7648                         edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, callback_data.Buf, NULL);
7649                         edit_state.CurLenA = callback_data.BufTextLen;  // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
7650                         edit_state.CursorAnimReset();
7651                     }
7652                 }
7653             }
7654 
7655             // Copy back to user buffer
7656             if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0)
7657             {
7658                 ImFormatString(buf, buf_size, "%s", edit_state.TempTextBuffer.Data);
7659                 value_changed = true;
7660             }
7661         }
7662     }
7663 
7664     if (!is_multiline)
7665         RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
7666 
7667     // Render
7668     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
7669     ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;
7670     ImVec2 text_size(0.f, 0.f);
7671     if (g.ActiveId == id || (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetID("#SCROLLY")))
7672     {
7673         edit_state.CursorAnim += g.IO.DeltaTime;
7674 
7675         // We need to:
7676         // - Display the text (this can be more easily clipped)
7677         // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)
7678         // - Measure text height (for scrollbar)
7679         // 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)
7680         const ImWchar* text_begin = edit_state.Text.Data;
7681         ImVec2 cursor_offset, select_start_offset;
7682 
7683         {
7684             // Count lines + find lines numbers straddling 'cursor' and 'select_start' position.
7685             const ImWchar* searches_input_ptr[2];
7686             searches_input_ptr[0] = text_begin + edit_state.StbState.cursor;
7687             searches_input_ptr[1] = NULL;
7688             int searches_remaining = 1;
7689             int searches_result_line_number[2] = { -1, -999 };
7690             if (edit_state.StbState.select_start != edit_state.StbState.select_end)
7691             {
7692                 searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
7693                 searches_result_line_number[1] = -1;
7694                 searches_remaining++;
7695             }
7696 
7697             // Iterate all lines to find our line numbers
7698             // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter.
7699             searches_remaining += is_multiline ? 1 : 0;
7700             int line_count = 0;
7701             for (const ImWchar* s = text_begin; *s != 0; s++)
7702                 if (*s == '\n')
7703                 {
7704                     line_count++;
7705                     if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; }
7706                     if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; }
7707                 }
7708             line_count++;
7709             if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count;
7710             if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count;
7711 
7712             // Calculate 2d position by finding the beginning of the line and measuring distance
7713             cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;
7714             cursor_offset.y = searches_result_line_number[0] * g.FontSize;
7715             if (searches_result_line_number[1] >= 0)
7716             {
7717                 select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;
7718                 select_start_offset.y = searches_result_line_number[1] * g.FontSize;
7719             }
7720 
7721             // Calculate text height
7722             if (is_multiline)
7723                 text_size = ImVec2(size.x, line_count * g.FontSize);
7724         }
7725 
7726         // Scroll
7727         if (edit_state.CursorFollow)
7728         {
7729             // Horizontal scroll in chunks of quarter width
7730             if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll))
7731             {
7732                 const float scroll_increment_x = size.x * 0.25f;
7733                 if (cursor_offset.x < edit_state.ScrollX)
7734                     edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x);
7735                 else if (cursor_offset.x - size.x >= edit_state.ScrollX)
7736                     edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x);
7737             }
7738             else
7739             {
7740                 edit_state.ScrollX = 0.0f;
7741             }
7742 
7743             // Vertical scroll
7744             if (is_multiline)
7745             {
7746                 float scroll_y = draw_window->Scroll.y;
7747                 if (cursor_offset.y - g.FontSize < scroll_y)
7748                     scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
7749                 else if (cursor_offset.y - size.y >= scroll_y)
7750                     scroll_y = cursor_offset.y - size.y;
7751                 draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y);   // To avoid a frame of lag
7752                 draw_window->Scroll.y = scroll_y;
7753                 render_pos.y = draw_window->DC.CursorPos.y;
7754             }
7755         }
7756         edit_state.CursorFollow = false;
7757         const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f);
7758 
7759         // Draw selection
7760         if (edit_state.StbState.select_start != edit_state.StbState.select_end)
7761         {
7762             const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
7763             const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end);
7764 
7765             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.
7766             float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
7767             ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg);
7768             ImVec2 rect_pos = render_pos + select_start_offset - render_scroll;
7769             for (const ImWchar* p = text_selected_begin; p < text_selected_end; )
7770             {
7771                 if (rect_pos.y > clip_rect.w + g.FontSize)
7772                     break;
7773                 if (rect_pos.y < clip_rect.y)
7774                 {
7775                     while (p < text_selected_end)
7776                         if (*p++ == '\n')
7777                             break;
7778                 }
7779                 else
7780                 {
7781                     ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true);
7782                     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
7783                     ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn));
7784                     rect.Clip(clip_rect);
7785                     if (rect.Overlaps(clip_rect))
7786                         draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
7787                 }
7788                 rect_pos.x = render_pos.x - render_scroll.x;
7789                 rect_pos.y += g.FontSize;
7790             }
7791         }
7792 
7793         draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf, buf+edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect);
7794 
7795         // Draw blinking cursor
7796         ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll;
7797         bool cursor_is_visible = (g.InputTextState.CursorAnim <= 0.0f) || fmodf(g.InputTextState.CursorAnim, 1.20f) <= 0.80f;
7798         if (cursor_is_visible)
7799             draw_window->DrawList->AddLine(cursor_screen_pos + ImVec2(0.0f,-g.FontSize+0.5f), cursor_screen_pos + ImVec2(0.0f,-1.5f), GetColorU32(ImGuiCol_Text));
7800 
7801         // 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.)
7802         if (is_editable)
7803             g.OsImePosRequest = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize);
7804     }
7805     else
7806     {
7807         // Render text only
7808         const char* buf_end = NULL;
7809         if (is_multiline)
7810             text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf, &buf_end) * g.FontSize); // We don't need width
7811         draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf, buf_end, 0.0f, is_multiline ? NULL : &clip_rect);
7812     }
7813 
7814     if (is_multiline)
7815     {
7816         ImGui::Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line
7817         ImGui::EndChildFrame();
7818         ImGui::EndGroup();
7819     }
7820 
7821     if (is_password)
7822         ImGui::PopFont();
7823 
7824     // Log as text
7825     if (g.LogEnabled && !is_password)
7826         LogRenderedText(render_pos, buf, NULL);
7827 
7828     if (label_size.x > 0)
7829         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
7830 
7831     if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)
7832         return enter_pressed;
7833     else
7834         return value_changed;
7835 }
7836 
InputText(const char * label,char * buf,size_t buf_size,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)7837 bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
7838 {
7839     IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline()
7840     bool ret = InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data);
7841     return ret;
7842 }
7843 
InputTextMultiline(const char * label,char * buf,size_t buf_size,const ImVec2 & size,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)7844 bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
7845 {
7846     bool ret = InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);
7847     return ret;
7848 }
7849 
7850 // 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)7851 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)
7852 {
7853     ImGuiWindow* window = GetCurrentWindow();
7854     if (window->SkipItems)
7855         return false;
7856 
7857     ImGuiState& g = *GImGui;
7858     const ImGuiStyle& style = g.Style;
7859     const ImVec2 label_size = CalcTextSize(label, NULL, true);
7860 
7861     ImGui::BeginGroup();
7862     ImGui::PushID(label);
7863     const ImVec2 button_sz = ImVec2(g.FontSize, g.FontSize) + style.FramePadding*2.0f;
7864     if (step_ptr)
7865         ImGui::PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_sz.x + style.ItemInnerSpacing.x)*2));
7866 
7867     char buf[64];
7868     DataTypeFormatString(data_type, data_ptr, scalar_format, buf, IM_ARRAYSIZE(buf));
7869 
7870     bool value_changed = false;
7871     if (!(extra_flags & ImGuiInputTextFlags_CharsHexadecimal))
7872         extra_flags |= ImGuiInputTextFlags_CharsDecimal;
7873     extra_flags |= ImGuiInputTextFlags_AutoSelectAll;
7874     if (ImGui::InputText("", buf, IM_ARRAYSIZE(buf), extra_flags))
7875         value_changed = DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, scalar_format);
7876 
7877     // Step buttons
7878     if (step_ptr)
7879     {
7880         ImGui::PopItemWidth();
7881         ImGui::SameLine(0, style.ItemInnerSpacing.x);
7882         if (ButtonEx("-", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
7883         {
7884             DataTypeApplyOp(data_type, '-', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
7885             value_changed = true;
7886         }
7887         ImGui::SameLine(0, style.ItemInnerSpacing.x);
7888         if (ButtonEx("+", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
7889         {
7890             DataTypeApplyOp(data_type, '+', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
7891             value_changed = true;
7892         }
7893     }
7894     ImGui::PopID();
7895 
7896     if (label_size.x > 0)
7897     {
7898         ImGui::SameLine(0, style.ItemInnerSpacing.x);
7899         RenderText(ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + style.FramePadding.y), label);
7900         ItemSize(label_size, style.FramePadding.y);
7901     }
7902     ImGui::EndGroup();
7903 
7904     return value_changed;
7905 }
7906 
InputFloat(const char * label,float * v,float step,float step_fast,int decimal_precision,ImGuiInputTextFlags extra_flags)7907 bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags)
7908 {
7909     char display_format[16];
7910     if (decimal_precision < 0)
7911         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
7912     else
7913         ImFormatString(display_format, 16, "%%.%df", decimal_precision);
7914     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);
7915 }
7916 
InputInt(const char * label,int * v,int step,int step_fast,ImGuiInputTextFlags extra_flags)7917 bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags)
7918 {
7919     // 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.
7920     const char* scalar_format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d";
7921     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);
7922 }
7923 
InputFloatN(const char * label,float * v,int components,int decimal_precision,ImGuiInputTextFlags extra_flags)7924 bool ImGui::InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags)
7925 {
7926     ImGuiWindow* window = GetCurrentWindow();
7927     if (window->SkipItems)
7928         return false;
7929 
7930     ImGuiState& g = *GImGui;
7931     bool value_changed = false;
7932     ImGui::BeginGroup();
7933     ImGui::PushID(label);
7934     PushMultiItemsWidths(components);
7935     for (int i = 0; i < components; i++)
7936     {
7937         ImGui::PushID(i);
7938         value_changed |= ImGui::InputFloat("##v", &v[i], 0, 0, decimal_precision, extra_flags);
7939         ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
7940         ImGui::PopID();
7941         ImGui::PopItemWidth();
7942     }
7943     ImGui::PopID();
7944 
7945     window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
7946     ImGui::TextUnformatted(label, FindRenderedTextEnd(label));
7947     ImGui::EndGroup();
7948 
7949     return value_changed;
7950 }
7951 
InputFloat2(const char * label,float v[2],int decimal_precision,ImGuiInputTextFlags extra_flags)7952 bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags)
7953 {
7954     return InputFloatN(label, v, 2, decimal_precision, extra_flags);
7955 }
7956 
InputFloat3(const char * label,float v[3],int decimal_precision,ImGuiInputTextFlags extra_flags)7957 bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags)
7958 {
7959     return InputFloatN(label, v, 3, decimal_precision, extra_flags);
7960 }
7961 
InputFloat4(const char * label,float v[4],int decimal_precision,ImGuiInputTextFlags extra_flags)7962 bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags)
7963 {
7964     return InputFloatN(label, v, 4, decimal_precision, extra_flags);
7965 }
7966 
InputIntN(const char * label,int * v,int components,ImGuiInputTextFlags extra_flags)7967 bool ImGui::InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags)
7968 {
7969     ImGuiWindow* window = GetCurrentWindow();
7970     if (window->SkipItems)
7971         return false;
7972 
7973     ImGuiState& g = *GImGui;
7974     bool value_changed = false;
7975     ImGui::BeginGroup();
7976     ImGui::PushID(label);
7977     PushMultiItemsWidths(components);
7978     for (int i = 0; i < components; i++)
7979     {
7980         ImGui::PushID(i);
7981         value_changed |= ImGui::InputInt("##v", &v[i], 0, 0, extra_flags);
7982         ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
7983         ImGui::PopID();
7984         ImGui::PopItemWidth();
7985     }
7986     ImGui::PopID();
7987 
7988     window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
7989     ImGui::TextUnformatted(label, FindRenderedTextEnd(label));
7990     ImGui::EndGroup();
7991 
7992     return value_changed;
7993 }
7994 
InputInt2(const char * label,int v[2],ImGuiInputTextFlags extra_flags)7995 bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags)
7996 {
7997     return InputIntN(label, v, 2, extra_flags);
7998 }
7999 
InputInt3(const char * label,int v[3],ImGuiInputTextFlags extra_flags)8000 bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags)
8001 {
8002     return InputIntN(label, v, 3, extra_flags);
8003 }
8004 
InputInt4(const char * label,int v[4],ImGuiInputTextFlags extra_flags)8005 bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags)
8006 {
8007     return InputIntN(label, v, 4, extra_flags);
8008 }
8009 
Items_ArrayGetter(void * data,int idx,const char ** out_text)8010 static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
8011 {
8012     const char** items = (const char**)data;
8013     if (out_text)
8014         *out_text = items[idx];
8015     return true;
8016 }
8017 
Items_SingleStringGetter(void * data,int idx,const char ** out_text)8018 static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
8019 {
8020     // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
8021     const char* items_separated_by_zeros = (const char*)data;
8022     int items_count = 0;
8023     const char* p = items_separated_by_zeros;
8024     while (*p)
8025     {
8026         if (idx == items_count)
8027             break;
8028         p += strlen(p) + 1;
8029         items_count++;
8030     }
8031     if (!*p)
8032         return false;
8033     if (out_text)
8034         *out_text = p;
8035     return true;
8036 }
8037 
8038 // Combo box helper allowing to pass an array of strings.
Combo(const char * label,int * current_item,const char ** items,int items_count,int height_in_items)8039 bool ImGui::Combo(const char* label, int* current_item, const char** items, int items_count, int height_in_items)
8040 {
8041     const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
8042     return value_changed;
8043 }
8044 
8045 // 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)8046 bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
8047 {
8048     int items_count = 0;
8049     const char* p = items_separated_by_zeros;       // FIXME-OPT: Avoid computing this, or at least only when combo is open
8050     while (*p)
8051     {
8052         p += strlen(p) + 1;
8053         items_count++;
8054     }
8055     bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
8056     return value_changed;
8057 }
8058 
8059 // 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)8060 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)
8061 {
8062     ImGuiWindow* window = GetCurrentWindow();
8063     if (window->SkipItems)
8064         return false;
8065 
8066     ImGuiState& g = *GImGui;
8067     const ImGuiStyle& style = g.Style;
8068     const ImGuiID id = window->GetID(label);
8069     const float w = CalcItemWidth();
8070 
8071     const ImVec2 label_size = CalcTextSize(label, NULL, true);
8072     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
8073     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));
8074     ItemSize(total_bb, style.FramePadding.y);
8075     if (!ItemAdd(total_bb, &id))
8076         return false;
8077 
8078     const float arrow_size = (g.FontSize + style.FramePadding.x * 2.0f);
8079     const bool hovered = IsHovered(frame_bb, id);
8080     bool popup_opened = IsPopupOpen(id);
8081     bool popup_opened_now = false;
8082 
8083     const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
8084     RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
8085     RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(popup_opened || hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding); // FIXME-ROUNDING
8086     RenderCollapseTriangle(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y) + style.FramePadding, true);
8087 
8088     if (*current_item >= 0 && *current_item < items_count)
8089     {
8090         const char* item_text;
8091         if (items_getter(data, *current_item, &item_text))
8092             RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, item_text, NULL, NULL);
8093     }
8094 
8095     if (label_size.x > 0)
8096         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
8097 
8098     if (hovered)
8099     {
8100         SetHoveredID(id);
8101         if (g.IO.MouseClicked[0])
8102         {
8103             SetActiveID(0);
8104             if (IsPopupOpen(id))
8105             {
8106                 ClosePopup(id);
8107             }
8108             else
8109             {
8110                 FocusWindow(window);
8111                 OpenPopup(label);
8112                 popup_opened = popup_opened_now = true;
8113             }
8114         }
8115     }
8116 
8117     bool value_changed = false;
8118     if (IsPopupOpen(id))
8119     {
8120         // Size default to hold ~7 items
8121         if (height_in_items < 0)
8122             height_in_items = 7;
8123 
8124         float popup_height = (label_size.y + style.ItemSpacing.y) * ImMin(items_count, height_in_items) + (style.FramePadding.y * 3);
8125         float popup_y1 = frame_bb.Max.y;
8126         float popup_y2 = ImClamp(popup_y1 + popup_height, popup_y1, g.IO.DisplaySize.y - style.DisplaySafeAreaPadding.y);
8127         if ((popup_y2 - popup_y1) < ImMin(popup_height, frame_bb.Min.y - style.DisplaySafeAreaPadding.y))
8128         {
8129             // 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)
8130             popup_y1 = ImClamp(frame_bb.Min.y - popup_height, style.DisplaySafeAreaPadding.y, frame_bb.Min.y);
8131             popup_y2 = frame_bb.Min.y;
8132         }
8133         ImRect popup_rect(ImVec2(frame_bb.Min.x, popup_y1), ImVec2(frame_bb.Max.x, popup_y2));
8134         ImGui::SetNextWindowPos(popup_rect.Min);
8135         ImGui::SetNextWindowSize(popup_rect.GetSize());
8136         ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
8137 
8138         const ImGuiWindowFlags flags = ImGuiWindowFlags_ComboBox | ((window->Flags & ImGuiWindowFlags_ShowBorders) ? ImGuiWindowFlags_ShowBorders : 0);
8139         if (BeginPopupEx(label, flags))
8140         {
8141             // Display items
8142             ImGui::Spacing();
8143             for (int i = 0; i < items_count; i++)
8144             {
8145                 ImGui::PushID((void*)(intptr_t)i);
8146                 const bool item_selected = (i == *current_item);
8147                 const char* item_text;
8148                 if (!items_getter(data, i, &item_text))
8149                     item_text = "*Unknown item*";
8150                 if (ImGui::Selectable(item_text, item_selected))
8151                 {
8152                     SetActiveID(0);
8153                     value_changed = true;
8154                     *current_item = i;
8155                 }
8156                 if (item_selected && popup_opened_now)
8157                     ImGui::SetScrollHere();
8158                 ImGui::PopID();
8159             }
8160             ImGui::EndPopup();
8161         }
8162         ImGui::PopStyleVar();
8163     }
8164     return value_changed;
8165 }
8166 
8167 // Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image.
8168 // 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)8169 bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
8170 {
8171     ImGuiWindow* window = GetCurrentWindow();
8172     if (window->SkipItems)
8173         return false;
8174 
8175     ImGuiState& g = *GImGui;
8176     const ImGuiStyle& style = g.Style;
8177 
8178     if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
8179         PopClipRect();
8180 
8181     ImGuiID id = window->GetID(label);
8182     ImVec2 label_size = CalcTextSize(label, NULL, true);
8183     ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
8184     ImVec2 pos = window->DC.CursorPos;
8185     pos.y += window->DC.CurrentLineTextBaseOffset;
8186     ImRect bb(pos, pos + size);
8187     ItemSize(bb);
8188 
8189     // Fill horizontal space.
8190     ImVec2 window_padding = window->WindowPadding;
8191     float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? ImGui::GetWindowContentRegionMax().x : ImGui::GetContentRegionMax().x;
8192     float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x);
8193     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);
8194     ImRect bb_with_spacing(pos, pos + size_draw);
8195     if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth))
8196         bb_with_spacing.Max.x += window_padding.x;
8197 
8198     // Selectables are tightly packed together, we extend the box to cover spacing between selectable.
8199     float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f);
8200     float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f);
8201     float spacing_R = style.ItemSpacing.x - spacing_L;
8202     float spacing_D = style.ItemSpacing.y - spacing_U;
8203     bb_with_spacing.Min.x -= spacing_L;
8204     bb_with_spacing.Min.y -= spacing_U;
8205     bb_with_spacing.Max.x += spacing_R;
8206     bb_with_spacing.Max.y += spacing_D;
8207     if (!ItemAdd(bb_with_spacing, &id))
8208     {
8209         if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
8210             PushColumnClipRect();
8211         return false;
8212     }
8213 
8214     ImGuiButtonFlags button_flags = 0;
8215     if (flags & ImGuiSelectableFlags_Menu) button_flags |= ImGuiButtonFlags_PressedOnClick;
8216     if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnClick|ImGuiButtonFlags_PressedOnRelease;
8217     if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled;
8218     if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnDoubleClick;
8219     bool hovered, held;
8220     bool pressed = ButtonBehavior(bb_with_spacing, id, &hovered, &held, button_flags);
8221     if (flags & ImGuiSelectableFlags_Disabled)
8222         selected = false;
8223 
8224     // Render
8225     if (hovered || selected)
8226     {
8227         const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
8228         RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, 0.0f);
8229     }
8230 
8231     if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
8232     {
8233         PushColumnClipRect();
8234         bb_with_spacing.Max.x -= (ImGui::GetContentRegionMax().x - max_x);
8235     }
8236 
8237     if (flags & ImGuiSelectableFlags_Disabled) ImGui::PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
8238     RenderTextClipped(bb.Min, bb_with_spacing.Max, label, NULL, &label_size);
8239     if (flags & ImGuiSelectableFlags_Disabled) ImGui::PopStyleColor();
8240 
8241     // Automatically close popups
8242     if (pressed && !(flags & ImGuiSelectableFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
8243         ImGui::CloseCurrentPopup();
8244     return pressed;
8245 }
8246 
Selectable(const char * label,bool * p_selected,ImGuiSelectableFlags flags,const ImVec2 & size_arg)8247 bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
8248 {
8249     if (ImGui::Selectable(label, *p_selected, flags, size_arg))
8250     {
8251         *p_selected = !*p_selected;
8252         return true;
8253     }
8254     return false;
8255 }
8256 
8257 // Helper to calculate the size of a listbox and display a label on the right.
8258 // 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)8259 bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
8260 {
8261     ImGuiWindow* window = GetCurrentWindow();
8262     if (window->SkipItems)
8263         return false;
8264 
8265     const ImGuiStyle& style = ImGui::GetStyle();
8266     const ImGuiID id = ImGui::GetID(label);
8267     const ImVec2 label_size = CalcTextSize(label, NULL, true);
8268 
8269     // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
8270     ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), ImGui::GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y);
8271     ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y));
8272     ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
8273     ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
8274     window->DC.LastItemRect = bb;
8275 
8276     ImGui::BeginGroup();
8277     if (label_size.x > 0)
8278         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
8279 
8280     ImGui::BeginChildFrame(id, frame_bb.GetSize());
8281     return true;
8282 }
8283 
ListBoxHeader(const char * label,int items_count,int height_in_items)8284 bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items)
8285 {
8286     // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
8287     // 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.
8288     // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution.
8289     if (height_in_items < 0)
8290         height_in_items = ImMin(items_count, 7);
8291     float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f);
8292 
8293     // 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().
8294     ImVec2 size;
8295     size.x = 0.0f;
8296     size.y = ImGui::GetTextLineHeightWithSpacing() * height_in_items_f + ImGui::GetStyle().ItemSpacing.y;
8297     return ImGui::ListBoxHeader(label, size);
8298 }
8299 
ListBoxFooter()8300 void ImGui::ListBoxFooter()
8301 {
8302     ImGuiWindow* parent_window = GetParentWindow();
8303     const ImRect bb = parent_window->DC.LastItemRect;
8304     const ImGuiStyle& style = ImGui::GetStyle();
8305 
8306     ImGui::EndChildFrame();
8307 
8308     // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect)
8309     // We call SameLine() to restore DC.CurrentLine* data
8310     ImGui::SameLine();
8311     parent_window->DC.CursorPos = bb.Min;
8312     ItemSize(bb, style.FramePadding.y);
8313     ImGui::EndGroup();
8314 }
8315 
ListBox(const char * label,int * current_item,const char ** items,int items_count,int height_items)8316 bool ImGui::ListBox(const char* label, int* current_item, const char** items, int items_count, int height_items)
8317 {
8318     const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items);
8319     return value_changed;
8320 }
8321 
ListBox(const char * label,int * current_item,bool (* items_getter)(void *,int,const char **),void * data,int items_count,int height_in_items)8322 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)
8323 {
8324     if (!ImGui::ListBoxHeader(label, items_count, height_in_items))
8325         return false;
8326 
8327     // 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.
8328     bool value_changed = false;
8329     ImGuiListClipper clipper(items_count, ImGui::GetTextLineHeightWithSpacing());
8330     for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
8331     {
8332         const bool item_selected = (i == *current_item);
8333         const char* item_text;
8334         if (!items_getter(data, i, &item_text))
8335             item_text = "*Unknown item*";
8336 
8337         ImGui::PushID(i);
8338         if (ImGui::Selectable(item_text, item_selected))
8339         {
8340             *current_item = i;
8341             value_changed = true;
8342         }
8343         ImGui::PopID();
8344     }
8345     clipper.End();
8346     ImGui::ListBoxFooter();
8347     return value_changed;
8348 }
8349 
MenuItem(const char * label,const char * shortcut,bool selected,bool enabled)8350 bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)
8351 {
8352     ImGuiWindow* window = GetCurrentWindow();
8353     if (window->SkipItems)
8354         return false;
8355 
8356     ImGuiState& g = *GImGui;
8357     ImVec2 pos = window->DC.CursorPos;
8358     ImVec2 label_size = CalcTextSize(label, NULL, true);
8359     ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f);
8360     float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame
8361     float extra_w = ImMax(0.0f, ImGui::GetContentRegionAvail().x - w);
8362 
8363     bool pressed = ImGui::Selectable(label, false, ImGuiSelectableFlags_MenuItem | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
8364     if (shortcut_size.x > 0.0f)
8365     {
8366         ImGui::PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
8367         RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
8368         ImGui::PopStyleColor();
8369     }
8370 
8371     if (selected)
8372         RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), GetColorU32(ImGuiCol_Text));
8373 
8374     return pressed;
8375 }
8376 
MenuItem(const char * label,const char * shortcut,bool * p_selected,bool enabled)8377 bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled)
8378 {
8379     if (ImGui::MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled))
8380     {
8381         if (p_selected)
8382             *p_selected = !*p_selected;
8383         return true;
8384     }
8385     return false;
8386 }
8387 
BeginMainMenuBar()8388 bool ImGui::BeginMainMenuBar()
8389 {
8390     ImGuiState& g = *GImGui;
8391     ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f));
8392     ImGui::SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.FontBaseSize + g.Style.FramePadding.y * 2.0f));
8393     ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
8394     ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0));
8395     if (!ImGui::Begin("##MainMenuBar", NULL, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_MenuBar)
8396         || !ImGui::BeginMenuBar())
8397     {
8398         ImGui::End();
8399         ImGui::PopStyleVar(2);
8400         return false;
8401     }
8402     g.CurrentWindow->DC.MenuBarOffsetX += g.Style.DisplaySafeAreaPadding.x;
8403     return true;
8404 }
8405 
EndMainMenuBar()8406 void ImGui::EndMainMenuBar()
8407 {
8408     ImGui::EndMenuBar();
8409     ImGui::End();
8410     ImGui::PopStyleVar(2);
8411 }
8412 
BeginMenuBar()8413 bool ImGui::BeginMenuBar()
8414 {
8415     ImGuiWindow* window = GetCurrentWindow();
8416     if (window->SkipItems)
8417         return false;
8418     if (!(window->Flags & ImGuiWindowFlags_MenuBar))
8419         return false;
8420 
8421     IM_ASSERT(!window->DC.MenuBarAppending);
8422     ImGui::BeginGroup(); // Save position
8423     ImGui::PushID("##menubar");
8424     ImRect rect = window->MenuBarRect();
8425     PushClipRect(ImVec2(rect.Min.x+0.5f, rect.Min.y-0.5f+window->BorderSize), ImVec2(rect.Max.x+0.5f, rect.Max.y-0.5f), false);
8426     window->DC.CursorPos = ImVec2(rect.Min.x + window->DC.MenuBarOffsetX, rect.Min.y);// + g.Style.FramePadding.y);
8427     window->DC.LayoutType = ImGuiLayoutType_Horizontal;
8428     window->DC.MenuBarAppending = true;
8429     ImGui::AlignFirstTextHeightToWidgets();
8430     return true;
8431 }
8432 
EndMenuBar()8433 void ImGui::EndMenuBar()
8434 {
8435     ImGuiWindow* window = GetCurrentWindow();
8436     if (window->SkipItems)
8437         return;
8438 
8439     IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);
8440     IM_ASSERT(window->DC.MenuBarAppending);
8441     PopClipRect();
8442     ImGui::PopID();
8443     window->DC.MenuBarOffsetX = window->DC.CursorPos.x - window->MenuBarRect().Min.x;
8444     window->DC.GroupStack.back().AdvanceCursor = false;
8445     ImGui::EndGroup();
8446     window->DC.LayoutType = ImGuiLayoutType_Vertical;
8447     window->DC.MenuBarAppending = false;
8448 }
8449 
BeginMenu(const char * label,bool enabled)8450 bool ImGui::BeginMenu(const char* label, bool enabled)
8451 {
8452     ImGuiWindow* window = GetCurrentWindow();
8453     if (window->SkipItems)
8454         return false;
8455 
8456     ImGuiState& g = *GImGui;
8457     const ImGuiStyle& style = g.Style;
8458     const ImGuiID id = window->GetID(label);
8459 
8460     ImVec2 label_size = CalcTextSize(label, NULL, true);
8461     ImGuiWindow* backed_focused_window = g.FocusedWindow;
8462 
8463     bool pressed;
8464     bool opened = IsPopupOpen(id);
8465     bool menuset_opened = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenedPopupStack.Size > g.CurrentPopupStack.Size && g.OpenedPopupStack[g.CurrentPopupStack.Size].ParentMenuSet == window->GetID("##menus"));
8466     if (menuset_opened)
8467         g.FocusedWindow = window;
8468 
8469     ImVec2 popup_pos, pos = window->DC.CursorPos;
8470     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
8471     {
8472         popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight());
8473         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
8474         ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
8475         float w = label_size.x;
8476         pressed = ImGui::Selectable(label, opened, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
8477         ImGui::PopStyleVar();
8478         ImGui::SameLine();
8479         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
8480     }
8481     else
8482     {
8483         popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
8484         float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame
8485         float extra_w = ImMax(0.0f, ImGui::GetContentRegionAvail().x - w);
8486         pressed = ImGui::Selectable(label, opened, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
8487         if (!enabled) ImGui::PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
8488         RenderCollapseTriangle(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), false);
8489         if (!enabled) ImGui::PopStyleColor();
8490     }
8491 
8492     bool hovered = enabled && IsHovered(window->DC.LastItemRect, id);
8493     if (menuset_opened)
8494         g.FocusedWindow = backed_focused_window;
8495 
8496     bool want_open = false, want_close = false;
8497     if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
8498     {
8499         // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
8500         bool moving_within_opened_triangle = false;
8501         if (g.HoveredWindow == window && g.OpenedPopupStack.Size > g.CurrentPopupStack.Size && g.OpenedPopupStack[g.CurrentPopupStack.Size].ParentWindow == window)
8502         {
8503             if (ImGuiWindow* next_window = g.OpenedPopupStack[g.CurrentPopupStack.Size].Window)
8504             {
8505                 ImRect next_window_rect = next_window->Rect();
8506                 ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
8507                 ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
8508                 ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
8509                 float extra = ImClamp(fabsf(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
8510                 ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f;   // to avoid numerical issues
8511                 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
8512                 tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
8513                 moving_within_opened_triangle = ImIsPointInTriangle(g.IO.MousePos, ta, tb, tc);
8514                 //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? 0x80008000 : 0x80000080); window->DrawList->PopClipRect(); // Debug
8515             }
8516         }
8517 
8518         want_close = (opened && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle);
8519         want_open = (!opened && hovered && !moving_within_opened_triangle) || (!opened && hovered && pressed);
8520     }
8521     else if (opened && pressed && menuset_opened) // menu-bar: click open menu to close
8522     {
8523         want_close = true;
8524         want_open = opened = false;
8525     }
8526     else if (pressed || (hovered && menuset_opened && !opened)) // menu-bar: first click to open, then hover to open others
8527         want_open = true;
8528 
8529     if (want_close && IsPopupOpen(id))
8530         ClosePopupToLevel(GImGui->CurrentPopupStack.Size);
8531 
8532     if (!opened && want_open && g.OpenedPopupStack.Size > g.CurrentPopupStack.Size)
8533     {
8534         // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
8535         ImGui::OpenPopup(label);
8536         return false;
8537     }
8538 
8539     opened |= want_open;
8540     if (want_open)
8541         ImGui::OpenPopup(label);
8542 
8543     if (opened)
8544     {
8545         ImGui::SetNextWindowPos(popup_pos, ImGuiSetCond_Always);
8546         ImGuiWindowFlags flags = ImGuiWindowFlags_ShowBorders | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu);
8547         opened = BeginPopupEx(label, flags); // opened can be 'false' when the popup is completely clipped (e.g. zero size display)
8548     }
8549 
8550     return opened;
8551 }
8552 
EndMenu()8553 void ImGui::EndMenu()
8554 {
8555     ImGui::EndPopup();
8556 }
8557 
8558 // A little colored square. Return true when clicked.
8559 // 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)8560 bool ImGui::ColorButton(const ImVec4& col, bool small_height, bool outline_border)
8561 {
8562     ImGuiWindow* window = GetCurrentWindow();
8563     if (window->SkipItems)
8564         return false;
8565 
8566     ImGuiState& g = *GImGui;
8567     const ImGuiStyle& style = g.Style;
8568     const ImGuiID id = window->GetID("#colorbutton");
8569     const float square_size = g.FontSize;
8570     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)));
8571     ItemSize(bb, small_height ? 0.0f : style.FramePadding.y);
8572     if (!ItemAdd(bb, &id))
8573         return false;
8574 
8575     bool hovered, held;
8576     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
8577     RenderFrame(bb.Min, bb.Max, GetColorU32(col), outline_border, style.FrameRounding);
8578 
8579     if (hovered)
8580         ImGui::SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col.x, col.y, col.z, col.w, IM_F32_TO_INT8(col.x), IM_F32_TO_INT8(col.y), IM_F32_TO_INT8(col.z), IM_F32_TO_INT8(col.z));
8581 
8582     return pressed;
8583 }
8584 
ColorEdit3(const char * label,float col[3])8585 bool ImGui::ColorEdit3(const char* label, float col[3])
8586 {
8587     float col4[4];
8588     col4[0] = col[0];
8589     col4[1] = col[1];
8590     col4[2] = col[2];
8591     col4[3] = 1.0f;
8592     const bool value_changed = ImGui::ColorEdit4(label, col4, false);
8593     col[0] = col4[0];
8594     col[1] = col4[1];
8595     col[2] = col4[2];
8596     return value_changed;
8597 }
8598 
8599 // Edit colors components (each component in 0.0f..1.0f range
8600 // Use CTRL-Click to input value and TAB to go to next item.
ColorEdit4(const char * label,float col[4],bool alpha)8601 bool ImGui::ColorEdit4(const char* label, float col[4], bool alpha)
8602 {
8603     ImGuiWindow* window = GetCurrentWindow();
8604     if (window->SkipItems)
8605         return false;
8606 
8607     ImGuiState& g = *GImGui;
8608     const ImGuiStyle& style = g.Style;
8609     const ImGuiID id = window->GetID(label);
8610     const float w_full = CalcItemWidth();
8611     const float square_sz = (g.FontSize + style.FramePadding.y * 2.0f);
8612 
8613     ImGuiColorEditMode edit_mode = window->DC.ColorEditMode;
8614     if (edit_mode == ImGuiColorEditMode_UserSelect || edit_mode == ImGuiColorEditMode_UserSelectShowButton)
8615         edit_mode = g.ColorEditModeStorage.GetInt(id, 0) % 3;
8616 
8617     float f[4] = { col[0], col[1], col[2], col[3] };
8618     if (edit_mode == ImGuiColorEditMode_HSV)
8619         ImGui::ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
8620 
8621     int i[4] = { IM_F32_TO_INT8(f[0]), IM_F32_TO_INT8(f[1]), IM_F32_TO_INT8(f[2]), IM_F32_TO_INT8(f[3]) };
8622 
8623     int components = alpha ? 4 : 3;
8624     bool value_changed = false;
8625 
8626     ImGui::BeginGroup();
8627     ImGui::PushID(label);
8628 
8629     const bool hsv = (edit_mode == 1);
8630     switch (edit_mode)
8631     {
8632     case ImGuiColorEditMode_RGB:
8633     case ImGuiColorEditMode_HSV:
8634         {
8635             // RGB/HSV 0..255 Sliders
8636             const float w_items_all = w_full - (square_sz + style.ItemInnerSpacing.x);
8637             const float w_item_one  = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
8638             const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
8639 
8640             const bool hide_prefix = (w_item_one <= CalcTextSize("M:999").x);
8641             const char* ids[4] = { "##X", "##Y", "##Z", "##W" };
8642             const char* fmt_table[3][4] =
8643             {
8644                 {   "%3.0f",   "%3.0f",   "%3.0f",   "%3.0f" },
8645                 { "R:%3.0f", "G:%3.0f", "B:%3.0f", "A:%3.0f" },
8646                 { "H:%3.0f", "S:%3.0f", "V:%3.0f", "A:%3.0f" }
8647             };
8648             const char** fmt = hide_prefix ? fmt_table[0] : hsv ? fmt_table[2] : fmt_table[1];
8649 
8650             ImGui::PushItemWidth(w_item_one);
8651             for (int n = 0; n < components; n++)
8652             {
8653                 if (n > 0)
8654                     ImGui::SameLine(0, style.ItemInnerSpacing.x);
8655                 if (n + 1 == components)
8656                     ImGui::PushItemWidth(w_item_last);
8657                 value_changed |= ImGui::DragInt(ids[n], &i[n], 1.0f, 0, 255, fmt[n]);
8658             }
8659             ImGui::PopItemWidth();
8660             ImGui::PopItemWidth();
8661         }
8662         break;
8663     case ImGuiColorEditMode_HEX:
8664         {
8665             // RGB Hexadecimal Input
8666             const float w_slider_all = w_full - square_sz;
8667             char buf[64];
8668             if (alpha)
8669                 ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", i[0], i[1], i[2], i[3]);
8670             else
8671                 ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", i[0], i[1], i[2]);
8672             ImGui::PushItemWidth(w_slider_all - style.ItemInnerSpacing.x);
8673             if (ImGui::InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase))
8674             {
8675                 value_changed |= true;
8676                 char* p = buf;
8677                 while (*p == '#' || ImCharIsSpace(*p))
8678                     p++;
8679                 i[0] = i[1] = i[2] = i[3] = 0;
8680                 if (alpha)
8681                     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)
8682                 else
8683                     sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
8684             }
8685             ImGui::PopItemWidth();
8686         }
8687         break;
8688     }
8689 
8690     ImGui::SameLine(0, style.ItemInnerSpacing.x);
8691 
8692     const ImVec4 col_display(col[0], col[1], col[2], 1.0f);
8693     if (ImGui::ColorButton(col_display))
8694         g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away!
8695 
8696     // Recreate our own tooltip over's ColorButton() one because we want to display correct alpha here
8697     if (ImGui::IsItemHovered())
8698         ImGui::SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col[0], col[1], col[2], col[3], IM_F32_TO_INT8(col[0]), IM_F32_TO_INT8(col[1]), IM_F32_TO_INT8(col[2]), IM_F32_TO_INT8(col[3]));
8699 
8700     if (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton)
8701     {
8702         ImGui::SameLine(0, style.ItemInnerSpacing.x);
8703         const char* button_titles[3] = { "RGB", "HSV", "HEX" };
8704         if (ButtonEx(button_titles[edit_mode], ImVec2(0,0), ImGuiButtonFlags_DontClosePopups))
8705             g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away!
8706     }
8707 
8708     const char* label_display_end = FindRenderedTextEnd(label);
8709     if (label != label_display_end)
8710     {
8711         ImGui::SameLine(0, (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton) ? -1.0f : style.ItemInnerSpacing.x);
8712         ImGui::TextUnformatted(label, label_display_end);
8713     }
8714 
8715     // Convert back
8716     for (int n = 0; n < 4; n++)
8717         f[n] = i[n] / 255.0f;
8718     if (edit_mode == 1)
8719         ImGui::ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
8720 
8721     if (value_changed)
8722     {
8723         col[0] = f[0];
8724         col[1] = f[1];
8725         col[2] = f[2];
8726         if (alpha)
8727             col[3] = f[3];
8728     }
8729 
8730     ImGui::PopID();
8731     ImGui::EndGroup();
8732 
8733     return value_changed;
8734 }
8735 
ColorEditMode(ImGuiColorEditMode mode)8736 void ImGui::ColorEditMode(ImGuiColorEditMode mode)
8737 {
8738     ImGuiWindow* window = GetCurrentWindow();
8739     window->DC.ColorEditMode = mode;
8740 }
8741 
8742 // Horizontal separating line.
Separator()8743 void ImGui::Separator()
8744 {
8745     ImGuiWindow* window = GetCurrentWindow();
8746     if (window->SkipItems)
8747         return;
8748 
8749     if (window->DC.ColumnsCount > 1)
8750         PopClipRect();
8751 
8752     float x1 = window->Pos.x;
8753     float x2 = window->Pos.x + window->Size.x;
8754     if (!window->DC.GroupStack.empty())
8755         x1 += window->DC.IndentX;
8756 
8757     const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y));
8758     ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit   // FIXME: Height should be 1.0f not 0.0f ?
8759     if (!ItemAdd(bb, NULL))
8760     {
8761         if (window->DC.ColumnsCount > 1)
8762             PushColumnClipRect();
8763         return;
8764     }
8765 
8766     window->DrawList->AddLine(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border));
8767 
8768     ImGuiState& g = *GImGui;
8769     if (g.LogEnabled)
8770         ImGui::LogText(IM_NEWLINE "--------------------------------");
8771 
8772     if (window->DC.ColumnsCount > 1)
8773     {
8774         PushColumnClipRect();
8775         window->DC.ColumnsCellMinY = window->DC.CursorPos.y;
8776     }
8777 }
8778 
Spacing()8779 void ImGui::Spacing()
8780 {
8781     ImGuiWindow* window = GetCurrentWindow();
8782     if (window->SkipItems)
8783         return;
8784     ItemSize(ImVec2(0,0));
8785 }
8786 
Dummy(const ImVec2 & size)8787 void ImGui::Dummy(const ImVec2& size)
8788 {
8789     ImGuiWindow* window = GetCurrentWindow();
8790     if (window->SkipItems)
8791         return;
8792 
8793     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
8794     ItemSize(bb);
8795     ItemAdd(bb, NULL);
8796 }
8797 
IsRectVisible(const ImVec2 & size)8798 bool ImGui::IsRectVisible(const ImVec2& size)
8799 {
8800     ImGuiWindow* window = GetCurrentWindowRead();
8801     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
8802 }
8803 
BeginGroup()8804 void ImGui::BeginGroup()
8805 {
8806     ImGuiWindow* window = GetCurrentWindow();
8807 
8808     window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
8809     ImGuiGroupData& group_data = window->DC.GroupStack.back();
8810     group_data.BackupCursorPos = window->DC.CursorPos;
8811     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
8812     group_data.BackupIndentX = window->DC.IndentX;
8813     group_data.BackupCurrentLineHeight = window->DC.CurrentLineHeight;
8814     group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
8815     group_data.BackupLogLinePosY = window->DC.LogLinePosY;
8816     group_data.AdvanceCursor = true;
8817 
8818     window->DC.IndentX = window->DC.CursorPos.x - window->Pos.x;
8819     window->DC.CursorMaxPos = window->DC.CursorPos;
8820     window->DC.CurrentLineHeight = 0.0f;
8821     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
8822 }
8823 
EndGroup()8824 void ImGui::EndGroup()
8825 {
8826     ImGuiWindow* window = GetCurrentWindow();
8827     ImGuiStyle& style = ImGui::GetStyle();
8828 
8829     IM_ASSERT(!window->DC.GroupStack.empty());	// Mismatched BeginGroup()/EndGroup() calls
8830 
8831     ImGuiGroupData& group_data = window->DC.GroupStack.back();
8832 
8833     ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
8834     group_bb.Max.y -= style.ItemSpacing.y;      // Cancel out last vertical spacing because we are adding one ourselves.
8835     group_bb.Max = ImMax(group_bb.Min, group_bb.Max);
8836 
8837     window->DC.CursorPos = group_data.BackupCursorPos;
8838     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
8839     window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight;
8840     window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
8841     window->DC.IndentX = group_data.BackupIndentX;
8842     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
8843 
8844     if (group_data.AdvanceCursor)
8845     {
8846         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.
8847         ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset);
8848         ItemAdd(group_bb, NULL);
8849     }
8850 
8851     window->DC.GroupStack.pop_back();
8852 
8853     //window->DrawList->AddRect(group_bb.Min, group_bb.Max, 0xFFFF00FF);   // Debug
8854 }
8855 
8856 // Gets back to previous line and continue with horizontal layout
8857 //      pos_x == 0      : follow on previous item
8858 //      pos_x != 0      : align to specified column
8859 //      spacing_w < 0   : use default spacing if column_x==0, no spacing if column_x!=0
8860 //      spacing_w >= 0  : enforce spacing
SameLine(float pos_x,float spacing_w)8861 void ImGui::SameLine(float pos_x, float spacing_w)
8862 {
8863     ImGuiWindow* window = GetCurrentWindow();
8864     if (window->SkipItems)
8865         return;
8866 
8867     ImGuiState& g = *GImGui;
8868     if (pos_x != 0.0f)
8869     {
8870         if (spacing_w < 0.0f) spacing_w = 0.0f;
8871         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w;
8872         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
8873     }
8874     else
8875     {
8876         if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
8877         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
8878         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
8879     }
8880     window->DC.CurrentLineHeight = window->DC.PrevLineHeight;
8881     window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
8882 }
8883 
NextColumn()8884 void ImGui::NextColumn()
8885 {
8886     ImGuiWindow* window = GetCurrentWindow();
8887     if (window->SkipItems)
8888         return;
8889 
8890     ImGuiState& g = *GImGui;
8891     if (window->DC.ColumnsCount > 1)
8892     {
8893         ImGui::PopItemWidth();
8894         PopClipRect();
8895 
8896         window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
8897         if (++window->DC.ColumnsCurrent < window->DC.ColumnsCount)
8898         {
8899             // Columns 1+ cancel out IndentX
8900             window->DC.ColumnsOffsetX = ImGui::GetColumnOffset(window->DC.ColumnsCurrent) - window->DC.IndentX + g.Style.ItemSpacing.x;
8901             window->DrawList->ChannelsSetCurrent(window->DC.ColumnsCurrent);
8902         }
8903         else
8904         {
8905             window->DC.ColumnsCurrent = 0;
8906             window->DC.ColumnsOffsetX = 0.0f;
8907             window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY;
8908             window->DrawList->ChannelsSetCurrent(0);
8909         }
8910         window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
8911         window->DC.CursorPos.y = window->DC.ColumnsCellMinY;
8912         window->DC.CurrentLineHeight = 0.0f;
8913         window->DC.CurrentLineTextBaseOffset = 0.0f;
8914 
8915         PushColumnClipRect();
8916         ImGui::PushItemWidth(ImGui::GetColumnWidth() * 0.65f);  // FIXME: Move on columns setup
8917     }
8918 }
8919 
GetColumnIndex()8920 int ImGui::GetColumnIndex()
8921 {
8922     ImGuiWindow* window = GetCurrentWindowRead();
8923     return window->DC.ColumnsCurrent;
8924 }
8925 
GetColumnsCount()8926 int ImGui::GetColumnsCount()
8927 {
8928     ImGuiWindow* window = GetCurrentWindowRead();
8929     return window->DC.ColumnsCount;
8930 }
8931 
GetDraggedColumnOffset(int column_index)8932 static float GetDraggedColumnOffset(int column_index)
8933 {
8934     // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
8935     // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
8936     ImGuiState& g = *GImGui;
8937     ImGuiWindow* window = ImGui::GetCurrentWindowRead();
8938     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.
8939     IM_ASSERT(g.ActiveId == window->DC.ColumnsSetID + ImGuiID(column_index));
8940 
8941     float x = g.IO.MousePos.x + g.ActiveClickDeltaToCenter.x - window->Pos.x;
8942     x = ImClamp(x, ImGui::GetColumnOffset(column_index-1)+g.Style.ColumnsMinSpacing, ImGui::GetColumnOffset(column_index+1)-g.Style.ColumnsMinSpacing);
8943 
8944     return (float)(int)x;
8945 }
8946 
GetColumnOffset(int column_index)8947 float ImGui::GetColumnOffset(int column_index)
8948 {
8949     ImGuiState& g = *GImGui;
8950     ImGuiWindow* window = GetCurrentWindowRead();
8951     if (column_index < 0)
8952         column_index = window->DC.ColumnsCurrent;
8953 
8954     if (g.ActiveId)
8955     {
8956         const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(column_index);
8957         if (g.ActiveId == column_id)
8958             return GetDraggedColumnOffset(column_index);
8959     }
8960 
8961     IM_ASSERT(column_index < window->DC.ColumnsData.Size);
8962     const float t = window->DC.ColumnsData[column_index].OffsetNorm;
8963     const float x_offset = window->DC.ColumnsMinX + t * (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
8964     return (float)(int)x_offset;
8965 }
8966 
SetColumnOffset(int column_index,float offset)8967 void ImGui::SetColumnOffset(int column_index, float offset)
8968 {
8969     ImGuiWindow* window = GetCurrentWindow();
8970     if (column_index < 0)
8971         column_index = window->DC.ColumnsCurrent;
8972 
8973     IM_ASSERT(column_index < window->DC.ColumnsData.Size);
8974     const float t = (offset - window->DC.ColumnsMinX) / (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
8975     window->DC.ColumnsData[column_index].OffsetNorm = t;
8976 
8977     const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(column_index);
8978     window->DC.StateStorage->SetFloat(column_id, t);
8979 }
8980 
GetColumnWidth(int column_index)8981 float ImGui::GetColumnWidth(int column_index)
8982 {
8983     ImGuiWindow* window = GetCurrentWindowRead();
8984     if (column_index < 0)
8985         column_index = window->DC.ColumnsCurrent;
8986 
8987     const float w = GetColumnOffset(column_index+1) - GetColumnOffset(column_index);
8988     return w;
8989 }
8990 
PushColumnClipRect(int column_index)8991 static void PushColumnClipRect(int column_index)
8992 {
8993     ImGuiWindow* window = ImGui::GetCurrentWindow();
8994     if (column_index < 0)
8995         column_index = window->DC.ColumnsCurrent;
8996 
8997     const float x1 = window->Pos.x + ImGui::GetColumnOffset(column_index) - 1;
8998     const float x2 = window->Pos.x + ImGui::GetColumnOffset(column_index+1) - 1;
8999     ImGui::PushClipRect(ImVec2(x1,-FLT_MAX), ImVec2(x2,+FLT_MAX), true);
9000 }
9001 
Columns(int columns_count,const char * id,bool border)9002 void ImGui::Columns(int columns_count, const char* id, bool border)
9003 {
9004     ImGuiState& g = *GImGui;
9005     ImGuiWindow* window = GetCurrentWindow();
9006     IM_ASSERT(columns_count >= 1);
9007 
9008     if (window->DC.ColumnsCount != 1)
9009     {
9010         if (window->DC.ColumnsCurrent != 0)
9011             ItemSize(ImVec2(0,0));   // Advance to column 0
9012         ImGui::PopItemWidth();
9013         PopClipRect();
9014         window->DrawList->ChannelsMerge();
9015 
9016         window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
9017         window->DC.CursorPos.y = window->DC.ColumnsCellMaxY;
9018     }
9019 
9020     // Draw columns borders and handle resize at the time of "closing" a columns set
9021     if (window->DC.ColumnsCount != columns_count && window->DC.ColumnsCount != 1 && window->DC.ColumnsShowBorders && !window->SkipItems)
9022     {
9023         const float y1 = window->DC.ColumnsStartPosY;
9024         const float y2 = window->DC.CursorPos.y;
9025         for (int i = 1; i < window->DC.ColumnsCount; i++)
9026         {
9027             float x = window->Pos.x + GetColumnOffset(i);
9028             const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(i);
9029             const ImRect column_rect(ImVec2(x-4,y1),ImVec2(x+4,y2));
9030             if (IsClippedEx(column_rect, &column_id, false))
9031                 continue;
9032 
9033             bool hovered, held;
9034             ButtonBehavior(column_rect, column_id, &hovered, &held, true);
9035             if (hovered || held)
9036                 g.MouseCursor = ImGuiMouseCursor_ResizeEW;
9037 
9038             // Draw before resize so our items positioning are in sync with the line being drawn
9039             const ImU32 col = GetColorU32(held ? ImGuiCol_ColumnActive : hovered ? ImGuiCol_ColumnHovered : ImGuiCol_Column);
9040             const float xi = (float)(int)x;
9041             window->DrawList->AddLine(ImVec2(xi, y1+1.0f), ImVec2(xi, y2), col);
9042 
9043             if (held)
9044             {
9045                 if (g.ActiveIdIsJustActivated)
9046                     g.ActiveClickDeltaToCenter.x = x - g.IO.MousePos.x;
9047 
9048                 x = GetDraggedColumnOffset(i);
9049                 SetColumnOffset(i, x);
9050             }
9051         }
9052     }
9053 
9054     // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
9055     // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
9056     ImGui::PushID(0x11223347 + (id ? 0 : columns_count));
9057     window->DC.ColumnsSetID = window->GetID(id ? id : "columns");
9058     ImGui::PopID();
9059 
9060     // Set state for first column
9061     window->DC.ColumnsCurrent = 0;
9062     window->DC.ColumnsCount = columns_count;
9063     window->DC.ColumnsShowBorders = border;
9064 
9065     const float content_region_width = window->SizeContentsExplicit.x ? window->SizeContentsExplicit.x : window->Size.x;
9066     window->DC.ColumnsMinX = window->DC.IndentX; // Lock our horizontal range
9067     window->DC.ColumnsMaxX = content_region_width - window->Scroll.x - ((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x;
9068     window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
9069     window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.CursorPos.y;
9070     window->DC.ColumnsOffsetX = 0.0f;
9071     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
9072 
9073     if (window->DC.ColumnsCount != 1)
9074     {
9075         // Cache column offsets
9076         window->DC.ColumnsData.resize(columns_count + 1);
9077         for (int column_index = 0; column_index < columns_count + 1; column_index++)
9078         {
9079             const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(column_index);
9080             KeepAliveID(column_id);
9081             const float default_t = column_index / (float)window->DC.ColumnsCount;
9082             const float t = window->DC.StateStorage->GetFloat(column_id, default_t);      // Cheaply store our floating point value inside the integer (could store an union into the map?)
9083             window->DC.ColumnsData[column_index].OffsetNorm = t;
9084         }
9085         window->DrawList->ChannelsSplit(window->DC.ColumnsCount);
9086         PushColumnClipRect();
9087         ImGui::PushItemWidth(ImGui::GetColumnWidth() * 0.65f);
9088     }
9089     else
9090     {
9091         window->DC.ColumnsData.resize(0);
9092     }
9093 }
9094 
Indent()9095 void ImGui::Indent()
9096 {
9097     ImGuiState& g = *GImGui;
9098     ImGuiWindow* window = GetCurrentWindow();
9099     window->DC.IndentX += g.Style.IndentSpacing;
9100     window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
9101 }
9102 
Unindent()9103 void ImGui::Unindent()
9104 {
9105     ImGuiState& g = *GImGui;
9106     ImGuiWindow* window = GetCurrentWindow();
9107     window->DC.IndentX -= g.Style.IndentSpacing;
9108     window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
9109 }
9110 
TreePush(const char * str_id)9111 void ImGui::TreePush(const char* str_id)
9112 {
9113     ImGuiWindow* window = GetCurrentWindow();
9114     ImGui::Indent();
9115     window->DC.TreeDepth++;
9116     PushID(str_id ? str_id : "#TreePush");
9117 }
9118 
TreePush(const void * ptr_id)9119 void ImGui::TreePush(const void* ptr_id)
9120 {
9121     ImGuiWindow* window = GetCurrentWindow();
9122     ImGui::Indent();
9123     window->DC.TreeDepth++;
9124     PushID(ptr_id ? ptr_id : (const void*)"#TreePush");
9125 }
9126 
TreePop()9127 void ImGui::TreePop()
9128 {
9129     ImGuiWindow* window = GetCurrentWindow();
9130     ImGui::Unindent();
9131     window->DC.TreeDepth--;
9132     PopID();
9133 }
9134 
Value(const char * prefix,bool b)9135 void ImGui::Value(const char* prefix, bool b)
9136 {
9137     ImGui::Text("%s: %s", prefix, (b ? "true" : "false"));
9138 }
9139 
Value(const char * prefix,int v)9140 void ImGui::Value(const char* prefix, int v)
9141 {
9142     ImGui::Text("%s: %d", prefix, v);
9143 }
9144 
Value(const char * prefix,unsigned int v)9145 void ImGui::Value(const char* prefix, unsigned int v)
9146 {
9147     ImGui::Text("%s: %d", prefix, v);
9148 }
9149 
Value(const char * prefix,float v,const char * float_format)9150 void ImGui::Value(const char* prefix, float v, const char* float_format)
9151 {
9152     if (float_format)
9153     {
9154         char fmt[64];
9155         ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format);
9156         ImGui::Text(fmt, prefix, v);
9157     }
9158     else
9159     {
9160         ImGui::Text("%s: %.3f", prefix, v);
9161     }
9162 }
9163 
9164 // FIXME: May want to remove those helpers?
ValueColor(const char * prefix,const ImVec4 & v)9165 void ImGui::ValueColor(const char* prefix, const ImVec4& v)
9166 {
9167     ImGui::Text("%s: (%.2f,%.2f,%.2f,%.2f)", prefix, v.x, v.y, v.z, v.w);
9168     ImGui::SameLine();
9169     ImGui::ColorButton(v, true);
9170 }
9171 
ValueColor(const char * prefix,unsigned int v)9172 void ImGui::ValueColor(const char* prefix, unsigned int v)
9173 {
9174     ImGui::Text("%s: %08X", prefix, v);
9175     ImGui::SameLine();
9176 
9177     ImVec4 col;
9178     col.x = (float)((v >> 0) & 0xFF) / 255.0f;
9179     col.y = (float)((v >> 8) & 0xFF) / 255.0f;
9180     col.z = (float)((v >> 16) & 0xFF) / 255.0f;
9181     col.w = (float)((v >> 24) & 0xFF) / 255.0f;
9182     ImGui::ColorButton(col, true);
9183 }
9184 
9185 //-----------------------------------------------------------------------------
9186 // PLATFORM DEPENDANT HELPERS
9187 //-----------------------------------------------------------------------------
9188 
9189 #if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS))
9190 #undef WIN32_LEAN_AND_MEAN
9191 #define WIN32_LEAN_AND_MEAN
9192 #include <windows.h>
9193 #endif
9194 
9195 // Win32 API clipboard implementation
9196 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS)
9197 
9198 #ifdef _MSC_VER
9199 #pragma comment(lib, "user32")
9200 #endif
9201 
GetClipboardTextFn_DefaultImpl()9202 static const char* GetClipboardTextFn_DefaultImpl()
9203 {
9204     static char* buf_local = NULL;
9205     if (buf_local)
9206     {
9207         ImGui::MemFree(buf_local);
9208         buf_local = NULL;
9209     }
9210     if (!OpenClipboard(NULL))
9211         return NULL;
9212     HANDLE wbuf_handle = GetClipboardData(CF_UNICODETEXT);
9213     if (wbuf_handle == NULL)
9214         return NULL;
9215     if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle))
9216     {
9217         int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
9218         buf_local = (char*)ImGui::MemAlloc(buf_len * sizeof(char));
9219         ImTextStrToUtf8(buf_local, buf_len, wbuf_global, NULL);
9220     }
9221     GlobalUnlock(wbuf_handle);
9222     CloseClipboard();
9223     return buf_local;
9224 }
9225 
SetClipboardTextFn_DefaultImpl(const char * text)9226 static void SetClipboardTextFn_DefaultImpl(const char* text)
9227 {
9228     if (!OpenClipboard(NULL))
9229         return;
9230 
9231     const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
9232     HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
9233     if (wbuf_handle == NULL)
9234         return;
9235     ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle);
9236     ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
9237     GlobalUnlock(wbuf_handle);
9238     EmptyClipboard();
9239     SetClipboardData(CF_UNICODETEXT, wbuf_handle);
9240     CloseClipboard();
9241 }
9242 
9243 #else
9244 
9245 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
GetClipboardTextFn_DefaultImpl()9246 static const char* GetClipboardTextFn_DefaultImpl()
9247 {
9248     return GImGui->PrivateClipboard;
9249 }
9250 
9251 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
SetClipboardTextFn_DefaultImpl(const char * text)9252 static void SetClipboardTextFn_DefaultImpl(const char* text)
9253 {
9254     ImGuiState& g = *GImGui;
9255     if (g.PrivateClipboard)
9256     {
9257         ImGui::MemFree(g.PrivateClipboard);
9258         g.PrivateClipboard = NULL;
9259     }
9260     const char* text_end = text + strlen(text);
9261     g.PrivateClipboard = (char*)ImGui::MemAlloc((size_t)(text_end - text) + 1);
9262     memcpy(g.PrivateClipboard, text, (size_t)(text_end - text));
9263     g.PrivateClipboard[(int)(text_end - text)] = 0;
9264 }
9265 
9266 #endif
9267 
9268 // Win32 API IME support (for Asian languages, etc.)
9269 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS)
9270 
9271 #include <imm.h>
9272 #ifdef _MSC_VER
9273 #pragma comment(lib, "imm32")
9274 #endif
9275 
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)9276 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
9277 {
9278     // Notify OS Input Method Editor of text input position
9279     if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle)
9280         if (HIMC himc = ImmGetContext(hwnd))
9281         {
9282             COMPOSITIONFORM cf;
9283             cf.ptCurrentPos.x = x;
9284             cf.ptCurrentPos.y = y;
9285             cf.dwStyle = CFS_FORCE_POSITION;
9286             ImmSetCompositionWindow(himc, &cf);
9287         }
9288 }
9289 
9290 #else
9291 
ImeSetInputScreenPosFn_DefaultImpl(int,int)9292 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
9293 
9294 #endif
9295 
9296 //-----------------------------------------------------------------------------
9297 // HELP
9298 //-----------------------------------------------------------------------------
9299 
ShowMetricsWindow(bool * opened)9300 void ImGui::ShowMetricsWindow(bool* opened)
9301 {
9302     if (ImGui::Begin("ImGui Metrics", opened))
9303     {
9304         ImGui::Text("ImGui %s", ImGui::GetVersion());
9305         ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
9306         ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3);
9307         ImGui::Text("%d allocations", ImGui::GetIO().MetricsAllocs);
9308         static bool show_clip_rects = true;
9309         ImGui::Checkbox("Show clipping rectangles when hovering a ImDrawCmd", &show_clip_rects);
9310         ImGui::Separator();
9311 
9312         struct Funcs
9313         {
9314             static void NodeDrawList(ImDrawList* draw_list, const char* label)
9315             {
9316                 bool node_opened = 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);
9317                 if (draw_list == ImGui::GetWindowDrawList())
9318                 {
9319                     ImGui::SameLine();
9320                     ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
9321                     if (node_opened) ImGui::TreePop();
9322                     return;
9323                 }
9324                 if (!node_opened)
9325                     return;
9326 
9327                 ImDrawList* overlay_draw_list = &GImGui->OverlayDrawList;   // Render additional visuals into the top-most draw list
9328                 overlay_draw_list->PushClipRectFullScreen();
9329                 int elem_offset = 0;
9330                 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
9331                 {
9332                     if (pcmd->UserCallback)
9333                     {
9334                         ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
9335                         continue;
9336                     }
9337                     bool draw_opened = 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);
9338                     if (show_clip_rects && ImGui::IsItemHovered())
9339                     {
9340                         ImRect clip_rect = pcmd->ClipRect;
9341                         ImRect vtxs_rect;
9342                         ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
9343                         for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
9344                             vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
9345                         clip_rect.Round(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, ImColor(255,255,0));
9346                         vtxs_rect.Round(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, ImColor(255,0,255));
9347                     }
9348                     if (!draw_opened)
9349                         continue;
9350                     for (int i = elem_offset; i+2 < elem_offset + (int)pcmd->ElemCount; i += 3)
9351                     {
9352                         ImVec2 triangles_pos[3];
9353                         char buf[300], *buf_p = buf;
9354                         for (int n = 0; n < 3; n++)
9355                         {
9356                             ImDrawVert& v = draw_list->VtxBuffer[(draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data[i+n] : i+n];
9357                             triangles_pos[n] = v.pos;
9358                             buf_p += sprintf(buf_p, "vtx %04d { pos = (%8.2f,%8.2f), uv = (%.6f,%.6f), col = %08X }\n", i+n, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
9359                         }
9360                         ImGui::Selectable(buf, false);
9361                         if (ImGui::IsItemHovered())
9362                             overlay_draw_list->AddPolyline(triangles_pos, 3, ImColor(255,255,0), true, 1.0f, false);  // Add triangle without AA, more readable for large-thin triangle
9363                     }
9364                     ImGui::TreePop();
9365                 }
9366                 overlay_draw_list->PopClipRect();
9367                 ImGui::TreePop();
9368             }
9369 
9370             static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
9371             {
9372                 if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
9373                     return;
9374                 for (int i = 0; i < windows.Size; i++)
9375                     Funcs::NodeWindow(windows[i], "Window");
9376                 ImGui::TreePop();
9377             }
9378 
9379             static void NodeWindow(ImGuiWindow* window, const char* label)
9380             {
9381                 if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
9382                     return;
9383                 NodeDrawList(window->DrawList, "DrawList");
9384                 if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
9385                 if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
9386                 ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
9387                 ImGui::TreePop();
9388             }
9389         };
9390 
9391         ImGuiState& g = *GImGui;                // Access private state
9392         Funcs::NodeWindows(g.Windows, "Windows");
9393         if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.RenderDrawLists[0].Size))
9394         {
9395             for (int i = 0; i < g.RenderDrawLists[0].Size; i++)
9396                 Funcs::NodeDrawList(g.RenderDrawLists[0][i], "DrawList");
9397             ImGui::TreePop();
9398         }
9399         if (ImGui::TreeNode("Popups", "Opened Popups Stack (%d)", g.OpenedPopupStack.Size))
9400         {
9401             for (int i = 0; i < g.OpenedPopupStack.Size; i++)
9402             {
9403                 ImGuiWindow* window = g.OpenedPopupStack[i].Window;
9404                 ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenedPopupStack[i].PopupID, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : "");
9405             }
9406             ImGui::TreePop();
9407         }
9408         if (ImGui::TreeNode("Basic state"))
9409         {
9410             ImGui::Text("FocusedWindow: '%s'", g.FocusedWindow ? g.FocusedWindow->Name : "NULL");
9411             ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
9412             ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
9413             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
9414             ImGui::Text("ActiveID: 0x%08X/0x%08X", g.ActiveId, g.ActiveIdPreviousFrame);
9415             ImGui::TreePop();
9416         }
9417     }
9418     ImGui::End();
9419 }
9420 
9421 //-----------------------------------------------------------------------------
9422 
9423 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
9424 // 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.
9425 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
9426 #include "imgui_user.inl"
9427 #endif
9428 
9429 //-----------------------------------------------------------------------------
9430