1 // dear imgui, v1.61 WIP
2 // (main code and documentation)
3 
4 // Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code.
5 // Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
6 // Get latest version at https://github.com/ocornut/imgui
7 // Releases change-log at https://github.com/ocornut/imgui/releases
8 // Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/1269
9 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
10 // This library is free but I need your support to sustain development and maintenance.
11 // If you work for a company, please consider financial support, see README. For individuals: https://www.patreon.com/imgui
12 
13 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
14 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
15 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
16 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
17 // to a better solution or official support for them.
18 
19 /*
20 
21  Index
22  - MISSION STATEMENT
23  - END-USER GUIDE
24  - PROGRAMMER GUIDE (read me!)
25    - Read first
26    - How to update to a newer version of Dear ImGui
27    - Getting started with integrating Dear ImGui in your code/engine
28    - Using gamepad/keyboard navigation controls [BETA]
29  - API BREAKING CHANGES (read me when you update!)
30  - ISSUES & TODO LIST
31  - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
32    - How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
33    - How can I display an image? What is ImTextureID, how does it works?
34    - How can I have multiple widgets with the same label or without a label? A primer on labels and the ID Stack.
35    - How can I load a different font than the default?
36    - How can I easily use icons in my application?
37    - How can I load multiple fonts?
38    - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
39    - How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
40    - I integrated Dear ImGui in my engine and the text or lines are blurry..
41    - I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
42    - How can I help?
43  - ISSUES & TODO-LIST
44  - CODE
45 
46 
47  MISSION STATEMENT
48  =================
49 
50  - Easy to use to create code-driven and data-driven tools
51  - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools
52  - Easy to hack and improve
53  - Minimize screen real-estate usage
54  - Minimize setup and maintenance
55  - Minimize state storage on user side
56  - Portable, minimize dependencies, run on target (consoles, phones, etc.)
57  - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,
58    opening a tree node for the first time, etc. but a typical frame should not allocate anything)
59 
60  Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
61  - Doesn't look fancy, doesn't animate
62  - Limited layout features, intricate layouts are typically crafted in code
63 
64 
65  END-USER GUIDE
66  ==============
67 
68  - Double-click on title bar to collapse window.
69  - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
70  - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
71  - Click and drag on any empty space to move window.
72  - TAB/SHIFT+TAB to cycle through keyboard editable fields.
73  - CTRL+Click on a slider or drag box to input value as text.
74  - Use mouse wheel to scroll.
75  - Text editor:
76    - Hold SHIFT or use mouse to select text.
77    - CTRL+Left/Right to word jump.
78    - CTRL+Shift+Left/Right to select words.
79    - CTRL+A our Double-Click to select all.
80    - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
81    - CTRL+Z,CTRL+Y to undo/redo.
82    - ESCAPE to revert text to its original value.
83    - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
84    - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
85  - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
86  - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW
87 
88 
89  PROGRAMMER GUIDE
90  ================
91 
92  READ FIRST
93 
94  - Read the FAQ below this section!
95  - Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction
96    or destruction steps, less data retention on your side, less state duplication, less state synchronization, less bugs.
97  - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
98  - You can learn about immediate-mode gui principles at http://www.johno.se/book/imgui.html or watch http://mollyrocket.com/861
99 
100  HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
101 
102  - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
103  - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
104    If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
105    from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
106    likely be a comment about it. Please report any issue to the GitHub page!
107  - Try to keep your copy of dear imgui reasonably up to date.
108 
109  GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
110 
111  - Run and study the examples and demo to get acquainted with the library.
112  - Add the Dear ImGui source files to your projects, using your preferred build system.
113    It is recommended you build the .cpp files as part of your project and not as a library.
114  - You can later customize the imconfig.h file to tweak some compilation time behavior, such as integrating imgui types with your own maths types.
115  - You may be able to grab and copy a ready made imgui_impl_*** file from the examples/ folder.
116  - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
117 
118  - Init: retrieve the ImGuiIO structure with ImGui::GetIO() and fill the fields marked 'Settings': at minimum you need to set io.DisplaySize
119    (application resolution). Later on you will fill your keyboard mapping, clipboard handlers, and other advanced features but for a basic
120    integration you don't need to worry about it all.
121  - Init: call io.Fonts->GetTexDataAsRGBA32(...), it will build the font atlas texture, then load the texture pixels into graphics memory.
122  - Every frame:
123     - In your main loop as early a possible, fill the IO fields marked 'Input' (e.g. mouse position, buttons, keyboard info, etc.)
124     - Call ImGui::NewFrame() to begin the frame
125     - You can use any ImGui function you want between NewFrame() and Render()
126     - Call ImGui::Render() as late as you can to end the frame and finalize render data. it will call your io.RenderDrawListFn handler.
127        (Even if you don't render, call Render() and ignore the callback, or call EndFrame() instead. Otherwhise some features will break)
128  - All rendering information are stored into command-lists until ImGui::Render() is called.
129  - Dear ImGui never touches or knows about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide.
130  - Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases
131    of your own application.
132  - Refer to the examples applications in the examples/ folder for instruction on how to setup your code.
133  - A minimal application skeleton may be:
134 
135      // Application init
136      ImGui::CreateContext();
137      ImGuiIO& io = ImGui::GetIO();
138      io.DisplaySize.x = 1920.0f;
139      io.DisplaySize.y = 1280.0f;
140      // TODO: Fill others settings of the io structure later.
141 
142      // Load texture atlas (there is a default font so you don't need to care about choosing a font yet)
143      unsigned char* pixels;
144      int width, height;
145      io.Fonts->GetTexDataAsRGBA32(pixels, &width, &height);
146      // TODO: At this points you've got the texture data and you need to upload that your your graphic system:
147      MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA)
148      // TODO: Store your texture pointer/identifier (whatever your engine uses) in 'io.Fonts->TexID'. This will be passed back to your via the renderer.
149      io.Fonts->TexID = (void*)texture;
150 
151      // Application main loop
152      while (true)
153      {
154         // Setup low-level inputs (e.g. on Win32, GetKeyboardState(), or write to those fields from your Windows message loop handlers, etc.)
155         ImGuiIO& io = ImGui::GetIO();
156         io.DeltaTime = 1.0f/60.0f;
157         io.MousePos = mouse_pos;
158         io.MouseDown[0] = mouse_button_0;
159         io.MouseDown[1] = mouse_button_1;
160 
161         // Call NewFrame(), after this point you can use ImGui::* functions anytime
162         ImGui::NewFrame();
163 
164         // Most of your application code here
165         MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
166         MyGameRender(); // may use any ImGui functions as well!
167 
168         // Render & swap video buffers
169         ImGui::Render();
170         MyImGuiRenderFunction(ImGui::GetDrawData());
171         SwapBuffers();
172      }
173 
174      // Shutdown
175      ImGui::DestroyContext();
176 
177 
178  - A minimal render function skeleton may be:
179 
180     void void MyRenderFunction(ImDrawData* draw_data)
181     {
182        // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
183        // TODO: Setup viewport, orthographic projection matrix
184        // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
185        for (int n = 0; n < draw_data->CmdListsCount; n++)
186        {
187           const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by ImGui
188           const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by ImGui
189           for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
190           {
191              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
192              if (pcmd->UserCallback)
193              {
194                  pcmd->UserCallback(cmd_list, pcmd);
195              }
196              else
197              {
198                  // The texture for the draw call is specified by pcmd->TextureId.
199                  // The vast majority of draw calls with use the imgui texture atlas, which value you have set yourself during initialization.
200                  MyEngineBindTexture(pcmd->TextureId);
201 
202                  // We are using scissoring to clip some objects. All low-level graphics API supports it.
203                  // If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
204                  // (some elements visible outside their bounds) but you can fix that once everywhere else works!
205                  MyEngineScissor((int)pcmd->ClipRect.x, (int)pcmd->ClipRect.y, (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y));
206 
207                  // Render 'pcmd->ElemCount/3' indexed triangles.
208                  // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits if your engine doesn't support 16-bits indices.
209                  MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
210              }
211              idx_buffer += pcmd->ElemCount;
212           }
213        }
214     }
215 
216  - The examples/ folders contains many functional implementation of the pseudo-code above.
217  - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
218    They tell you if ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs from the rest of your application.
219    However, in both cases you need to pass on the inputs to imgui. Read the FAQ below for more information about those flags.
220  - Please read the FAQ above. Amusingly, it is called a FAQ because people frequently have the same issues!
221 
222  USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS [BETA]
223 
224  - The gamepad/keyboard navigation is in Beta. Ask questions and report issues at https://github.com/ocornut/imgui/issues/787
225  - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
226  - Gamepad:
227     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
228     - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
229       Note that io.NavInputs[] is cleared by EndFrame().
230     - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
231          0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
232     - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
233       Your code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.).
234     - You can download PNG/PSD files depicting the gamepad controls for common controllers at: goo.gl/9LgVZW.
235     - If you need to share inputs between your game and the imgui parts, the easiest approach is to go all-or-nothing, with a buttons combo
236       to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
237  - Keyboard:
238     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
239       NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays.
240     - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
241       will be set. For more advanced uses, you may want to read from:
242        - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
243        - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
244        - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
245       Please reach out if you think the game vs navigation input sharing could be improved.
246  - Mouse:
247     - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
248     - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + uSynergy.c (on your console/tablet/phone app) to share your PC mouse/keyboard.
249     - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavEnableSetMousePos flag.
250       Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
251       When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved.
252       When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that.
253       (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse as moving back and forth!)
254       (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want
255        to set a boolean to ignore your other external mouse positions until the external source is moved again.)
256 
257 
258  API BREAKING CHANGES
259  ====================
260 
261  Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix.
262  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.
263  Also read releases logs https://github.com/ocornut/imgui/releases for more details.
264 
265  - 2018/04/09 (1.61) - IM_DELETE() helper function added in 1.60 doesn't clear the input _pointer_ reference, more consistent with expectation and allows passing r-value.
266  - 2018/03/20 (1.60) - Renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some binding ahead of merging the Nav branch).
267  - 2018/03/12 (1.60) - Removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
268  - 2018/03/08 (1.60) - Changed ImFont::DisplayOffset.y to default to 0 instead of +1. Fixed rounding of Ascent/Descent to match TrueType renderer. If you were adding or subtracting to ImFont::DisplayOffset check if your fonts are correctly aligned vertically.
269  - 2018/03/03 (1.60) - Renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
270  - 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is not really usable in many situations at the moment.
271  - 2018/02/16 (1.60) - obsoleted the io.RenderDrawListsFn callback, you can call your graphics engine render function after ImGui::Render(). Use ImGui::GetDrawData() to retrieve the ImDrawData* to display.
272  - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
273                        - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
274                        - removed Shutdown() function, as DestroyContext() serve this purpose.
275                        - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwhise CreateContext() will create its own font atlas instance.
276                        - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
277                        - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
278  - 2018/01/31 (1.60) - moved sample TTF files from extra_fonts/ to misc/fonts/. If you loaded files directly from the imgui repo you may need to update your paths.
279  - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
280  - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
281  - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
282  - 2017/12/29 (1.60) - removed CalcItemRectClosestPoint() which was weird and not really used by anyone except demo code. If you need it it's easy to replicate on your side.
283  - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
284  - 2017/12/21 (1.53) - ImDrawList: renamed style.AntiAliasedShapes to style.AntiAliasedFill for consistency and as a way to explicitly break code that manipulate those flag at runtime. You can now manipulate ImDrawList::Flags
285  - 2017/12/21 (1.53) - ImDrawList: removed 'bool anti_aliased = true' final parameter of ImDrawList::AddPolyline() and ImDrawList::AddConvexPolyFilled(). Prefer manipulating ImDrawList::Flags if you need to toggle them during the frame.
286  - 2017/12/14 (1.53) - using the ImGuiWindowFlags_NoScrollWithMouse flag on a child window forwards the mouse wheel event to the parent window, unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set.
287  - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
288  - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
289                      - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
290  - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
291  - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
292  - 2017/11/27 (1.53) - renamed ImGuiTextBuffer::append() helper to appendf(), appendv() to appendfv(). If you copied the 'Log' demo in your code, it uses appendv() so that needs to be renamed.
293  - 2017/11/18 (1.53) - Style, Begin: removed ImGuiWindowFlags_ShowBorders window flag. Borders are now fully set up in the ImGuiStyle structure (see e.g. style.FrameBorderSize, style.WindowBorderSize). Use ImGui::ShowStyleEditor() to look them up.
294                        Please note that the style system will keep evolving (hopefully stabilizing in Q1 2018), and so custom styles will probably subtly break over time. It is recommended you use the StyleColorsClassic(), StyleColorsDark(), StyleColorsLight() functions.
295  - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
296  - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
297  - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
298  - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
299  - 2017/10/24 (1.52) - renamed IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS to IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS for consistency.
300  - 2017/10/20 (1.52) - changed IsWindowHovered() default parameters behavior to return false if an item is active in another window (e.g. click-dragging item from another window to this window). You can use the newly introduced IsWindowHovered() flags to requests this specific behavior if you need it.
301  - 2017/10/20 (1.52) - marked IsItemHoveredRect()/IsMouseHoveringWindow() as obsolete, in favor of using the newly introduced flags for IsItemHovered() and IsWindowHovered(). See https://github.com/ocornut/imgui/issues/1382 for details.
302                        removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
303  - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
304  - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
305  - 2017/09/25 (1.52) - removed SetNextWindowPosCenter() because SetNextWindowPos() now has the optional pivot information to do the same and more. Kept redirection function (will obsolete).
306  - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your binding if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
307  - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
308                      - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
309                      - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
310  - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
311  - 2017/08/20 (1.51) - added PushStyleColor(ImGuiCol idx, ImU32 col) overload, which _might_ cause an "ambiguous call" compilation error if you are using ImColor() with implicit cast. Cast to ImU32 or ImVec4 explicily to fix.
312  - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame.
313  - 2017/08/15 (1.51) - changed parameter order for BeginPopupContextWindow() from (const char*,int buttons,bool also_over_items) to (const char*,int buttons,bool also_over_items). Note that most calls relied on default parameters completely.
314  - 2017/08/13 (1.51) - renamed ImGuiCol_Columns*** to ImGuiCol_Separator***. Kept redirection enums (will obsolete).
315  - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete).
316  - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
317  - 2017/08/08 (1.51) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to the various Color*() functions. The SetColorEditOptions() allows to initialize default but the user can still change them with right-click context menu.
318                      - changed prototype of 'ColorEdit4(const char* label, float col[4], bool show_alpha = true)' to 'ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)', where passing flags = 0x01 is a safe no-op (hello dodgy backward compatibility!). - check and run the demo window, under "Color/Picker Widgets", to understand the various new options.
319                      - changed prototype of rarely used 'ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)' to 'ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0,0))'
320  - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
321  - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
322  - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
323  - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetId() and use it instead of passing string to BeginChild().
324  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
325  - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc.
326  - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully breakage should be minimal.
327  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
328                        If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you.
329                        However if your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
330                        This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color.
331                            ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
332                            {
333                                float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
334                                return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a);
335                            }
336                        If this is confusing, pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color.
337  - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
338  - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
339  - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen).
340  - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDraw::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer.
341  - 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).
342  - 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)
343  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
344  - 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.
345  - 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.
346  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
347  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
348  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
349                        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.
350                        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!
351  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
352  - 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.
353  - 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
354  - 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.
355                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
356  - 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.
357                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
358                      - 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.
359                      - the signature of the io.RenderDrawListsFn handler has changed!
360                        old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
361                        new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
362                          argument:   'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
363                          ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
364                          ImDrawCmd:  'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
365                      - 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.
366                      - 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!
367                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
368  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
369  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
370  - 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.
371  - 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
372  - 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!
373  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
374  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
375  - 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.
376  - 2015/05/11 (1.40) - changed BeginPopup() API, takes a string identifier instead of a bool. ImGui needs to manage the open/closed state of popups. Call OpenPopup() to actually set the "open" state of a popup. BeginPopup() returns true if the popup is opened.
377  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
378  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
379  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
380  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
381  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
382  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
383  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
384  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
385  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
386  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
387  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
388  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
389  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
390  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
391  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
392  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
393  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
394               (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
395                        font init:  const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..>
396                        became:     unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier;
397                        you now more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
398                        it is now recommended that you sample the font texture with bilinear interpolation.
399               (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
400               (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
401               (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
402  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
403  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
404  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
405  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
406  - 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)
407  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
408  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
409  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
410  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
411  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
412  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
413 
414 
415  ISSUES & TODO-LIST
416  ==================
417  See TODO.txt
418 
419 
420  FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
421  ======================================
422 
423  Q: How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
424  A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure.
425     - When 'io.WantCaptureMouse' is set, imgui wants to use your mouse state, and you may want to discard/hide the inputs from the rest of your application.
426     - When 'io.WantCaptureKeyboard' is set, imgui wants to use your keyboard state, and you may want to discard/hide the inputs from the rest of your application.
427     - When 'io.WantTextInput' is set to may want to notify your OS to popup an on-screen keyboard, if available (e.g. on a mobile phone, or console OS).
428     Note: you should always pass your mouse/keyboard inputs to imgui, even when the io.WantCaptureXXX flag are set false.
429      This is because imgui needs to detect that you clicked in the void to unfocus its windows.
430     Note: The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!).
431      It handle mouse dragging correctly (both dragging that started over your application or over an imgui window) and handle e.g. modal windows blocking inputs.
432      Those flags are updated by ImGui::NewFrame(). Preferably read the flags after calling NewFrame() if you can afford it, but reading them before is also
433      perfectly fine, as the bool toggle fairly rarely. If you have on a touch device, you might find use for an early call to NewFrameUpdateHoveredWindowAndCaptureFlags().
434     Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically
435      have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
436      were targetted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
437 
438  Q: How can I display an image? What is ImTextureID, how does it works?
439  A: ImTextureID is a void* used to pass renderer-agnostic texture references around until it hits your render function.
440     Dear ImGui knows nothing about what those bits represent, it just passes them around. It is up to you to decide what you want the void* to carry!
441     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.
442     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.
443     Refer to examples applications, where each renderer (in a imgui_impl_xxxx.cpp file) is treating ImTextureID as a different thing.
444     (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!)
445     To display a custom image/texture within an ImGui window, you may use ImGui::Image(), ImGui::ImageButton(), ImDrawList::AddImage() functions.
446     Dear ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use.
447     You may call ImGui::ShowMetricsWindow() to explore active draw lists and visualize/understand how the draw data is generated.
448     It is your responsibility to get textures uploaded to your GPU.
449 
450  Q: How can I have multiple widgets with the same label or without a label?
451  A: A primer on labels and the ID Stack...
452 
453    - Elements that are typically not clickable, such as Text() items don't need an ID.
454 
455    - Interactive widgets require state to be carried over multiple frames (most typically Dear ImGui
456      often needs to remember what is the "active" widget). To do so they need a unique ID. Unique ID
457      are typically derived from a string label, an integer index or a pointer.
458 
459        Button("OK");          // Label = "OK",     ID = top of id stack + hash of "OK"
460        Button("Cancel");      // Label = "Cancel", ID = top of id stack + hash of "Cancel"
461 
462    - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having
463      two buttons labeled "OK" in different windows or different tree locations is fine.
464 
465    - If you have a same ID twice in the same location, you'll have a conflict:
466 
467        Button("OK");
468        Button("OK");          // ID collision! Interacting with either button will trigger the first one.
469 
470      Fear not! this is easy to solve and there are many ways to solve it!
471 
472    - Solving ID conflict in a simple/local context:
473      When passing a label you can optionally specify extra ID information within string itself.
474      Use "##" to pass a complement to the ID that won't be visible to the end-user.
475      This helps solving the simple collision cases when you know e.g. at compilation time which items
476      are going to be created:
477 
478        Button("Play");        // Label = "Play",   ID = top of id stack + hash of "Play"
479        Button("Play##foo1");  // Label = "Play",   ID = top of id stack + hash of "Play##foo1" (different from above)
480        Button("Play##foo2");  // Label = "Play",   ID = top of id stack + hash of "Play##foo2" (different from above)
481 
482    - If you want to completely hide the label, but still need an ID:
483 
484        Checkbox("##On", &b);  // Label = "",       ID = top of id stack + hash of "##On" (no label!)
485 
486    - Occasionally/rarely you might want change a label while preserving a constant ID. This allows
487      you to animate labels. For example you may want to include varying information in a window title bar,
488      but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID:
489 
490        Button("Hello###ID";   // Label = "Hello",  ID = top of id stack + hash of "ID"
491        Button("World###ID";   // Label = "World",  ID = top of id stack + hash of "ID" (same as above)
492 
493        sprintf(buf, "My game (%f FPS)###MyGame", fps);
494        Begin(buf);            // Variable label,   ID = hash of "MyGame"
495 
496    - Solving ID conflict in a more general manner:
497      Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts
498      within the same window. This is the most convenient way of distinguishing ID when iterating and
499      creating many UI elements programmatically.
500      You can push a pointer, a string or an integer value into the ID stack.
501      Remember that ID are formed from the concatenation of _everything_ in the ID stack!
502 
503        for (int i = 0; i < 100; i++)
504        {
505          PushID(i);
506          Button("Click");   // Label = "Click",  ID = top of id stack + hash of integer + hash of "Click"
507          PopID();
508        }
509 
510        for (int i = 0; i < 100; i++)
511        {
512          MyObject* obj = Objects[i];
513          PushID(obj);
514          Button("Click");   // Label = "Click",  ID = top of id stack + hash of pointer + hash of "Click"
515          PopID();
516        }
517 
518        for (int i = 0; i < 100; i++)
519        {
520          MyObject* obj = Objects[i];
521          PushID(obj->Name);
522          Button("Click");   // Label = "Click",  ID = top of id stack + hash of string + hash of "Click"
523          PopID();
524        }
525 
526    - More example showing that you can stack multiple prefixes into the ID stack:
527 
528        Button("Click");     // Label = "Click",  ID = top of id stack + hash of "Click"
529        PushID("node");
530        Button("Click");     // Label = "Click",  ID = top of id stack + hash of "node" + hash of "Click"
531          PushID(my_ptr);
532            Button("Click"); // Label = "Click",  ID = top of id stack + hash of "node" + hash of ptr + hash of "Click"
533          PopID();
534        PopID();
535 
536    - Tree nodes implicitly creates a scope for you by calling PushID().
537 
538        Button("Click");     // Label = "Click",  ID = top of id stack + hash of "Click"
539        if (TreeNode("node"))
540        {
541          Button("Click");   // Label = "Click",  ID = top of id stack + hash of "node" + hash of "Click"
542          TreePop();
543        }
544 
545    - When working with trees, ID are used to preserve the open/close state of each tree node.
546      Depending on your use cases you may want to use strings, indices or pointers as ID.
547       e.g. when following a single pointer that may change over time, using a static string as ID
548        will preserve your node open/closed state when the targeted object change.
549       e.g. when displaying a list of objects, using indices or pointers as ID will preserve the
550        node open/closed state differently. See what makes more sense in your situation!
551 
552  Q: How can I load a different font than the default?
553  A: Use the font atlas to load the TTF/OTF file you want:
554       ImGuiIO& io = ImGui::GetIO();
555       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
556       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
557     (default is ProggyClean.ttf, rendered at size 13, embedded in dear imgui's source code)
558 
559     New programmers: remember that in C/C++ and most programming languages if you want to use a
560     backslash \ within a string literal, you need to write it double backslash "\\":
561       io.Fonts->AddFontFromFileTTF("MyDataFolder\MyFontFile.ttf", size_in_pixels);   // WRONG (you are escape the M here!)
562       io.Fonts->AddFontFromFileTTF("MyDataFolder\\MyFontFile.ttf", size_in_pixels);  // CORRECT
563       io.Fonts->AddFontFromFileTTF("MyDataFolder/MyFontFile.ttf", size_in_pixels);   // ALSO CORRECT
564 
565  Q: How can I easily use icons in my application?
566  A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you
567     main font. Then you can refer to icons within your strings. Read 'How can I load multiple fonts?'
568     and the file 'misc/fonts/README.txt' for instructions and useful header files.
569 
570  Q: How can I load multiple fonts?
571  A: Use the font atlas to pack them into a single texture:
572     (Read misc/fonts/README.txt and the code in ImFontAtlas for more details.)
573 
574       ImGuiIO& io = ImGui::GetIO();
575       ImFont* font0 = io.Fonts->AddFontDefault();
576       ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
577       ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
578       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
579       // the first loaded font gets used by default
580       // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
581 
582       // Options
583       ImFontConfig config;
584       config.OversampleH = 3;
585       config.OversampleV = 1;
586       config.GlyphOffset.y -= 2.0f;      // Move everything by 2 pixels up
587       config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters
588       io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config);
589 
590       // Combine multiple fonts into one (e.g. for icon fonts)
591       ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
592       ImFontConfig config;
593       config.MergeMode = true;
594       io.Fonts->AddFontDefault();
595       io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font
596       io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs
597 
598  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
599  A: When loading a font, pass custom Unicode ranges to specify the glyphs to load.
600 
601       // Add default Japanese ranges
602       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese());
603 
604       // Or create your own custom ranges (e.g. for a game you can feed your entire game script and only build the characters the game need)
605       ImVector<ImWchar> ranges;
606       ImFontAtlas::GlyphRangesBuilder builder;
607       builder.AddText("Hello world");                        // Add a string (here "Hello world" contains 7 unique characters)
608       builder.AddChar(0x7262);                               // Add a specific character
609       builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges
610       builder.BuildRanges(&ranges);                          // Build the final result (ordered ranges with all the unique characters submitted)
611       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data);
612 
613     All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8
614     by using the u8"hello" syntax. Specifying literal in your source code using a local code page
615     (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work!
616     Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
617 
618     Text input: it is up to your application to pass the right character code by calling
619     io.AddInputCharacter(). The applications in examples/ are doing that. For languages relying
620     on an Input Method Editor (IME), on Windows you can copy the Hwnd of your application in the
621     io.ImeWindowHandle field. The default implementation of io.ImeSetInputScreenPosFn() will set
622     your Microsoft IME position correctly.
623 
624  Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
625  A: - You can create a dummy window. Call SetNextWindowBgAlpha(0.0f), call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flags.
626       Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
627     - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows.
628     - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create your own ImDrawListSharedData.
629 
630  Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
631  A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
632     Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
633 
634  Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
635  A: You are probably mishandling the clipping rectangles in your render function.
636     Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
637 
638  Q: How can I help?
639  A: - If you are experienced with Dear ImGui and C++, look at the github issues, or TODO.txt and see how you want/can help!
640     - Convince your company to fund development time! Individual users: you can also become a Patron (patreon.com/imgui) or donate on PayPal! See README.
641     - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
642       You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1269). Visuals are ideal as they inspire other programmers.
643       But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
644     - If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues (on github or privately).
645 
646  - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window.
647         this is also useful to set yourself in the context of another window (to get/set other settings)
648  - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug".
649  - tip: the ImGuiOnceUponAFrame helper will allow run the block of code only once a frame. You can use it to quickly add custom UI in the middle
650         of a deep nested inner loop in your code.
651  - tip: you can call Render() multiple times (e.g for VR renders).
652  - tip: call and read the ShowDemoWindow() code in imgui_demo.cpp for more example of how to use ImGui!
653 
654 */
655 
656 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
657 #define _CRT_SECURE_NO_WARNINGS
658 #endif
659 
660 #include "imgui.h"
661 #define IMGUI_DEFINE_MATH_OPERATORS
662 #include "imgui_internal.h"
663 
664 #include <ctype.h>      // toupper, isprint
665 #include <stdlib.h>     // NULL, malloc, free, qsort, atoi
666 #include <stdio.h>      // vsnprintf, sscanf, printf
667 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
668 #include <stddef.h>     // intptr_t
669 #else
670 #include <stdint.h>     // intptr_t
671 #endif
672 
673 #define IMGUI_DEBUG_NAV_SCORING     0
674 #define IMGUI_DEBUG_NAV_RECTS       0
675 
676 // Visual Studio warnings
677 #ifdef _MSC_VER
678 #pragma warning (disable: 4127) // condition expression is constant
679 #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
680 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
681 #endif
682 
683 // Clang warnings with -Weverything
684 #if defined(__clang__) && !defined(__INTEL_COMPILER)
685 #pragma clang diagnostic ignored "-Wunknown-pragmas"        // warning : unknown warning group '-Wformat-pedantic *'        // not all warnings are known by all clang versions.. so ignoring warnings triggers new warnings on some configuration. great!
686 #pragma clang diagnostic ignored "-Wold-style-cast"         // warning : use of old-style cast                              // yes, they are more terse.
687 #pragma clang diagnostic ignored "-Wfloat-equal"            // warning : comparing floating point with == or != is unsafe   // storing and comparing against same constants (typically 0.0f) is ok.
688 #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.
689 #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.
690 #pragma clang diagnostic ignored "-Wglobal-constructors"    // warning : declaration requires a global destructor           // similar to above, not sure what the exact difference it.
691 #pragma clang diagnostic ignored "-Wsign-conversion"        // warning : implicit conversion changes signedness             //
692 #pragma clang diagnostic ignored "-Wformat-pedantic"        // warning : format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic.
693 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int'
694 #elif defined(__GNUC__)
695 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
696 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
697 #pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
698 #pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
699 #pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
700 #pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
701 #pragma GCC diagnostic ignored "-Wstrict-overflow"          // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
702 #endif
703 
704 // Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall
705 #ifdef _MSC_VER
706 #define IMGUI_CDECL __cdecl
707 #else
708 #define IMGUI_CDECL
709 #endif
710 
711 //-------------------------------------------------------------------------
712 // Forward Declarations
713 //-------------------------------------------------------------------------
714 
715 static bool             IsKeyPressedMap(ImGuiKey key, bool repeat = true);
716 
717 static ImFont*          GetDefaultFont();
718 static void             SetCurrentWindow(ImGuiWindow* window);
719 static void             SetWindowScrollX(ImGuiWindow* window, float new_scroll_x);
720 static void             SetWindowScrollY(ImGuiWindow* window, float new_scroll_y);
721 static void             SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond);
722 static void             SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond);
723 static void             SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond);
724 static ImGuiWindow*     FindHoveredWindow();
725 static ImGuiWindow*     CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
726 static void             CheckStacksSize(ImGuiWindow* window, bool write);
727 static ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
728 
729 static void             AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
730 static void             AddWindowToDrawData(ImVector<ImDrawList*>* out_list, ImGuiWindow* window);
731 static void             AddWindowToSortedBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
732 
733 static ImGuiWindowSettings* AddWindowSettings(const char* name);
734 
735 static void             LoadIniSettingsFromDisk(const char* ini_filename);
736 static void             LoadIniSettingsFromMemory(const char* buf);
737 static void             SaveIniSettingsToDisk(const char* ini_filename);
738 static void             SaveIniSettingsToMemory(ImVector<char>& out_buf);
739 static void             MarkIniSettingsDirty(ImGuiWindow* window);
740 
741 static ImRect           GetViewportRect();
742 
743 static void             ClosePopupToLevel(int remaining);
744 
745 static bool             InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data);
746 static int              InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
747 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);
748 
749 static inline void      DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size);
750 static inline void      DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size);
751 static void             DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2);
752 static bool             DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format);
753 
754 namespace ImGui
755 {
756 static void             NavUpdate();
757 static void             NavUpdateWindowing();
758 static void             NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id);
759 
760 static void             NewFrameUpdateMovingWindow();
761 static void             NewFrameUpdateMouseInputs();
762 static void             UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
763 static void             FocusFrontMostActiveWindow(ImGuiWindow* ignore_window);
764 }
765 
766 //-----------------------------------------------------------------------------
767 // Platform dependent default implementations
768 //-----------------------------------------------------------------------------
769 
770 static const char*      GetClipboardTextFn_DefaultImpl(void* user_data);
771 static void             SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
772 static void             ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
773 
774 //-----------------------------------------------------------------------------
775 // Context
776 //-----------------------------------------------------------------------------
777 
778 // Current context pointer. Implicitely used by all ImGui functions. Always assumed to be != NULL.
779 // CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
780 // If you use DLL hotreloading you might need to call SetCurrentContext() after reloading code from this file.
781 // ImGui functions are not thread-safe because of this pointer. If you want thread-safety to allow N threads to access N different contexts, you can:
782 // - Change this variable to use thread local storage. You may #define GImGui in imconfig.h for that purpose. Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
783 // - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts)
784 #ifndef GImGui
785 ImGuiContext*   GImGui = NULL;
786 #endif
787 
788 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
789 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
790 // Otherwise, you probably don't want to modify them mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction.
791 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)792 static void*   MallocWrapper(size_t size, void* user_data)    { (void)user_data; return malloc(size); }
FreeWrapper(void * ptr,void * user_data)793 static void    FreeWrapper(void* ptr, void* user_data)        { (void)user_data; free(ptr); }
794 #else
MallocWrapper(size_t size,void * user_data)795 static void*   MallocWrapper(size_t size, void* user_data)    { (void)user_data; (void)size; IM_ASSERT(0); return NULL; }
FreeWrapper(void * ptr,void * user_data)796 static void    FreeWrapper(void* ptr, void* user_data)        { (void)user_data; (void)ptr; IM_ASSERT(0); }
797 #endif
798 
799 static void*  (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
800 static void   (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
801 static void*    GImAllocatorUserData = NULL;
802 static size_t   GImAllocatorActiveAllocationsCount = 0;
803 
804 //-----------------------------------------------------------------------------
805 // User facing structures
806 //-----------------------------------------------------------------------------
807 
ImGuiStyle()808 ImGuiStyle::ImGuiStyle()
809 {
810     Alpha                   = 1.0f;             // Global alpha applies to everything in ImGui
811     WindowPadding           = ImVec2(8,8);      // Padding within a window
812     WindowRounding          = 7.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows
813     WindowBorderSize        = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
814     WindowMinSize           = ImVec2(32,32);    // Minimum window size
815     WindowTitleAlign        = ImVec2(0.0f,0.5f);// Alignment for title bar text
816     ChildRounding           = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
817     ChildBorderSize         = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
818     PopupRounding           = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
819     PopupBorderSize         = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
820     FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
821     FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
822     FrameBorderSize         = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
823     ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
824     ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
825     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!
826     IndentSpacing           = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
827     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns
828     ScrollbarSize           = 16.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
829     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar
830     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar
831     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
832     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
833     DisplayWindowPadding    = ImVec2(22,22);    // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows.
834     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.
835     MouseCursorScale        = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
836     AntiAliasedLines        = true;             // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
837     AntiAliasedFill         = true;             // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
838     CurveTessellationTol    = 1.25f;            // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
839 
840     // Default theme
841     ImGui::StyleColorsDark(this);
842 }
843 
844 // To scale your entire UI (e.g. if you want your app to use High DPI or generally be DPI aware) you may use this helper function. Scaling the fonts is done separately and is up to you.
845 // Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times.
ScaleAllSizes(float scale_factor)846 void ImGuiStyle::ScaleAllSizes(float scale_factor)
847 {
848     WindowPadding = ImFloor(WindowPadding * scale_factor);
849     WindowRounding = ImFloor(WindowRounding * scale_factor);
850     WindowMinSize = ImFloor(WindowMinSize * scale_factor);
851     ChildRounding = ImFloor(ChildRounding * scale_factor);
852     PopupRounding = ImFloor(PopupRounding * scale_factor);
853     FramePadding = ImFloor(FramePadding * scale_factor);
854     FrameRounding = ImFloor(FrameRounding * scale_factor);
855     ItemSpacing = ImFloor(ItemSpacing * scale_factor);
856     ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
857     TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
858     IndentSpacing = ImFloor(IndentSpacing * scale_factor);
859     ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
860     ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
861     ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
862     GrabMinSize = ImFloor(GrabMinSize * scale_factor);
863     GrabRounding = ImFloor(GrabRounding * scale_factor);
864     DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
865     DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
866     MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
867 }
868 
ImGuiIO()869 ImGuiIO::ImGuiIO()
870 {
871     // Most fields are initialized with zero
872     memset(this, 0, sizeof(*this));
873 
874     // Settings
875     ConfigFlags = 0x00;
876     BackendFlags = 0x00;
877     DisplaySize = ImVec2(-1.0f, -1.0f);
878     DeltaTime = 1.0f/60.0f;
879     IniSavingRate = 5.0f;
880     IniFilename = "imgui.ini";
881     LogFilename = "imgui_log.txt";
882     MouseDoubleClickTime = 0.30f;
883     MouseDoubleClickMaxDist = 6.0f;
884     for (int i = 0; i < ImGuiKey_COUNT; i++)
885         KeyMap[i] = -1;
886     KeyRepeatDelay = 0.250f;
887     KeyRepeatRate = 0.050f;
888     UserData = NULL;
889 
890     Fonts = NULL;
891     FontGlobalScale = 1.0f;
892     FontDefault = NULL;
893     FontAllowUserScaling = false;
894     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
895     DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f);
896 
897     // Advanced/subtle behaviors
898 #ifdef __APPLE__
899     OptMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
900 #else
901     OptMacOSXBehaviors = false;
902 #endif
903     OptCursorBlink = true;
904 
905     // Settings (User Functions)
906     GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;   // Platform dependent default implementations
907     SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
908     ClipboardUserData = NULL;
909     ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
910     ImeWindowHandle = NULL;
911 
912 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
913     RenderDrawListsFn = NULL;
914 #endif
915 
916     // Input (NB: we already have memset zero the entire structure)
917     MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
918     MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
919     MouseDragThreshold = 6.0f;
920     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
921     for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i]  = KeysDownDurationPrev[i] = -1.0f;
922     for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
923 }
924 
925 // Pass in translated ASCII characters for text input.
926 // - with glfw you can get those from the callback set in glfwSetCharCallback()
927 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(ImWchar c)928 void ImGuiIO::AddInputCharacter(ImWchar c)
929 {
930     const int n = ImStrlenW(InputCharacters);
931     if (n + 1 < IM_ARRAYSIZE(InputCharacters))
932     {
933         InputCharacters[n] = c;
934         InputCharacters[n+1] = '\0';
935     }
936 }
937 
AddInputCharactersUTF8(const char * utf8_chars)938 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
939 {
940     // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more
941     const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar);
942     ImWchar wchars[wchars_buf_len];
943     ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL);
944     for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++)
945         AddInputCharacter(wchars[i]);
946 }
947 
948 //-----------------------------------------------------------------------------
949 // HELPERS
950 //-----------------------------------------------------------------------------
951 
952 #define IM_F32_TO_INT8_UNBOUND(_VAL)    ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f)))   // Unsaturated, for display purpose
953 #define IM_F32_TO_INT8_SAT(_VAL)        ((int)(ImSaturate(_VAL) * 255.0f + 0.5f))               // Saturated, always output 0..255
954 
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)955 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
956 {
957     ImVec2 ap = p - a;
958     ImVec2 ab_dir = b - a;
959     float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
960     if (dot < 0.0f)
961         return a;
962     float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
963     if (dot > ab_len_sqr)
964         return b;
965     return a + ab_dir * dot / ab_len_sqr;
966 }
967 
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)968 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
969 {
970     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
971     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
972     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
973     return ((b1 == b2) && (b2 == b3));
974 }
975 
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)976 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
977 {
978     ImVec2 v0 = b - a;
979     ImVec2 v1 = c - a;
980     ImVec2 v2 = p - a;
981     const float denom = v0.x * v1.y - v1.x * v0.y;
982     out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
983     out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
984     out_u = 1.0f - out_v - out_w;
985 }
986 
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)987 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
988 {
989     ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
990     ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
991     ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
992     float dist2_ab = ImLengthSqr(p - proj_ab);
993     float dist2_bc = ImLengthSqr(p - proj_bc);
994     float dist2_ca = ImLengthSqr(p - proj_ca);
995     float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
996     if (m == dist2_ab)
997         return proj_ab;
998     if (m == dist2_bc)
999         return proj_bc;
1000     return proj_ca;
1001 }
1002 
ImStricmp(const char * str1,const char * str2)1003 int ImStricmp(const char* str1, const char* str2)
1004 {
1005     int d;
1006     while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1007     return d;
1008 }
1009 
ImStrnicmp(const char * str1,const char * str2,size_t count)1010 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1011 {
1012     int d = 0;
1013     while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1014     return d;
1015 }
1016 
ImStrncpy(char * dst,const char * src,size_t count)1017 void ImStrncpy(char* dst, const char* src, size_t count)
1018 {
1019     if (count < 1) return;
1020     strncpy(dst, src, count-1);
1021     dst[count-1] = 0;
1022 }
1023 
ImStrdup(const char * str)1024 char* ImStrdup(const char *str)
1025 {
1026     size_t len = strlen(str) + 1;
1027     void* buf = ImGui::MemAlloc(len);
1028     return (char*)memcpy(buf, (const void*)str, len);
1029 }
1030 
ImStrchrRange(const char * str,const char * str_end,char c)1031 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1032 {
1033     for ( ; str < str_end; str++)
1034         if (*str == c)
1035             return str;
1036     return NULL;
1037 }
1038 
ImStrlenW(const ImWchar * str)1039 int ImStrlenW(const ImWchar* str)
1040 {
1041     int n = 0;
1042     while (*str++) n++;
1043     return n;
1044 }
1045 
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1046 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1047 {
1048     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1049         buf_mid_line--;
1050     return buf_mid_line;
1051 }
1052 
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1053 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1054 {
1055     if (!needle_end)
1056         needle_end = needle + strlen(needle);
1057 
1058     const char un0 = (char)toupper(*needle);
1059     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1060     {
1061         if (toupper(*haystack) == un0)
1062         {
1063             const char* b = needle + 1;
1064             for (const char* a = haystack + 1; b < needle_end; a++, b++)
1065                 if (toupper(*a) != toupper(*b))
1066                     break;
1067             if (b == needle_end)
1068                 return haystack;
1069         }
1070         haystack++;
1071     }
1072     return NULL;
1073 }
1074 
ImAtoi(const char * src,int * output)1075 static const char* ImAtoi(const char* src, int* output)
1076 {
1077     int negative = 0;
1078     if (*src == '-') { negative = 1; src++; }
1079     if (*src == '+') { src++; }
1080     int v = 0;
1081     while (*src >= '0' && *src <= '9')
1082         v = (v * 10) + (*src++ - '0');
1083     *output = negative ? -v : v;
1084     return src;
1085 }
1086 
1087 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1088 // Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm.
1089 // B) When buf==NULL vsnprintf() will return the output size.
1090 #ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1091 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1092 {
1093     va_list args;
1094     va_start(args, fmt);
1095     int w = vsnprintf(buf, buf_size, fmt, args);
1096     va_end(args);
1097     if (buf == NULL)
1098         return w;
1099     if (w == -1 || w >= (int)buf_size)
1100         w = (int)buf_size - 1;
1101     buf[w] = 0;
1102     return w;
1103 }
1104 
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1105 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1106 {
1107     int w = vsnprintf(buf, buf_size, fmt, args);
1108     if (buf == NULL)
1109         return w;
1110     if (w == -1 || w >= (int)buf_size)
1111         w = (int)buf_size - 1;
1112     buf[w] = 0;
1113     return w;
1114 }
1115 #endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1116 
1117 // Pass data_size==0 for zero-terminated strings
1118 // 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)1119 ImU32 ImHash(const void* data, int data_size, ImU32 seed)
1120 {
1121     static ImU32 crc32_lut[256] = { 0 };
1122     if (!crc32_lut[1])
1123     {
1124         const ImU32 polynomial = 0xEDB88320;
1125         for (ImU32 i = 0; i < 256; i++)
1126         {
1127             ImU32 crc = i;
1128             for (ImU32 j = 0; j < 8; j++)
1129                 crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial);
1130             crc32_lut[i] = crc;
1131         }
1132     }
1133 
1134     seed = ~seed;
1135     ImU32 crc = seed;
1136     const unsigned char* current = (const unsigned char*)data;
1137 
1138     if (data_size > 0)
1139     {
1140         // Known size
1141         while (data_size--)
1142             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++];
1143     }
1144     else
1145     {
1146         // Zero-terminated string
1147         while (unsigned char c = *current++)
1148         {
1149             // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1150             // Because this syntax is rarely used we are optimizing for the common case.
1151             // - If we reach ### in the string we discard the hash so far and reset to the seed.
1152             // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller.
1153             if (c == '#' && current[0] == '#' && current[1] == '#')
1154                 crc = seed;
1155             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1156         }
1157     }
1158     return ~crc;
1159 }
1160 
1161 //-----------------------------------------------------------------------------
1162 // ImText* helpers
1163 //-----------------------------------------------------------------------------
1164 
1165 // Convert UTF-8 to 32-bits character, process single character input.
1166 // Based on stb_from_utf8() from github.com/nothings/stb/
1167 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1168 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1169 {
1170     unsigned int c = (unsigned int)-1;
1171     const unsigned char* str = (const unsigned char*)in_text;
1172     if (!(*str & 0x80))
1173     {
1174         c = (unsigned int)(*str++);
1175         *out_char = c;
1176         return 1;
1177     }
1178     if ((*str & 0xe0) == 0xc0)
1179     {
1180         *out_char = 0xFFFD; // will be invalid but not end of string
1181         if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1182         if (*str < 0xc2) return 2;
1183         c = (unsigned int)((*str++ & 0x1f) << 6);
1184         if ((*str & 0xc0) != 0x80) return 2;
1185         c += (*str++ & 0x3f);
1186         *out_char = c;
1187         return 2;
1188     }
1189     if ((*str & 0xf0) == 0xe0)
1190     {
1191         *out_char = 0xFFFD; // will be invalid but not end of string
1192         if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1193         if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1194         if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1195         c = (unsigned int)((*str++ & 0x0f) << 12);
1196         if ((*str & 0xc0) != 0x80) return 3;
1197         c += (unsigned int)((*str++ & 0x3f) << 6);
1198         if ((*str & 0xc0) != 0x80) return 3;
1199         c += (*str++ & 0x3f);
1200         *out_char = c;
1201         return 3;
1202     }
1203     if ((*str & 0xf8) == 0xf0)
1204     {
1205         *out_char = 0xFFFD; // will be invalid but not end of string
1206         if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1207         if (*str > 0xf4) return 4;
1208         if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1209         if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1210         c = (unsigned int)((*str++ & 0x07) << 18);
1211         if ((*str & 0xc0) != 0x80) return 4;
1212         c += (unsigned int)((*str++ & 0x3f) << 12);
1213         if ((*str & 0xc0) != 0x80) return 4;
1214         c += (unsigned int)((*str++ & 0x3f) << 6);
1215         if ((*str & 0xc0) != 0x80) return 4;
1216         c += (*str++ & 0x3f);
1217         // utf-8 encodings of values used in surrogate pairs are invalid
1218         if ((c & 0xFFFFF800) == 0xD800) return 4;
1219         *out_char = c;
1220         return 4;
1221     }
1222     *out_char = 0;
1223     return 0;
1224 }
1225 
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1226 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1227 {
1228     ImWchar* buf_out = buf;
1229     ImWchar* buf_end = buf + buf_size;
1230     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1231     {
1232         unsigned int c;
1233         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1234         if (c == 0)
1235             break;
1236         if (c < 0x10000)    // FIXME: Losing characters that don't fit in 2 bytes
1237             *buf_out++ = (ImWchar)c;
1238     }
1239     *buf_out = 0;
1240     if (in_text_remaining)
1241         *in_text_remaining = in_text;
1242     return (int)(buf_out - buf);
1243 }
1244 
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1245 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1246 {
1247     int char_count = 0;
1248     while ((!in_text_end || in_text < in_text_end) && *in_text)
1249     {
1250         unsigned int c;
1251         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1252         if (c == 0)
1253             break;
1254         if (c < 0x10000)
1255             char_count++;
1256     }
1257     return char_count;
1258 }
1259 
1260 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1261 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1262 {
1263     if (c < 0x80)
1264     {
1265         buf[0] = (char)c;
1266         return 1;
1267     }
1268     if (c < 0x800)
1269     {
1270         if (buf_size < 2) return 0;
1271         buf[0] = (char)(0xc0 + (c >> 6));
1272         buf[1] = (char)(0x80 + (c & 0x3f));
1273         return 2;
1274     }
1275     if (c >= 0xdc00 && c < 0xe000)
1276     {
1277         return 0;
1278     }
1279     if (c >= 0xd800 && c < 0xdc00)
1280     {
1281         if (buf_size < 4) return 0;
1282         buf[0] = (char)(0xf0 + (c >> 18));
1283         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1284         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1285         buf[3] = (char)(0x80 + ((c ) & 0x3f));
1286         return 4;
1287     }
1288     //else if (c < 0x10000)
1289     {
1290         if (buf_size < 3) return 0;
1291         buf[0] = (char)(0xe0 + (c >> 12));
1292         buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1293         buf[2] = (char)(0x80 + ((c ) & 0x3f));
1294         return 3;
1295     }
1296 }
1297 
ImTextCountUtf8BytesFromChar(unsigned int c)1298 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1299 {
1300     if (c < 0x80) return 1;
1301     if (c < 0x800) return 2;
1302     if (c >= 0xdc00 && c < 0xe000) return 0;
1303     if (c >= 0xd800 && c < 0xdc00) return 4;
1304     return 3;
1305 }
1306 
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1307 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1308 {
1309     char* buf_out = buf;
1310     const char* buf_end = buf + buf_size;
1311     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1312     {
1313         unsigned int c = (unsigned int)(*in_text++);
1314         if (c < 0x80)
1315             *buf_out++ = (char)c;
1316         else
1317             buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1318     }
1319     *buf_out = 0;
1320     return (int)(buf_out - buf);
1321 }
1322 
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1323 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1324 {
1325     int bytes_count = 0;
1326     while ((!in_text_end || in_text < in_text_end) && *in_text)
1327     {
1328         unsigned int c = (unsigned int)(*in_text++);
1329         if (c < 0x80)
1330             bytes_count++;
1331         else
1332             bytes_count += ImTextCountUtf8BytesFromChar(c);
1333     }
1334     return bytes_count;
1335 }
1336 
ColorConvertU32ToFloat4(ImU32 in)1337 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1338 {
1339     float s = 1.0f/255.0f;
1340     return ImVec4(
1341         ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1342         ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1343         ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1344         ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1345 }
1346 
ColorConvertFloat4ToU32(const ImVec4 & in)1347 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1348 {
1349     ImU32 out;
1350     out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1351     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1352     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1353     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1354     return out;
1355 }
1356 
GetColorU32(ImGuiCol idx,float alpha_mul)1357 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1358 {
1359     ImGuiStyle& style = GImGui->Style;
1360     ImVec4 c = style.Colors[idx];
1361     c.w *= style.Alpha * alpha_mul;
1362     return ColorConvertFloat4ToU32(c);
1363 }
1364 
GetColorU32(const ImVec4 & col)1365 ImU32 ImGui::GetColorU32(const ImVec4& col)
1366 {
1367     ImGuiStyle& style = GImGui->Style;
1368     ImVec4 c = col;
1369     c.w *= style.Alpha;
1370     return ColorConvertFloat4ToU32(c);
1371 }
1372 
GetStyleColorVec4(ImGuiCol idx)1373 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
1374 {
1375     ImGuiStyle& style = GImGui->Style;
1376     return style.Colors[idx];
1377 }
1378 
GetColorU32(ImU32 col)1379 ImU32 ImGui::GetColorU32(ImU32 col)
1380 {
1381     float style_alpha = GImGui->Style.Alpha;
1382     if (style_alpha >= 1.0f)
1383         return col;
1384     ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
1385     a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
1386     return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
1387 }
1388 
1389 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1390 // 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)1391 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1392 {
1393     float K = 0.f;
1394     if (g < b)
1395     {
1396         ImSwap(g, b);
1397         K = -1.f;
1398     }
1399     if (r < g)
1400     {
1401         ImSwap(r, g);
1402         K = -2.f / 6.f - K;
1403     }
1404 
1405     const float chroma = r - (g < b ? g : b);
1406     out_h = fabsf(K + (g - b) / (6.f * chroma + 1e-20f));
1407     out_s = chroma / (r + 1e-20f);
1408     out_v = r;
1409 }
1410 
1411 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1412 // 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)1413 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1414 {
1415     if (s == 0.0f)
1416     {
1417         // gray
1418         out_r = out_g = out_b = v;
1419         return;
1420     }
1421 
1422     h = fmodf(h, 1.0f) / (60.0f/360.0f);
1423     int   i = (int)h;
1424     float f = h - (float)i;
1425     float p = v * (1.0f - s);
1426     float q = v * (1.0f - s * f);
1427     float t = v * (1.0f - s * (1.0f - f));
1428 
1429     switch (i)
1430     {
1431     case 0: out_r = v; out_g = t; out_b = p; break;
1432     case 1: out_r = q; out_g = v; out_b = p; break;
1433     case 2: out_r = p; out_g = v; out_b = t; break;
1434     case 3: out_r = p; out_g = q; out_b = v; break;
1435     case 4: out_r = t; out_g = p; out_b = v; break;
1436     case 5: default: out_r = v; out_g = p; out_b = q; break;
1437     }
1438 }
1439 
ImFileOpen(const char * filename,const char * mode)1440 FILE* ImFileOpen(const char* filename, const char* mode)
1441 {
1442 #if defined(_WIN32) && !defined(__CYGWIN__)
1443     // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can)
1444     const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1445     const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1446     ImVector<ImWchar> buf;
1447     buf.resize(filename_wsize + mode_wsize);
1448     ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1449     ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1450     return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1451 #else
1452     return fopen(filename, mode);
1453 #endif
1454 }
1455 
1456 // Load file content into memory
1457 // Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree()
ImFileLoadToMemory(const char * filename,const char * file_open_mode,int * out_file_size,int padding_bytes)1458 void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, int* out_file_size, int padding_bytes)
1459 {
1460     IM_ASSERT(filename && file_open_mode);
1461     if (out_file_size)
1462         *out_file_size = 0;
1463 
1464     FILE* f;
1465     if ((f = ImFileOpen(filename, file_open_mode)) == NULL)
1466         return NULL;
1467 
1468     long file_size_signed;
1469     if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
1470     {
1471         fclose(f);
1472         return NULL;
1473     }
1474 
1475     int file_size = (int)file_size_signed;
1476     void* file_data = ImGui::MemAlloc((size_t)(file_size + padding_bytes));
1477     if (file_data == NULL)
1478     {
1479         fclose(f);
1480         return NULL;
1481     }
1482     if (fread(file_data, 1, (size_t)file_size, f) != (size_t)file_size)
1483     {
1484         fclose(f);
1485         ImGui::MemFree(file_data);
1486         return NULL;
1487     }
1488     if (padding_bytes > 0)
1489         memset((void *)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1490 
1491     fclose(f);
1492     if (out_file_size)
1493         *out_file_size = file_size;
1494 
1495     return file_data;
1496 }
1497 
1498 //-----------------------------------------------------------------------------
1499 // ImGuiStorage
1500 // Helper: Key->value storage
1501 //-----------------------------------------------------------------------------
1502 
1503 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::Pair> & data,ImGuiID key)1504 static ImVector<ImGuiStorage::Pair>::iterator LowerBound(ImVector<ImGuiStorage::Pair>& data, ImGuiID key)
1505 {
1506     ImVector<ImGuiStorage::Pair>::iterator first = data.begin();
1507     ImVector<ImGuiStorage::Pair>::iterator last = data.end();
1508     size_t count = (size_t)(last - first);
1509     while (count > 0)
1510     {
1511         size_t count2 = count >> 1;
1512         ImVector<ImGuiStorage::Pair>::iterator mid = first + count2;
1513         if (mid->key < key)
1514         {
1515             first = ++mid;
1516             count -= count2 + 1;
1517         }
1518         else
1519         {
1520             count = count2;
1521         }
1522     }
1523     return first;
1524 }
1525 
1526 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1527 void ImGuiStorage::BuildSortByKey()
1528 {
1529     struct StaticFunc
1530     {
1531         static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1532         {
1533             // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1534             if (((const Pair*)lhs)->key > ((const Pair*)rhs)->key) return +1;
1535             if (((const Pair*)lhs)->key < ((const Pair*)rhs)->key) return -1;
1536             return 0;
1537         }
1538     };
1539     if (Data.Size > 1)
1540         qsort(Data.Data, (size_t)Data.Size, sizeof(Pair), StaticFunc::PairCompareByID);
1541 }
1542 
GetInt(ImGuiID key,int default_val) const1543 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1544 {
1545     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1546     if (it == Data.end() || it->key != key)
1547         return default_val;
1548     return it->val_i;
1549 }
1550 
GetBool(ImGuiID key,bool default_val) const1551 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1552 {
1553     return GetInt(key, default_val ? 1 : 0) != 0;
1554 }
1555 
GetFloat(ImGuiID key,float default_val) const1556 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1557 {
1558     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1559     if (it == Data.end() || it->key != key)
1560         return default_val;
1561     return it->val_f;
1562 }
1563 
GetVoidPtr(ImGuiID key) const1564 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1565 {
1566     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1567     if (it == Data.end() || it->key != key)
1568         return NULL;
1569     return it->val_p;
1570 }
1571 
1572 // 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)1573 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1574 {
1575     ImVector<Pair>::iterator it = LowerBound(Data, key);
1576     if (it == Data.end() || it->key != key)
1577         it = Data.insert(it, Pair(key, default_val));
1578     return &it->val_i;
1579 }
1580 
GetBoolRef(ImGuiID key,bool default_val)1581 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1582 {
1583     return (bool*)GetIntRef(key, default_val ? 1 : 0);
1584 }
1585 
GetFloatRef(ImGuiID key,float default_val)1586 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1587 {
1588     ImVector<Pair>::iterator it = LowerBound(Data, key);
1589     if (it == Data.end() || it->key != key)
1590         it = Data.insert(it, Pair(key, default_val));
1591     return &it->val_f;
1592 }
1593 
GetVoidPtrRef(ImGuiID key,void * default_val)1594 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1595 {
1596     ImVector<Pair>::iterator it = LowerBound(Data, key);
1597     if (it == Data.end() || it->key != key)
1598         it = Data.insert(it, Pair(key, default_val));
1599     return &it->val_p;
1600 }
1601 
1602 // FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame)
SetInt(ImGuiID key,int val)1603 void ImGuiStorage::SetInt(ImGuiID key, int val)
1604 {
1605     ImVector<Pair>::iterator it = LowerBound(Data, key);
1606     if (it == Data.end() || it->key != key)
1607     {
1608         Data.insert(it, Pair(key, val));
1609         return;
1610     }
1611     it->val_i = val;
1612 }
1613 
SetBool(ImGuiID key,bool val)1614 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1615 {
1616     SetInt(key, val ? 1 : 0);
1617 }
1618 
SetFloat(ImGuiID key,float val)1619 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1620 {
1621     ImVector<Pair>::iterator it = LowerBound(Data, key);
1622     if (it == Data.end() || it->key != key)
1623     {
1624         Data.insert(it, Pair(key, val));
1625         return;
1626     }
1627     it->val_f = val;
1628 }
1629 
SetVoidPtr(ImGuiID key,void * val)1630 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1631 {
1632     ImVector<Pair>::iterator it = LowerBound(Data, key);
1633     if (it == Data.end() || it->key != key)
1634     {
1635         Data.insert(it, Pair(key, val));
1636         return;
1637     }
1638     it->val_p = val;
1639 }
1640 
SetAllInt(int v)1641 void ImGuiStorage::SetAllInt(int v)
1642 {
1643     for (int i = 0; i < Data.Size; i++)
1644         Data[i].val_i = v;
1645 }
1646 
1647 //-----------------------------------------------------------------------------
1648 // ImGuiTextFilter
1649 //-----------------------------------------------------------------------------
1650 
1651 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1652 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1653 {
1654     if (default_filter)
1655     {
1656         ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1657         Build();
1658     }
1659     else
1660     {
1661         InputBuf[0] = 0;
1662         CountGrep = 0;
1663     }
1664 }
1665 
Draw(const char * label,float width)1666 bool ImGuiTextFilter::Draw(const char* label, float width)
1667 {
1668     if (width != 0.0f)
1669         ImGui::PushItemWidth(width);
1670     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1671     if (width != 0.0f)
1672         ImGui::PopItemWidth();
1673     if (value_changed)
1674         Build();
1675     return value_changed;
1676 }
1677 
split(char separator,ImVector<TextRange> & out)1678 void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>& out)
1679 {
1680     out.resize(0);
1681     const char* wb = b;
1682     const char* we = wb;
1683     while (we < e)
1684     {
1685         if (*we == separator)
1686         {
1687             out.push_back(TextRange(wb, we));
1688             wb = we + 1;
1689         }
1690         we++;
1691     }
1692     if (wb != we)
1693         out.push_back(TextRange(wb, we));
1694 }
1695 
Build()1696 void ImGuiTextFilter::Build()
1697 {
1698     Filters.resize(0);
1699     TextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
1700     input_range.split(',', Filters);
1701 
1702     CountGrep = 0;
1703     for (int i = 0; i != Filters.Size; i++)
1704     {
1705         Filters[i].trim_blanks();
1706         if (Filters[i].empty())
1707             continue;
1708         if (Filters[i].front() != '-')
1709             CountGrep += 1;
1710     }
1711 }
1712 
PassFilter(const char * text,const char * text_end) const1713 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
1714 {
1715     if (Filters.empty())
1716         return true;
1717 
1718     if (text == NULL)
1719         text = "";
1720 
1721     for (int i = 0; i != Filters.Size; i++)
1722     {
1723         const TextRange& f = Filters[i];
1724         if (f.empty())
1725             continue;
1726         if (f.front() == '-')
1727         {
1728             // Subtract
1729             if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL)
1730                 return false;
1731         }
1732         else
1733         {
1734             // Grep
1735             if (ImStristr(text, text_end, f.begin(), f.end()) != NULL)
1736                 return true;
1737         }
1738     }
1739 
1740     // Implicit * grep
1741     if (CountGrep == 0)
1742         return true;
1743 
1744     return false;
1745 }
1746 
1747 //-----------------------------------------------------------------------------
1748 // ImGuiTextBuffer
1749 //-----------------------------------------------------------------------------
1750 
1751 // On some platform vsnprintf() takes va_list by reference and modifies it.
1752 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1753 #ifndef va_copy
1754 #define va_copy(dest, src) (dest = src)
1755 #endif
1756 
1757 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)1758 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
1759 {
1760     va_list args_copy;
1761     va_copy(args_copy, args);
1762 
1763     int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
1764     if (len <= 0)
1765         return;
1766 
1767     const int write_off = Buf.Size;
1768     const int needed_sz = write_off + len;
1769     if (write_off + len >= Buf.Capacity)
1770     {
1771         int double_capacity = Buf.Capacity * 2;
1772         Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity);
1773     }
1774 
1775     Buf.resize(needed_sz);
1776     ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
1777 }
1778 
appendf(const char * fmt,...)1779 void ImGuiTextBuffer::appendf(const char* fmt, ...)
1780 {
1781     va_list args;
1782     va_start(args, fmt);
1783     appendfv(fmt, args);
1784     va_end(args);
1785 }
1786 
1787 //-----------------------------------------------------------------------------
1788 // ImGuiSimpleColumns (internal use only)
1789 //-----------------------------------------------------------------------------
1790 
ImGuiMenuColumns()1791 ImGuiMenuColumns::ImGuiMenuColumns()
1792 {
1793     Count = 0;
1794     Spacing = Width = NextWidth = 0.0f;
1795     memset(Pos, 0, sizeof(Pos));
1796     memset(NextWidths, 0, sizeof(NextWidths));
1797 }
1798 
Update(int count,float spacing,bool clear)1799 void ImGuiMenuColumns::Update(int count, float spacing, bool clear)
1800 {
1801     IM_ASSERT(Count <= IM_ARRAYSIZE(Pos));
1802     Count = count;
1803     Width = NextWidth = 0.0f;
1804     Spacing = spacing;
1805     if (clear) memset(NextWidths, 0, sizeof(NextWidths));
1806     for (int i = 0; i < Count; i++)
1807     {
1808         if (i > 0 && NextWidths[i] > 0.0f)
1809             Width += Spacing;
1810         Pos[i] = (float)(int)Width;
1811         Width += NextWidths[i];
1812         NextWidths[i] = 0.0f;
1813     }
1814 }
1815 
DeclColumns(float w0,float w1,float w2)1816 float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double
1817 {
1818     NextWidth = 0.0f;
1819     NextWidths[0] = ImMax(NextWidths[0], w0);
1820     NextWidths[1] = ImMax(NextWidths[1], w1);
1821     NextWidths[2] = ImMax(NextWidths[2], w2);
1822     for (int i = 0; i < 3; i++)
1823         NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f);
1824     return ImMax(Width, NextWidth);
1825 }
1826 
CalcExtraSpace(float avail_w)1827 float ImGuiMenuColumns::CalcExtraSpace(float avail_w)
1828 {
1829     return ImMax(0.0f, avail_w - Width);
1830 }
1831 
1832 //-----------------------------------------------------------------------------
1833 // ImGuiListClipper
1834 //-----------------------------------------------------------------------------
1835 
SetCursorPosYAndSetupDummyPrevLine(float pos_y,float line_height)1836 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
1837 {
1838     // Set cursor position and a few other things so that SetScrollHere() and Columns() can work when seeking cursor.
1839     // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
1840     // The clipper should probably have a 4th step to display the last item in a regular manner.
1841     ImGui::SetCursorPosY(pos_y);
1842     ImGuiWindow* window = ImGui::GetCurrentWindow();
1843     window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height;      // Setting those fields so that SetScrollHere() can properly function after the end of our clipper usage.
1844     window->DC.PrevLineHeight = (line_height - GImGui->Style.ItemSpacing.y);    // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list.
1845     if (window->DC.ColumnsSet)
1846         window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y;           // Setting this so that cell Y position are set properly
1847 }
1848 
1849 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
1850 // Use case B: Begin() called from constructor with items_height>0
1851 // FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style.
Begin(int count,float items_height)1852 void ImGuiListClipper::Begin(int count, float items_height)
1853 {
1854     StartPosY = ImGui::GetCursorPosY();
1855     ItemsHeight = items_height;
1856     ItemsCount = count;
1857     StepNo = 0;
1858     DisplayEnd = DisplayStart = -1;
1859     if (ItemsHeight > 0.0f)
1860     {
1861         ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
1862         if (DisplayStart > 0)
1863             SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
1864         StepNo = 2;
1865     }
1866 }
1867 
End()1868 void ImGuiListClipper::End()
1869 {
1870     if (ItemsCount < 0)
1871         return;
1872     // In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user.
1873     if (ItemsCount < INT_MAX)
1874         SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
1875     ItemsCount = -1;
1876     StepNo = 3;
1877 }
1878 
Step()1879 bool ImGuiListClipper::Step()
1880 {
1881     if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems)
1882     {
1883         ItemsCount = -1;
1884         return false;
1885     }
1886     if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height.
1887     {
1888         DisplayStart = 0;
1889         DisplayEnd = 1;
1890         StartPosY = ImGui::GetCursorPosY();
1891         StepNo = 1;
1892         return true;
1893     }
1894     if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element.
1895     {
1896         if (ItemsCount == 1) { ItemsCount = -1; return false; }
1897         float items_height = ImGui::GetCursorPosY() - StartPosY;
1898         IM_ASSERT(items_height > 0.0f);   // If this triggers, it means Item 0 hasn't moved the cursor vertically
1899         Begin(ItemsCount-1, items_height);
1900         DisplayStart++;
1901         DisplayEnd++;
1902         StepNo = 3;
1903         return true;
1904     }
1905     if (StepNo == 2) // Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3.
1906     {
1907         IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
1908         StepNo = 3;
1909         return true;
1910     }
1911     if (StepNo == 3) // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop.
1912         End();
1913     return false;
1914 }
1915 
1916 //-----------------------------------------------------------------------------
1917 // ImGuiWindow
1918 //-----------------------------------------------------------------------------
1919 
ImGuiWindow(ImGuiContext * context,const char * name)1920 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
1921     : DrawListInst(&context->DrawListSharedData)
1922 {
1923     Name = ImStrdup(name);
1924     ID = ImHash(name, 0);
1925     IDStack.push_back(ID);
1926     Flags = 0;
1927     PosFloat = Pos = ImVec2(0.0f, 0.0f);
1928     Size = SizeFull = ImVec2(0.0f, 0.0f);
1929     SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
1930     WindowPadding = ImVec2(0.0f, 0.0f);
1931     WindowRounding = 0.0f;
1932     WindowBorderSize = 0.0f;
1933     MoveId = GetID("#MOVE");
1934     ChildId = 0;
1935     Scroll = ImVec2(0.0f, 0.0f);
1936     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
1937     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
1938     ScrollbarX = ScrollbarY = false;
1939     ScrollbarSizes = ImVec2(0.0f, 0.0f);
1940     Active = WasActive = false;
1941     WriteAccessed = false;
1942     Collapsed = false;
1943     CollapseToggleWanted = false;
1944     SkipItems = false;
1945     Appearing = false;
1946     CloseButton = false;
1947     BeginOrderWithinParent = -1;
1948     BeginOrderWithinContext = -1;
1949     BeginCount = 0;
1950     PopupId = 0;
1951     AutoFitFramesX = AutoFitFramesY = -1;
1952     AutoFitOnlyGrows = false;
1953     AutoFitChildAxises = 0x00;
1954     AutoPosLastDirection = ImGuiDir_None;
1955     HiddenFrames = 0;
1956     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
1957     SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
1958 
1959     LastFrameActive = -1;
1960     ItemWidthDefault = 0.0f;
1961     FontWindowScale = 1.0f;
1962 
1963     DrawList = &DrawListInst;
1964     DrawList->_OwnerName = Name;
1965     ParentWindow = NULL;
1966     RootWindow = NULL;
1967     RootWindowForTitleBarHighlight = NULL;
1968     RootWindowForTabbing = NULL;
1969     RootWindowForNav = NULL;
1970 
1971     NavLastIds[0] = NavLastIds[1] = 0;
1972     NavRectRel[0] = NavRectRel[1] = ImRect();
1973     NavLastChildNavWindow = NULL;
1974 
1975     FocusIdxAllCounter = FocusIdxTabCounter = -1;
1976     FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX;
1977     FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX;
1978 }
1979 
~ImGuiWindow()1980 ImGuiWindow::~ImGuiWindow()
1981 {
1982     IM_ASSERT(DrawList == &DrawListInst);
1983     IM_DELETE(Name);
1984     for (int i = 0; i != ColumnsStorage.Size; i++)
1985         ColumnsStorage[i].~ImGuiColumnsSet();
1986 }
1987 
GetID(const char * str,const char * str_end)1988 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
1989 {
1990     ImGuiID seed = IDStack.back();
1991     ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
1992     ImGui::KeepAliveID(id);
1993     return id;
1994 }
1995 
GetID(const void * ptr)1996 ImGuiID ImGuiWindow::GetID(const void* ptr)
1997 {
1998     ImGuiID seed = IDStack.back();
1999     ImGuiID id = ImHash(&ptr, sizeof(void*), seed);
2000     ImGui::KeepAliveID(id);
2001     return id;
2002 }
2003 
GetIDNoKeepAlive(const char * str,const char * str_end)2004 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2005 {
2006     ImGuiID seed = IDStack.back();
2007     return ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
2008 }
2009 
2010 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2011 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2012 {
2013     ImGuiID seed = IDStack.back();
2014     const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) };
2015     ImGuiID id = ImHash(&r_rel, sizeof(r_rel), seed);
2016     ImGui::KeepAliveID(id);
2017     return id;
2018 }
2019 
2020 //-----------------------------------------------------------------------------
2021 // Internal API exposed in imgui_internal.h
2022 //-----------------------------------------------------------------------------
2023 
SetCurrentWindow(ImGuiWindow * window)2024 static void SetCurrentWindow(ImGuiWindow* window)
2025 {
2026     ImGuiContext& g = *GImGui;
2027     g.CurrentWindow = window;
2028     if (window)
2029         g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2030 }
2031 
SetNavID(ImGuiID id,int nav_layer)2032 static void SetNavID(ImGuiID id, int nav_layer)
2033 {
2034     ImGuiContext& g = *GImGui;
2035     IM_ASSERT(g.NavWindow);
2036     IM_ASSERT(nav_layer == 0 || nav_layer == 1);
2037     g.NavId = id;
2038     g.NavWindow->NavLastIds[nav_layer] = id;
2039 }
2040 
SetNavIDWithRectRel(ImGuiID id,int nav_layer,const ImRect & rect_rel)2041 static void SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel)
2042 {
2043     ImGuiContext& g = *GImGui;
2044     SetNavID(id, nav_layer);
2045     g.NavWindow->NavRectRel[nav_layer] = rect_rel;
2046     g.NavMousePosDirty = true;
2047     g.NavDisableHighlight = false;
2048     g.NavDisableMouseHover = true;
2049 }
2050 
SetActiveID(ImGuiID id,ImGuiWindow * window)2051 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2052 {
2053     ImGuiContext& g = *GImGui;
2054     g.ActiveIdIsJustActivated = (g.ActiveId != id);
2055     if (g.ActiveIdIsJustActivated)
2056         g.ActiveIdTimer = 0.0f;
2057     g.ActiveId = id;
2058     g.ActiveIdAllowNavDirFlags = 0;
2059     g.ActiveIdAllowOverlap = false;
2060     g.ActiveIdWindow = window;
2061     if (id)
2062     {
2063         g.ActiveIdIsAlive = true;
2064         g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
2065     }
2066 }
2067 
GetActiveID()2068 ImGuiID ImGui::GetActiveID()
2069 {
2070     ImGuiContext& g = *GImGui;
2071     return g.ActiveId;
2072 }
2073 
SetFocusID(ImGuiID id,ImGuiWindow * window)2074 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
2075 {
2076     ImGuiContext& g = *GImGui;
2077     IM_ASSERT(id != 0);
2078 
2079     // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it.
2080     const int nav_layer = window->DC.NavLayerCurrent;
2081     if (g.NavWindow != window)
2082         g.NavInitRequest = false;
2083     g.NavId = id;
2084     g.NavWindow = window;
2085     g.NavLayer = nav_layer;
2086     window->NavLastIds[nav_layer] = id;
2087     if (window->DC.LastItemId == id)
2088         window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
2089 
2090     if (g.ActiveIdSource == ImGuiInputSource_Nav)
2091         g.NavDisableMouseHover = true;
2092     else
2093         g.NavDisableHighlight = true;
2094 }
2095 
ClearActiveID()2096 void ImGui::ClearActiveID()
2097 {
2098     SetActiveID(0, NULL);
2099 }
2100 
SetHoveredID(ImGuiID id)2101 void ImGui::SetHoveredID(ImGuiID id)
2102 {
2103     ImGuiContext& g = *GImGui;
2104     g.HoveredId = id;
2105     g.HoveredIdAllowOverlap = false;
2106     g.HoveredIdTimer = (id != 0 && g.HoveredIdPreviousFrame == id) ? (g.HoveredIdTimer + g.IO.DeltaTime) : 0.0f;
2107 }
2108 
GetHoveredID()2109 ImGuiID ImGui::GetHoveredID()
2110 {
2111     ImGuiContext& g = *GImGui;
2112     return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
2113 }
2114 
KeepAliveID(ImGuiID id)2115 void ImGui::KeepAliveID(ImGuiID id)
2116 {
2117     ImGuiContext& g = *GImGui;
2118     if (g.ActiveId == id)
2119         g.ActiveIdIsAlive = true;
2120 }
2121 
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)2122 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
2123 {
2124     // An active popup disable hovering on other windows (apart from its own children)
2125     // FIXME-OPT: This could be cached/stored within the window.
2126     ImGuiContext& g = *GImGui;
2127     if (g.NavWindow)
2128         if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
2129             if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
2130             {
2131                 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
2132                 // NB: The order of those two tests is important because Modal windows are also Popups.
2133                 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
2134                     return false;
2135                 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
2136                     return false;
2137             }
2138 
2139     return true;
2140 }
2141 
2142 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_offset_y)2143 void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
2144 {
2145     ImGuiContext& g = *GImGui;
2146     ImGuiWindow* window = g.CurrentWindow;
2147     if (window->SkipItems)
2148         return;
2149 
2150     // Always align ourselves on pixel boundaries
2151     const float line_height = ImMax(window->DC.CurrentLineHeight, size.y);
2152     const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y);
2153     //if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG]
2154     window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
2155     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));
2156     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
2157     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
2158     //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
2159 
2160     window->DC.PrevLineHeight = line_height;
2161     window->DC.PrevLineTextBaseOffset = text_base_offset;
2162     window->DC.CurrentLineHeight = window->DC.CurrentLineTextBaseOffset = 0.0f;
2163 
2164     // Horizontal layout mode
2165     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
2166         SameLine();
2167 }
2168 
ItemSize(const ImRect & bb,float text_offset_y)2169 void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
2170 {
2171     ItemSize(bb.GetSize(), text_offset_y);
2172 }
2173 
NavScoreItemGetQuadrant(float dx,float dy)2174 static ImGuiDir NavScoreItemGetQuadrant(float dx, float dy)
2175 {
2176     if (fabsf(dx) > fabsf(dy))
2177         return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
2178     return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
2179 }
2180 
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)2181 static float NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
2182 {
2183     if (a1 < b0)
2184         return a1 - b0;
2185     if (b1 < a0)
2186         return a0 - b1;
2187     return 0.0f;
2188 }
2189 
2190 // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)2191 static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
2192 {
2193     ImGuiContext& g = *GImGui;
2194     ImGuiWindow* window = g.CurrentWindow;
2195     if (g.NavLayer != window->DC.NavLayerCurrent)
2196         return false;
2197 
2198     const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
2199     g.NavScoringCount++;
2200 
2201     // We perform scoring on items bounding box clipped by their parent window on the other axis (clipping on our movement axis would give us equal scores for all clipped items)
2202     if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
2203     {
2204         cand.Min.y = ImClamp(cand.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y);
2205         cand.Max.y = ImClamp(cand.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y);
2206     }
2207     else
2208     {
2209         cand.Min.x = ImClamp(cand.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
2210         cand.Max.x = ImClamp(cand.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
2211     }
2212 
2213     // Compute distance between boxes
2214     // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
2215     float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
2216     float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items
2217     if (dby != 0.0f && dbx != 0.0f)
2218        dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
2219     float dist_box = fabsf(dbx) + fabsf(dby);
2220 
2221     // Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter)
2222     float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
2223     float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
2224     float dist_center = fabsf(dcx) + fabsf(dcy); // L1 metric (need this for our connectedness guarantee)
2225 
2226     // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
2227     ImGuiDir quadrant;
2228     float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
2229     if (dbx != 0.0f || dby != 0.0f)
2230     {
2231         // For non-overlapping boxes, use distance between boxes
2232         dax = dbx;
2233         day = dby;
2234         dist_axial = dist_box;
2235         quadrant = NavScoreItemGetQuadrant(dbx, dby);
2236     }
2237     else if (dcx != 0.0f || dcy != 0.0f)
2238     {
2239         // For overlapping boxes with different centers, use distance between centers
2240         dax = dcx;
2241         day = dcy;
2242         dist_axial = dist_center;
2243         quadrant = NavScoreItemGetQuadrant(dcx, dcy);
2244     }
2245     else
2246     {
2247         // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter)
2248         quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
2249     }
2250 
2251 #if IMGUI_DEBUG_NAV_SCORING
2252     char buf[128];
2253     if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max))
2254     {
2255         ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]);
2256         g.OverlayDrawList.AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100));
2257         g.OverlayDrawList.AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
2258         g.OverlayDrawList.AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150));
2259         g.OverlayDrawList.AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
2260     }
2261     else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
2262     {
2263         if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
2264         if (quadrant == g.NavMoveDir)
2265         {
2266             ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
2267             g.OverlayDrawList.AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
2268             g.OverlayDrawList.AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
2269         }
2270     }
2271  #endif
2272 
2273     // Is it in the quadrant we're interesting in moving to?
2274     bool new_best = false;
2275     if (quadrant == g.NavMoveDir)
2276     {
2277         // Does it beat the current best candidate?
2278         if (dist_box < result->DistBox)
2279         {
2280             result->DistBox = dist_box;
2281             result->DistCenter = dist_center;
2282             return true;
2283         }
2284         if (dist_box == result->DistBox)
2285         {
2286             // Try using distance between center points to break ties
2287             if (dist_center < result->DistCenter)
2288             {
2289                 result->DistCenter = dist_center;
2290                 new_best = true;
2291             }
2292             else if (dist_center == result->DistCenter)
2293             {
2294                 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
2295                 // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index),
2296                 // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis.
2297                 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
2298                     new_best = true;
2299             }
2300         }
2301     }
2302 
2303     // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches
2304     // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
2305     // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too.
2306     // 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward.
2307     // Disabling it may however lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
2308     if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match
2309         if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
2310             if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f))
2311             {
2312                 result->DistAxial = dist_axial;
2313                 new_best = true;
2314             }
2315 
2316     return new_best;
2317 }
2318 
NavSaveLastChildNavWindow(ImGuiWindow * child_window)2319 static void NavSaveLastChildNavWindow(ImGuiWindow* child_window)
2320 {
2321     ImGuiWindow* parent_window = child_window;
2322     while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
2323         parent_window = parent_window->ParentWindow;
2324     if (parent_window && parent_window != child_window)
2325         parent_window->NavLastChildNavWindow = child_window;
2326 }
2327 
2328 // Call when we are expected to land on Layer 0 after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)2329 static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window)
2330 {
2331     return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window;
2332 }
2333 
NavRestoreLayer(int layer)2334 static void NavRestoreLayer(int layer)
2335 {
2336     ImGuiContext& g = *GImGui;
2337     g.NavLayer = layer;
2338     if (layer == 0)
2339         g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow);
2340     if (layer == 0 && g.NavWindow->NavLastIds[0] != 0)
2341         SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]);
2342     else
2343         ImGui::NavInitWindow(g.NavWindow, true);
2344 }
2345 
NavUpdateAnyRequestFlag()2346 static inline void NavUpdateAnyRequestFlag()
2347 {
2348     ImGuiContext& g = *GImGui;
2349     g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
2350     if (g.NavAnyRequest)
2351         IM_ASSERT(g.NavWindow != NULL);
2352 }
2353 
NavMoveRequestButNoResultYet()2354 static bool NavMoveRequestButNoResultYet()
2355 {
2356     ImGuiContext& g = *GImGui;
2357     return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
2358 }
2359 
NavMoveRequestCancel()2360 void ImGui::NavMoveRequestCancel()
2361 {
2362     ImGuiContext& g = *GImGui;
2363     g.NavMoveRequest = false;
2364     NavUpdateAnyRequestFlag();
2365 }
2366 
2367 // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
NavProcessItem(ImGuiWindow * window,const ImRect & nav_bb,const ImGuiID id)2368 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
2369 {
2370     ImGuiContext& g = *GImGui;
2371     //if (!g.IO.NavActive)  // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag.
2372     //    return;
2373 
2374     const ImGuiItemFlags item_flags = window->DC.ItemFlags;
2375     const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
2376     if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
2377     {
2378         // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
2379         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
2380         {
2381             g.NavInitResultId = id;
2382             g.NavInitResultRectRel = nav_bb_rel;
2383         }
2384         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
2385         {
2386             g.NavInitRequest = false; // Found a match, clear request
2387             NavUpdateAnyRequestFlag();
2388         }
2389     }
2390 
2391     // Scoring for navigation
2392     if (g.NavId != id && !(item_flags & ImGuiItemFlags_NoNav))
2393     {
2394         ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
2395 #if IMGUI_DEBUG_NAV_SCORING
2396         // [DEBUG] Score all items in NavWindow at all times
2397         if (!g.NavMoveRequest)
2398             g.NavMoveDir = g.NavMoveDirLast;
2399         bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
2400 #else
2401         bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
2402 #endif
2403         if (new_best)
2404         {
2405             result->ID = id;
2406             result->ParentID = window->IDStack.back();
2407             result->Window = window;
2408             result->RectRel = nav_bb_rel;
2409         }
2410     }
2411 
2412     // Update window-relative bounding box of navigated item
2413     if (g.NavId == id)
2414     {
2415         g.NavWindow = window;                                           // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
2416         g.NavLayer = window->DC.NavLayerCurrent;
2417         g.NavIdIsAlive = true;
2418         g.NavIdTabCounter = window->FocusIdxTabCounter;
2419         window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel;    // Store item bounding box (relative to window position)
2420     }
2421 }
2422 
2423 // Declare item bounding box for clipping and interaction.
2424 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
2425 // declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd().
ItemAdd(const ImRect & bb,ImGuiID id,const ImRect * nav_bb_arg)2426 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
2427 {
2428     ImGuiContext& g = *GImGui;
2429     ImGuiWindow* window = g.CurrentWindow;
2430 
2431     if (id != 0)
2432     {
2433         // Navigation processing runs prior to clipping early-out
2434         //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
2435         //  (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests unfortunately, but it is still limited to one window.
2436         //      it may not scale very well for windows with ten of thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
2437         //      We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick)
2438         window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
2439         if (g.NavId == id || g.NavAnyRequest)
2440             if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
2441                 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
2442                     NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
2443     }
2444 
2445     window->DC.LastItemId = id;
2446     window->DC.LastItemRect = bb;
2447     window->DC.LastItemStatusFlags = 0;
2448 
2449     // Clipping test
2450     const bool is_clipped = IsClippedEx(bb, id, false);
2451     if (is_clipped)
2452         return false;
2453     //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2454 
2455     // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2456     if (IsMouseHoveringRect(bb.Min, bb.Max))
2457         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
2458     return true;
2459 }
2460 
2461 // This is roughly matching the behavior of internal-facing ItemHoverable()
2462 // - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered()
2463 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)2464 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2465 {
2466     ImGuiContext& g = *GImGui;
2467     ImGuiWindow* window = g.CurrentWindow;
2468     if (g.NavDisableMouseHover && !g.NavDisableHighlight)
2469         return IsItemFocused();
2470 
2471     // Test for bounding box overlap, as updated as ItemAdd()
2472     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
2473         return false;
2474     IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0);   // Flags not supported by this function
2475 
2476     // Test if we are hovering the right window (our window could be behind another window)
2477     // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable to use IsItemHovered() after EndChild() itself.
2478     // Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while.
2479     //if (g.HoveredWindow != window)
2480     //    return false;
2481     if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2482         return false;
2483 
2484     // Test if another item is active (e.g. being dragged)
2485     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2486         if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2487             return false;
2488 
2489     // Test if interactions on this window are blocked by an active popup or modal
2490     if (!IsWindowContentHoverable(window, flags))
2491         return false;
2492 
2493     // Test if the item is disabled
2494     if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2495         return false;
2496 
2497     // Special handling for the 1st item after Begin() which represent the title bar. When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect tht case.
2498     if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2499         return false;
2500     return true;
2501 }
2502 
2503 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)2504 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2505 {
2506     ImGuiContext& g = *GImGui;
2507     if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
2508         return false;
2509 
2510     ImGuiWindow* window = g.CurrentWindow;
2511     if (g.HoveredWindow != window)
2512         return false;
2513     if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
2514         return false;
2515     if (!IsMouseHoveringRect(bb.Min, bb.Max))
2516         return false;
2517     if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_Default))
2518         return false;
2519     if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2520         return false;
2521 
2522     SetHoveredID(id);
2523     return true;
2524 }
2525 
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)2526 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
2527 {
2528     ImGuiContext& g = *GImGui;
2529     ImGuiWindow* window = g.CurrentWindow;
2530     if (!bb.Overlaps(window->ClipRect))
2531         if (id == 0 || id != g.ActiveId)
2532             if (clip_even_when_logged || !g.LogEnabled)
2533                 return true;
2534     return false;
2535 }
2536 
FocusableItemRegister(ImGuiWindow * window,ImGuiID id,bool tab_stop)2537 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop)
2538 {
2539     ImGuiContext& g = *GImGui;
2540 
2541     const bool allow_keyboard_focus = (window->DC.ItemFlags & (ImGuiItemFlags_AllowKeyboardFocus | ImGuiItemFlags_Disabled)) == ImGuiItemFlags_AllowKeyboardFocus;
2542     window->FocusIdxAllCounter++;
2543     if (allow_keyboard_focus)
2544         window->FocusIdxTabCounter++;
2545 
2546     // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item.
2547     // Note that we can always TAB out of a widget that doesn't allow tabbing in.
2548     if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab))
2549         window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items.
2550 
2551     if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
2552         return true;
2553     if (allow_keyboard_focus && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent)
2554     {
2555         g.NavJustTabbedId = id;
2556         return true;
2557     }
2558 
2559     return false;
2560 }
2561 
FocusableItemUnregister(ImGuiWindow * window)2562 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
2563 {
2564     window->FocusIdxAllCounter--;
2565     window->FocusIdxTabCounter--;
2566 }
2567 
CalcItemSize(ImVec2 size,float default_x,float default_y)2568 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y)
2569 {
2570     ImGuiContext& g = *GImGui;
2571     ImVec2 content_max;
2572     if (size.x < 0.0f || size.y < 0.0f)
2573         content_max = g.CurrentWindow->Pos + GetContentRegionMax();
2574     if (size.x <= 0.0f)
2575         size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x;
2576     if (size.y <= 0.0f)
2577         size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y;
2578     return size;
2579 }
2580 
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)2581 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
2582 {
2583     if (wrap_pos_x < 0.0f)
2584         return 0.0f;
2585 
2586     ImGuiWindow* window = GetCurrentWindowRead();
2587     if (wrap_pos_x == 0.0f)
2588         wrap_pos_x = GetContentRegionMax().x + window->Pos.x;
2589     else if (wrap_pos_x > 0.0f)
2590         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
2591 
2592     return ImMax(wrap_pos_x - pos.x, 1.0f);
2593 }
2594 
2595 //-----------------------------------------------------------------------------
2596 
MemAlloc(size_t sz)2597 void* ImGui::MemAlloc(size_t sz)
2598 {
2599     GImAllocatorActiveAllocationsCount++;
2600     return GImAllocatorAllocFunc(sz, GImAllocatorUserData);
2601 }
2602 
MemFree(void * ptr)2603 void ImGui::MemFree(void* ptr)
2604 {
2605     if (ptr) GImAllocatorActiveAllocationsCount--;
2606     return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
2607 }
2608 
GetClipboardText()2609 const char* ImGui::GetClipboardText()
2610 {
2611     return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : "";
2612 }
2613 
SetClipboardText(const char * text)2614 void ImGui::SetClipboardText(const char* text)
2615 {
2616     if (GImGui->IO.SetClipboardTextFn)
2617         GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text);
2618 }
2619 
GetVersion()2620 const char* ImGui::GetVersion()
2621 {
2622     return IMGUI_VERSION;
2623 }
2624 
2625 // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
2626 // Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module
GetCurrentContext()2627 ImGuiContext* ImGui::GetCurrentContext()
2628 {
2629     return GImGui;
2630 }
2631 
SetCurrentContext(ImGuiContext * ctx)2632 void ImGui::SetCurrentContext(ImGuiContext* ctx)
2633 {
2634 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
2635     IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
2636 #else
2637     GImGui = ctx;
2638 #endif
2639 }
2640 
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)2641 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void(*free_func)(void* ptr, void* user_data), void* user_data)
2642 {
2643     GImAllocatorAllocFunc = alloc_func;
2644     GImAllocatorFreeFunc = free_func;
2645     GImAllocatorUserData = user_data;
2646 }
2647 
CreateContext(ImFontAtlas * shared_font_atlas)2648 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
2649 {
2650     ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
2651     if (GImGui == NULL)
2652         SetCurrentContext(ctx);
2653     Initialize(ctx);
2654     return ctx;
2655 }
2656 
DestroyContext(ImGuiContext * ctx)2657 void ImGui::DestroyContext(ImGuiContext* ctx)
2658 {
2659     if (ctx == NULL)
2660         ctx = GImGui;
2661     Shutdown(ctx);
2662     if (GImGui == ctx)
2663         SetCurrentContext(NULL);
2664     IM_DELETE(ctx);
2665 }
2666 
GetIO()2667 ImGuiIO& ImGui::GetIO()
2668 {
2669     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2670     return GImGui->IO;
2671 }
2672 
GetStyle()2673 ImGuiStyle& ImGui::GetStyle()
2674 {
2675     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2676     return GImGui->Style;
2677 }
2678 
2679 // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
GetDrawData()2680 ImDrawData* ImGui::GetDrawData()
2681 {
2682     ImGuiContext& g = *GImGui;
2683     return g.DrawData.Valid ? &g.DrawData : NULL;
2684 }
2685 
GetTime()2686 float ImGui::GetTime()
2687 {
2688     return GImGui->Time;
2689 }
2690 
GetFrameCount()2691 int ImGui::GetFrameCount()
2692 {
2693     return GImGui->FrameCount;
2694 }
2695 
GetOverlayDrawList()2696 ImDrawList* ImGui::GetOverlayDrawList()
2697 {
2698     return &GImGui->OverlayDrawList;
2699 }
2700 
GetDrawListSharedData()2701 ImDrawListSharedData* ImGui::GetDrawListSharedData()
2702 {
2703     return &GImGui->DrawListSharedData;
2704 }
2705 
2706 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)2707 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
2708 {
2709     ImGuiContext& g = *GImGui;
2710     IM_ASSERT(window == g.NavWindow);
2711     bool init_for_nav = false;
2712     if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
2713         if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
2714             init_for_nav = true;
2715     if (init_for_nav)
2716     {
2717         SetNavID(0, g.NavLayer);
2718         g.NavInitRequest = true;
2719         g.NavInitRequestFromMove = false;
2720         g.NavInitResultId = 0;
2721         g.NavInitResultRectRel = ImRect();
2722         NavUpdateAnyRequestFlag();
2723     }
2724     else
2725     {
2726         g.NavId = window->NavLastIds[0];
2727     }
2728 }
2729 
NavCalcPreferredMousePos()2730 static ImVec2 NavCalcPreferredMousePos()
2731 {
2732     ImGuiContext& g = *GImGui;
2733     ImGuiWindow* window = g.NavWindow;
2734     if (!window)
2735         return g.IO.MousePos;
2736     const ImRect& rect_rel = window->NavRectRel[g.NavLayer];
2737     ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x*4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight()));
2738     ImRect visible_rect = GetViewportRect();
2739     return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max));   // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta.
2740 }
2741 
FindWindowIndex(ImGuiWindow * window)2742 static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N)
2743 {
2744     ImGuiContext& g = *GImGui;
2745     for (int i = g.Windows.Size-1; i >= 0; i--)
2746         if (g.Windows[i] == window)
2747             return i;
2748     return -1;
2749 }
2750 
FindWindowNavigable(int i_start,int i_stop,int dir)2751 static ImGuiWindow* FindWindowNavigable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
2752 {
2753     ImGuiContext& g = *GImGui;
2754     for (int i = i_start; i >= 0 && i < g.Windows.Size && i != i_stop; i += dir)
2755         if (ImGui::IsWindowNavFocusable(g.Windows[i]))
2756             return g.Windows[i];
2757     return NULL;
2758 }
2759 
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)2760 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
2761 {
2762     ImGuiContext& g = *GImGui;
2763     if (mode == ImGuiInputReadMode_Down)
2764         return g.IO.NavInputs[n];                         // Instant, read analog input (0.0f..1.0f, as provided by user)
2765 
2766     const float t = g.IO.NavInputsDownDuration[n];
2767     if (t < 0.0f && mode == ImGuiInputReadMode_Released)  // Return 1.0f when just released, no repeat, ignore analog input.
2768         return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
2769     if (t < 0.0f)
2770         return 0.0f;
2771     if (mode == ImGuiInputReadMode_Pressed)               // Return 1.0f when just pressed, no repeat, ignore analog input.
2772         return (t == 0.0f) ? 1.0f : 0.0f;
2773     if (mode == ImGuiInputReadMode_Repeat)
2774         return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f);
2775     if (mode == ImGuiInputReadMode_RepeatSlow)
2776         return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f);
2777     if (mode == ImGuiInputReadMode_RepeatFast)
2778         return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f);
2779     return 0.0f;
2780 }
2781 
2782 // Equivalent of IsKeyDown() for NavInputs[]
IsNavInputDown(ImGuiNavInput n)2783 static bool IsNavInputDown(ImGuiNavInput n)
2784 {
2785     return GImGui->IO.NavInputs[n] > 0.0f;
2786 }
2787 
2788 // Equivalent of IsKeyPressed() for NavInputs[]
IsNavInputPressed(ImGuiNavInput n,ImGuiInputReadMode mode)2789 static bool IsNavInputPressed(ImGuiNavInput n, ImGuiInputReadMode mode)
2790 {
2791     return ImGui::GetNavInputAmount(n, mode) > 0.0f;
2792 }
2793 
IsNavInputPressedAnyOfTwo(ImGuiNavInput n1,ImGuiNavInput n2,ImGuiInputReadMode mode)2794 static bool IsNavInputPressedAnyOfTwo(ImGuiNavInput n1, ImGuiNavInput n2, ImGuiInputReadMode mode)
2795 {
2796     return (ImGui::GetNavInputAmount(n1, mode) + ImGui::GetNavInputAmount(n2, mode)) > 0.0f;
2797 }
2798 
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)2799 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
2800 {
2801     ImVec2 delta(0.0f, 0.0f);
2802     if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
2803         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode)   - GetNavInputAmount(ImGuiNavInput_KeyLeft_,   mode), GetNavInputAmount(ImGuiNavInput_KeyDown_,   mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_,   mode));
2804     if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
2805         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode)   - GetNavInputAmount(ImGuiNavInput_DpadLeft,   mode), GetNavInputAmount(ImGuiNavInput_DpadDown,   mode) - GetNavInputAmount(ImGuiNavInput_DpadUp,   mode));
2806     if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
2807         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
2808     if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
2809         delta *= slow_factor;
2810     if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
2811         delta *= fast_factor;
2812     return delta;
2813 }
2814 
NavUpdateWindowingHighlightWindow(int focus_change_dir)2815 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
2816 {
2817     ImGuiContext& g = *GImGui;
2818     IM_ASSERT(g.NavWindowingTarget);
2819     if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
2820         return;
2821 
2822     const int i_current = FindWindowIndex(g.NavWindowingTarget);
2823     ImGuiWindow* window_target = FindWindowNavigable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
2824     if (!window_target)
2825         window_target = FindWindowNavigable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir);
2826     g.NavWindowingTarget = window_target;
2827     g.NavWindowingToggleLayer = false;
2828 }
2829 
2830 // Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer)
NavUpdateWindowing()2831 static void ImGui::NavUpdateWindowing()
2832 {
2833     ImGuiContext& g = *GImGui;
2834     ImGuiWindow* apply_focus_window = NULL;
2835     bool apply_toggle_layer = false;
2836 
2837     bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
2838     bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
2839     if (start_windowing_with_gamepad || start_windowing_with_keyboard)
2840         if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavigable(g.Windows.Size - 1, -INT_MAX, -1))
2841         {
2842             g.NavWindowingTarget = window->RootWindowForTabbing;
2843             g.NavWindowingHighlightTimer = g.NavWindowingHighlightAlpha = 0.0f;
2844             g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
2845             g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
2846         }
2847 
2848     // Gamepad update
2849     g.NavWindowingHighlightTimer += g.IO.DeltaTime;
2850     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
2851     {
2852         // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise
2853         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.20f) / 0.05f));
2854 
2855         // Select window to focus
2856         const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
2857         if (focus_change_dir != 0)
2858         {
2859             NavUpdateWindowingHighlightWindow(focus_change_dir);
2860             g.NavWindowingHighlightAlpha = 1.0f;
2861         }
2862 
2863         // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most)
2864         if (!IsNavInputDown(ImGuiNavInput_Menu))
2865         {
2866             g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
2867             if (g.NavWindowingToggleLayer && g.NavWindow)
2868                 apply_toggle_layer = true;
2869             else if (!g.NavWindowingToggleLayer)
2870                 apply_focus_window = g.NavWindowingTarget;
2871             g.NavWindowingTarget = NULL;
2872         }
2873     }
2874 
2875     // Keyboard: Focus
2876     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
2877     {
2878         // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
2879         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.15f) / 0.04f)); // 1.0f
2880         if (IsKeyPressedMap(ImGuiKey_Tab, true))
2881             NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
2882         if (!g.IO.KeyCtrl)
2883             apply_focus_window = g.NavWindowingTarget;
2884     }
2885 
2886     // Keyboard: Press and Release ALT to toggle menu layer
2887     // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB
2888     if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
2889         if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
2890             apply_toggle_layer = true;
2891 
2892     // Move window
2893     if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
2894     {
2895         ImVec2 move_delta;
2896         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
2897             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
2898         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
2899             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
2900         if (move_delta.x != 0.0f || move_delta.y != 0.0f)
2901         {
2902             const float NAV_MOVE_SPEED = 800.0f;
2903             const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
2904             g.NavWindowingTarget->PosFloat += move_delta * move_speed;
2905             g.NavDisableMouseHover = true;
2906             MarkIniSettingsDirty(g.NavWindowingTarget);
2907         }
2908     }
2909 
2910     // Apply final focus
2911     if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindowForTabbing))
2912     {
2913         g.NavDisableHighlight = false;
2914         g.NavDisableMouseHover = true;
2915         apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
2916         ClosePopupsOverWindow(apply_focus_window);
2917         FocusWindow(apply_focus_window);
2918         if (apply_focus_window->NavLastIds[0] == 0)
2919             NavInitWindow(apply_focus_window, false);
2920 
2921         // If the window only has a menu layer, select it directly
2922         if (apply_focus_window->DC.NavLayerActiveMask == (1 << 1))
2923             g.NavLayer = 1;
2924     }
2925     if (apply_focus_window)
2926         g.NavWindowingTarget = NULL;
2927 
2928     // Apply menu/layer toggle
2929     if (apply_toggle_layer && g.NavWindow)
2930     {
2931         ImGuiWindow* new_nav_window = g.NavWindow;
2932         while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
2933             new_nav_window = new_nav_window->ParentWindow;
2934         if (new_nav_window != g.NavWindow)
2935         {
2936             ImGuiWindow* old_nav_window = g.NavWindow;
2937             FocusWindow(new_nav_window);
2938             new_nav_window->NavLastChildNavWindow = old_nav_window;
2939         }
2940         g.NavDisableHighlight = false;
2941         g.NavDisableMouseHover = true;
2942         NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0);
2943     }
2944 }
2945 
2946 // NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated.
NavScrollToBringItemIntoView(ImGuiWindow * window,ImRect & item_rect_rel)2947 static void NavScrollToBringItemIntoView(ImGuiWindow* window, ImRect& item_rect_rel)
2948 {
2949     // Scroll to keep newly navigated item fully into view
2950     ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1));
2951     //g.OverlayDrawList.AddRect(window->Pos + window_rect_rel.Min, window->Pos + window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG]
2952     if (window_rect_rel.Contains(item_rect_rel))
2953         return;
2954 
2955     ImGuiContext& g = *GImGui;
2956     if (window->ScrollbarX && item_rect_rel.Min.x < window_rect_rel.Min.x)
2957     {
2958         window->ScrollTarget.x = item_rect_rel.Min.x + window->Scroll.x - g.Style.ItemSpacing.x;
2959         window->ScrollTargetCenterRatio.x = 0.0f;
2960     }
2961     else if (window->ScrollbarX && item_rect_rel.Max.x >= window_rect_rel.Max.x)
2962     {
2963         window->ScrollTarget.x = item_rect_rel.Max.x + window->Scroll.x + g.Style.ItemSpacing.x;
2964         window->ScrollTargetCenterRatio.x = 1.0f;
2965     }
2966     if (item_rect_rel.Min.y < window_rect_rel.Min.y)
2967     {
2968         window->ScrollTarget.y = item_rect_rel.Min.y + window->Scroll.y - g.Style.ItemSpacing.y;
2969         window->ScrollTargetCenterRatio.y = 0.0f;
2970     }
2971     else if (item_rect_rel.Max.y >= window_rect_rel.Max.y)
2972     {
2973         window->ScrollTarget.y = item_rect_rel.Max.y + window->Scroll.y + g.Style.ItemSpacing.y;
2974         window->ScrollTargetCenterRatio.y = 1.0f;
2975     }
2976 
2977     // Estimate upcoming scroll so we can offset our relative mouse position so mouse position can be applied immediately (under this block)
2978     ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
2979     item_rect_rel.Translate(window->Scroll - next_scroll);
2980 }
2981 
NavUpdate()2982 static void ImGui::NavUpdate()
2983 {
2984     ImGuiContext& g = *GImGui;
2985     g.IO.WantSetMousePos = false;
2986 
2987 #if 0
2988     if (g.NavScoringCount > 0) printf("[%05d] NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
2989 #endif
2990 
2991     if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad))
2992         if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f)
2993             g.NavInputSource = ImGuiInputSource_NavGamepad;
2994 
2995     // Update Keyboard->Nav inputs mapping
2996     if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
2997     {
2998         #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; }
2999         NAV_MAP_KEY(ImGuiKey_Space,     ImGuiNavInput_Activate );
3000         NAV_MAP_KEY(ImGuiKey_Enter,     ImGuiNavInput_Input    );
3001         NAV_MAP_KEY(ImGuiKey_Escape,    ImGuiNavInput_Cancel   );
3002         NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
3003         NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
3004         NAV_MAP_KEY(ImGuiKey_UpArrow,   ImGuiNavInput_KeyUp_   );
3005         NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
3006         if (g.IO.KeyCtrl)   g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
3007         if (g.IO.KeyShift)  g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
3008         if (g.IO.KeyAlt)    g.IO.NavInputs[ImGuiNavInput_KeyMenu_]  = 1.0f;
3009         #undef NAV_MAP_KEY
3010     }
3011 
3012     memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration));
3013     for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++)
3014         g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f;
3015 
3016     // Process navigation init request (select first/default focus)
3017     if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove))
3018     {
3019         // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
3020         IM_ASSERT(g.NavWindow);
3021         if (g.NavInitRequestFromMove)
3022             SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel);
3023         else
3024             SetNavID(g.NavInitResultId, g.NavLayer);
3025         g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
3026     }
3027     g.NavInitRequest = false;
3028     g.NavInitRequestFromMove = false;
3029     g.NavInitResultId = 0;
3030     g.NavJustMovedToId = 0;
3031 
3032     // Process navigation move request
3033     if (g.NavMoveRequest && (g.NavMoveResultLocal.ID != 0 || g.NavMoveResultOther.ID != 0))
3034     {
3035         // Select which result to use
3036         ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
3037         if (g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) // Maybe entering a flattened child? In this case solve the tie using the regular scoring rules
3038             if ((g.NavMoveResultOther.DistBox < g.NavMoveResultLocal.DistBox) || (g.NavMoveResultOther.DistBox == g.NavMoveResultLocal.DistBox && g.NavMoveResultOther.DistCenter < g.NavMoveResultLocal.DistCenter))
3039                 result = &g.NavMoveResultOther;
3040 
3041         IM_ASSERT(g.NavWindow && result->Window);
3042 
3043         // Scroll to keep newly navigated item fully into view
3044         if (g.NavLayer == 0)
3045             NavScrollToBringItemIntoView(result->Window, result->RectRel);
3046 
3047         // Apply result from previous frame navigation directional move request
3048         ClearActiveID();
3049         g.NavWindow = result->Window;
3050         SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel);
3051         g.NavJustMovedToId = result->ID;
3052         g.NavMoveFromClampedRefRect = false;
3053     }
3054 
3055     // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
3056     if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
3057     {
3058         IM_ASSERT(g.NavMoveRequest);
3059         if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
3060             g.NavDisableHighlight = false;
3061         g.NavMoveRequestForward = ImGuiNavForward_None;
3062     }
3063 
3064     // Apply application mouse position movement, after we had a chance to process move request result.
3065     if (g.NavMousePosDirty && g.NavIdIsAlive)
3066     {
3067         // Set mouse position given our knowledge of the nav widget position from last frame
3068         if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
3069         {
3070             g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredMousePos();
3071             g.IO.WantSetMousePos = true;
3072         }
3073         g.NavMousePosDirty = false;
3074     }
3075     g.NavIdIsAlive = false;
3076     g.NavJustTabbedId = 0;
3077     IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
3078 
3079     // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0
3080     if (g.NavWindow)
3081         NavSaveLastChildNavWindow(g.NavWindow);
3082     if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0)
3083         g.NavWindow->NavLastChildNavWindow = NULL;
3084 
3085     NavUpdateWindowing();
3086 
3087     // Set output flags for user application
3088     bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
3089     bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
3090     g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
3091     g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest;
3092 
3093     // Process NavCancel input (to close a popup, get back to parent, clear focus)
3094     if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
3095     {
3096         if (g.ActiveId != 0)
3097         {
3098             ClearActiveID();
3099         }
3100         else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
3101         {
3102             // Exit child window
3103             ImGuiWindow* child_window = g.NavWindow;
3104             ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
3105             IM_ASSERT(child_window->ChildId != 0);
3106             FocusWindow(parent_window);
3107             SetNavID(child_window->ChildId, 0);
3108             g.NavIdIsAlive = false;
3109             if (g.NavDisableMouseHover)
3110                 g.NavMousePosDirty = true;
3111         }
3112         else if (g.OpenPopupStack.Size > 0)
3113         {
3114             // Close open popup/menu
3115             if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
3116                 ClosePopupToLevel(g.OpenPopupStack.Size - 1);
3117         }
3118         else if (g.NavLayer != 0)
3119         {
3120             // Leave the "menu" layer
3121             NavRestoreLayer(0);
3122         }
3123         else
3124         {
3125             // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
3126             if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
3127                 g.NavWindow->NavLastIds[0] = 0;
3128             g.NavId = 0;
3129         }
3130     }
3131 
3132     // Process manual activation request
3133     g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
3134     if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
3135     {
3136         bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
3137         bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
3138         if (g.ActiveId == 0 && activate_pressed)
3139             g.NavActivateId = g.NavId;
3140         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
3141             g.NavActivateDownId = g.NavId;
3142         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
3143             g.NavActivatePressedId = g.NavId;
3144         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
3145             g.NavInputId = g.NavId;
3146     }
3147     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
3148         g.NavDisableHighlight = true;
3149     if (g.NavActivateId != 0)
3150         IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
3151     g.NavMoveRequest = false;
3152 
3153     // Process programmatic activation request
3154     if (g.NavNextActivateId != 0)
3155         g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
3156     g.NavNextActivateId = 0;
3157 
3158     // Initiate directional inputs request
3159     const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags;
3160     if (g.NavMoveRequestForward == ImGuiNavForward_None)
3161     {
3162         g.NavMoveDir = ImGuiDir_None;
3163         if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
3164         {
3165             if ((allowed_dir_flags & (1<<ImGuiDir_Left))  && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft, ImGuiNavInput_KeyLeft_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Left;
3166             if ((allowed_dir_flags & (1<<ImGuiDir_Right)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadRight,ImGuiNavInput_KeyRight_,ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Right;
3167             if ((allowed_dir_flags & (1<<ImGuiDir_Up))    && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadUp,   ImGuiNavInput_KeyUp_,   ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Up;
3168             if ((allowed_dir_flags & (1<<ImGuiDir_Down))  && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadDown, ImGuiNavInput_KeyDown_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Down;
3169         }
3170     }
3171     else
3172     {
3173         // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window)
3174         IM_ASSERT(g.NavMoveDir != ImGuiDir_None);
3175         IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
3176         g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
3177     }
3178 
3179     if (g.NavMoveDir != ImGuiDir_None)
3180     {
3181         g.NavMoveRequest = true;
3182         g.NavMoveDirLast = g.NavMoveDir;
3183     }
3184 
3185     // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match
3186     if (g.NavMoveRequest && g.NavId == 0)
3187     {
3188         g.NavInitRequest = g.NavInitRequestFromMove = true;
3189         g.NavInitResultId = 0;
3190         g.NavDisableHighlight = false;
3191     }
3192 
3193     NavUpdateAnyRequestFlag();
3194 
3195     // Scrolling
3196     if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
3197     {
3198         // *Fallback* manual-scroll with NavUp/NavDown when window has no navigable item
3199         ImGuiWindow* window = g.NavWindow;
3200         const float scroll_speed = ImFloor(window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
3201         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
3202         {
3203             if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
3204                 SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
3205             if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
3206                 SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
3207         }
3208 
3209         // *Normal* Manual scroll with NavScrollXXX keys
3210         // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
3211         ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f);
3212         if (scroll_dir.x != 0.0f && window->ScrollbarX)
3213         {
3214             SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
3215             g.NavMoveFromClampedRefRect = true;
3216         }
3217         if (scroll_dir.y != 0.0f)
3218         {
3219             SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
3220             g.NavMoveFromClampedRefRect = true;
3221         }
3222     }
3223 
3224     // Reset search results
3225     g.NavMoveResultLocal.Clear();
3226     g.NavMoveResultOther.Clear();
3227 
3228     // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items
3229     if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
3230     {
3231         ImGuiWindow* window = g.NavWindow;
3232         ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1,1), window->InnerRect.Max - window->Pos + ImVec2(1,1));
3233         if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
3234         {
3235             float pad = 0.0f;
3236             if (window) pad = window->CalcFontSize() * 0.5f;
3237             window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item
3238             window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel);
3239             g.NavId = 0;
3240         }
3241         g.NavMoveFromClampedRefRect = false;
3242     }
3243 
3244     // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items)
3245     ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0);
3246     g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
3247     g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x);
3248     g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
3249     IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous fabsf() calls in NavScoreItem().
3250     //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
3251     g.NavScoringCount = 0;
3252 #if IMGUI_DEBUG_NAV_RECTS
3253     if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) g.OverlayDrawList.AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG]
3254     if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames == 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredMousePos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); g.OverlayDrawList.AddCircleFilled(p, 3.0f, col); g.OverlayDrawList.AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
3255 #endif
3256 }
3257 
NewFrameUpdateMovingWindow()3258 static void ImGui::NewFrameUpdateMovingWindow()
3259 {
3260     ImGuiContext& g = *GImGui;
3261     if (g.MovingWindow && g.MovingWindow->MoveId == g.ActiveId && g.ActiveIdSource == ImGuiInputSource_Mouse)
3262     {
3263         // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3264         // We track it to preserve Focus and so that ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3265         KeepAliveID(g.ActiveId);
3266         IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3267         ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3268         if (g.IO.MouseDown[0])
3269         {
3270             ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3271             if (moving_window->PosFloat.x != pos.x || moving_window->PosFloat.y != pos.y)
3272             {
3273                 MarkIniSettingsDirty(moving_window);
3274                 moving_window->PosFloat = pos;
3275             }
3276             FocusWindow(g.MovingWindow);
3277         }
3278         else
3279         {
3280             ClearActiveID();
3281             g.MovingWindow = NULL;
3282         }
3283     }
3284     else
3285     {
3286         // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3287         if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3288         {
3289             KeepAliveID(g.ActiveId);
3290             if (!g.IO.MouseDown[0])
3291                 ClearActiveID();
3292         }
3293         g.MovingWindow = NULL;
3294     }
3295 }
3296 
NewFrameUpdateMouseInputs()3297 static void ImGui::NewFrameUpdateMouseInputs()
3298 {
3299     ImGuiContext& g = *GImGui;
3300 
3301     // If mouse just appeared or disappeared (usually denoted by -FLT_MAX component, but in reality we test for -256000.0f) we cancel out movement in MouseDelta
3302     if (ImGui::IsMousePosValid(&g.IO.MousePos) && ImGui::IsMousePosValid(&g.IO.MousePosPrev))
3303         g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3304     else
3305         g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3306     if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3307         g.NavDisableMouseHover = false;
3308 
3309     g.IO.MousePosPrev = g.IO.MousePos;
3310     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3311     {
3312         g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3313         g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3314         g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3315         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;
3316         g.IO.MouseDoubleClicked[i] = false;
3317         if (g.IO.MouseClicked[i])
3318         {
3319             if (g.Time - g.IO.MouseClickedTime[i] < g.IO.MouseDoubleClickTime)
3320             {
3321                 if (ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3322                     g.IO.MouseDoubleClicked[i] = true;
3323                 g.IO.MouseClickedTime[i] = -FLT_MAX;    // so the third click isn't turned into a double-click
3324             }
3325             else
3326             {
3327                 g.IO.MouseClickedTime[i] = g.Time;
3328             }
3329             g.IO.MouseClickedPos[i] = g.IO.MousePos;
3330             g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3331             g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3332         }
3333         else if (g.IO.MouseDown[i])
3334         {
3335             ImVec2 mouse_delta = g.IO.MousePos - g.IO.MouseClickedPos[i];
3336             g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, mouse_delta.x < 0.0f ? -mouse_delta.x : mouse_delta.x);
3337             g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, mouse_delta.y < 0.0f ? -mouse_delta.y : mouse_delta.y);
3338             g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(mouse_delta));
3339         }
3340         if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3341             g.NavDisableMouseHover = false;
3342     }
3343 }
3344 
3345 // The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app)
NewFrameUpdateHoveredWindowAndCaptureFlags()3346 void ImGui::NewFrameUpdateHoveredWindowAndCaptureFlags()
3347 {
3348     ImGuiContext& g = *GImGui;
3349 
3350     // Find the window hovered by mouse:
3351     // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3352     // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame.
3353     // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms.
3354     g.HoveredWindow = (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) ? g.MovingWindow : FindHoveredWindow();
3355     g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
3356 
3357     // Modal windows prevents cursor from hovering behind them.
3358     ImGuiWindow* modal_window = GetFrontMostPopupModal();
3359     if (modal_window)
3360         if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3361             g.HoveredRootWindow = g.HoveredWindow = NULL;
3362 
3363     // Disabled mouse?
3364     if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3365         g.HoveredWindow = g.HoveredRootWindow = NULL;
3366 
3367     // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward.
3368     int mouse_earliest_button_down = -1;
3369     bool mouse_any_down = false;
3370     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3371     {
3372         if (g.IO.MouseClicked[i])
3373             g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
3374         mouse_any_down |= g.IO.MouseDown[i];
3375         if (g.IO.MouseDown[i])
3376             if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3377                 mouse_earliest_button_down = i;
3378     }
3379     const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3380 
3381     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3382     // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3383     const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3384     if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3385         g.HoveredWindow = g.HoveredRootWindow = NULL;
3386 
3387     // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to imgui + app)
3388     if (g.WantCaptureMouseNextFrame != -1)
3389         g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3390     else
3391         g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
3392 
3393     // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to imgui + app)
3394     if (g.WantCaptureKeyboardNextFrame != -1)
3395         g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3396     else
3397         g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3398     if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3399         g.IO.WantCaptureKeyboard = true;
3400 
3401     // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3402     g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : 0;
3403 }
3404 
NewFrame()3405 void ImGui::NewFrame()
3406 {
3407     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3408     ImGuiContext& g = *GImGui;
3409 
3410     // Check user data
3411     // (We pass an error message in the assert expression as a trick to get it visible to programmers who are not using a debugger, as most assert handlers display their argument)
3412     IM_ASSERT(g.Initialized);
3413     IM_ASSERT(g.IO.DeltaTime >= 0.0f                                    && "Need a positive DeltaTime (zero is tolerated but will cause some timing issues)");
3414     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && "Invalid DisplaySize value");
3415     IM_ASSERT(g.IO.Fonts->Fonts.Size > 0                                && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3416     IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()                          && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3417     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting");
3418     IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f            && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)");
3419     IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
3420     for (int n = 0; n < ImGuiKey_COUNT; n++)
3421         IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)");
3422 
3423     // Do a simple check for required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was super recently added in 1.60 WIP)
3424     if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
3425         IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
3426 
3427     // Load settings on first frame
3428     if (!g.SettingsLoaded)
3429     {
3430         IM_ASSERT(g.SettingsWindows.empty());
3431         LoadIniSettingsFromDisk(g.IO.IniFilename);
3432         g.SettingsLoaded = true;
3433     }
3434 
3435     // Save settings (with a delay so we don't spam disk too much)
3436     if (g.SettingsDirtyTimer > 0.0f)
3437     {
3438         g.SettingsDirtyTimer -= g.IO.DeltaTime;
3439         if (g.SettingsDirtyTimer <= 0.0f)
3440             SaveIniSettingsToDisk(g.IO.IniFilename);
3441     }
3442 
3443     g.Time += g.IO.DeltaTime;
3444     g.FrameCount += 1;
3445     g.TooltipOverrideCount = 0;
3446     g.WindowsActiveCount = 0;
3447 
3448     SetCurrentFont(GetDefaultFont());
3449     IM_ASSERT(g.Font->IsLoaded());
3450     g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3451     g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3452 
3453     g.OverlayDrawList.Clear();
3454     g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID);
3455     g.OverlayDrawList.PushClipRectFullScreen();
3456     g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
3457 
3458     // Mark rendering data as invalid to prevent user who may have a handle on it to use it
3459     g.DrawData.Clear();
3460 
3461     // Clear reference to active widget if the widget isn't alive anymore
3462     if (!g.HoveredIdPreviousFrame)
3463         g.HoveredIdTimer = 0.0f;
3464     g.HoveredIdPreviousFrame = g.HoveredId;
3465     g.HoveredId = 0;
3466     g.HoveredIdAllowOverlap = false;
3467     if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3468         ClearActiveID();
3469     if (g.ActiveId)
3470         g.ActiveIdTimer += g.IO.DeltaTime;
3471     g.ActiveIdPreviousFrame = g.ActiveId;
3472     g.ActiveIdIsAlive = false;
3473     g.ActiveIdIsJustActivated = false;
3474     if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
3475         g.ScalarAsInputTextId = 0;
3476 
3477     // Elapse drag & drop payload
3478     if (g.DragDropActive && g.DragDropPayload.DataFrameCount + 1 < g.FrameCount)
3479     {
3480         ClearDragDrop();
3481         g.DragDropPayloadBufHeap.clear();
3482         memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
3483     }
3484     g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3485     g.DragDropAcceptIdCurr = 0;
3486     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3487 
3488     // Update keyboard input state
3489     memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3490     for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3491         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;
3492 
3493     // Update gamepad/keyboard directional navigation
3494     NavUpdate();
3495 
3496     // Update mouse input state
3497     NewFrameUpdateMouseInputs();
3498 
3499     // Calculate frame-rate for the user, as a purely luxurious feature
3500     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3501     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3502     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3503     g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3504 
3505     // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3506     NewFrameUpdateMovingWindow();
3507     NewFrameUpdateHoveredWindowAndCaptureFlags();
3508 
3509     if (GetFrontMostPopupModal() != NULL)
3510         g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3511     else
3512         g.ModalWindowDarkeningRatio = 0.0f;
3513 
3514     g.MouseCursor = ImGuiMouseCursor_Arrow;
3515     g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3516     g.OsImePosRequest = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3517 
3518     // Mouse wheel scrolling, scale
3519     if (g.HoveredWindow && !g.HoveredWindow->Collapsed && (g.IO.MouseWheel != 0.0f || g.IO.MouseWheelH != 0.0f))
3520     {
3521         // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent (unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set).
3522         ImGuiWindow* window = g.HoveredWindow;
3523         ImGuiWindow* scroll_window = window;
3524         while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs) && scroll_window->ParentWindow)
3525             scroll_window = scroll_window->ParentWindow;
3526         const bool scroll_allowed = !(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs);
3527 
3528         if (g.IO.MouseWheel != 0.0f)
3529         {
3530             if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3531             {
3532                 // Zoom / Scale window
3533                 const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3534                 const float scale = new_font_scale / window->FontWindowScale;
3535                 window->FontWindowScale = new_font_scale;
3536 
3537                 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3538                 window->Pos += offset;
3539                 window->PosFloat += offset;
3540                 window->Size *= scale;
3541                 window->SizeFull *= scale;
3542             }
3543             else if (!g.IO.KeyCtrl && scroll_allowed)
3544             {
3545                 // Mouse wheel vertical scrolling
3546                 float scroll_amount = 5 * scroll_window->CalcFontSize();
3547                 scroll_amount = (float)(int)ImMin(scroll_amount, (scroll_window->ContentsRegionRect.GetHeight() + scroll_window->WindowPadding.y * 2.0f) * 0.67f);
3548                 SetWindowScrollY(scroll_window, scroll_window->Scroll.y - g.IO.MouseWheel * scroll_amount);
3549             }
3550         }
3551         if (g.IO.MouseWheelH != 0.0f && scroll_allowed)
3552         {
3553             // Mouse wheel horizontal scrolling (for hardware that supports it)
3554             float scroll_amount = scroll_window->CalcFontSize();
3555             if (!g.IO.KeyCtrl && !(window->Flags & ImGuiWindowFlags_NoScrollWithMouse))
3556                 SetWindowScrollX(window, window->Scroll.x - g.IO.MouseWheelH * scroll_amount);
3557         }
3558     }
3559 
3560     // Pressing TAB activate widget focus
3561     if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false))
3562     {
3563         if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3564             g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3565         else
3566             g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0;
3567     }
3568     g.NavIdTabCounter = INT_MAX;
3569 
3570     // Mark all windows as not visible
3571     for (int i = 0; i != g.Windows.Size; i++)
3572     {
3573         ImGuiWindow* window = g.Windows[i];
3574         window->WasActive = window->Active;
3575         window->Active = false;
3576         window->WriteAccessed = false;
3577     }
3578 
3579     // Closing the focused window restore focus to the first active root window in descending z-order
3580     if (g.NavWindow && !g.NavWindow->WasActive)
3581         FocusFrontMostActiveWindow(NULL);
3582 
3583     // No window should be open at the beginning of the frame.
3584     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3585     g.CurrentWindowStack.resize(0);
3586     g.CurrentPopupStack.resize(0);
3587     ClosePopupsOverWindow(g.NavWindow);
3588 
3589     // Create implicit window - we will only render it if the user has added something to it.
3590     // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3591     SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
3592     Begin("Debug##Default");
3593 }
3594 
SettingsHandlerWindow_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)3595 static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
3596 {
3597     ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0));
3598     if (!settings)
3599         settings = AddWindowSettings(name);
3600     return (void*)settings;
3601 }
3602 
SettingsHandlerWindow_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)3603 static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
3604 {
3605     ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
3606     float x, y;
3607     int i;
3608     if (sscanf(line, "Pos=%f,%f", &x, &y) == 2)         settings->Pos = ImVec2(x, y);
3609     else if (sscanf(line, "Size=%f,%f", &x, &y) == 2)   settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize);
3610     else if (sscanf(line, "Collapsed=%d", &i) == 1)     settings->Collapsed = (i != 0);
3611 }
3612 
SettingsHandlerWindow_WriteAll(ImGuiContext * imgui_ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)3613 static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
3614 {
3615     // Gather data from windows that were active during this session
3616     ImGuiContext& g = *imgui_ctx;
3617     for (int i = 0; i != g.Windows.Size; i++)
3618     {
3619         ImGuiWindow* window = g.Windows[i];
3620         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
3621             continue;
3622         ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID);
3623         if (!settings)
3624             settings = AddWindowSettings(window->Name);
3625         settings->Pos = window->Pos;
3626         settings->Size = window->SizeFull;
3627         settings->Collapsed = window->Collapsed;
3628     }
3629 
3630     // Write a buffer
3631     // If a window wasn't opened in this session we preserve its settings
3632     buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve
3633     for (int i = 0; i != g.SettingsWindows.Size; i++)
3634     {
3635         const ImGuiWindowSettings* settings = &g.SettingsWindows[i];
3636         if (settings->Pos.x == FLT_MAX)
3637             continue;
3638         const char* name = settings->Name;
3639         if (const char* p = strstr(name, "###"))  // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
3640             name = p;
3641         buf->appendf("[%s][%s]\n", handler->TypeName, name);
3642         buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
3643         buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
3644         buf->appendf("Collapsed=%d\n", settings->Collapsed);
3645         buf->appendf("\n");
3646     }
3647 }
3648 
Initialize(ImGuiContext * context)3649 void ImGui::Initialize(ImGuiContext* context)
3650 {
3651     ImGuiContext& g = *context;
3652     IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3653     g.LogClipboard = IM_NEW(ImGuiTextBuffer)();
3654 
3655     // Add .ini handle for ImGuiWindow type
3656     ImGuiSettingsHandler ini_handler;
3657     ini_handler.TypeName = "Window";
3658     ini_handler.TypeHash = ImHash("Window", 0, 0);
3659     ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen;
3660     ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine;
3661     ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll;
3662     g.SettingsHandlers.push_front(ini_handler);
3663 
3664     g.Initialized = true;
3665 }
3666 
3667 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)3668 void ImGui::Shutdown(ImGuiContext* context)
3669 {
3670     ImGuiContext& g = *context;
3671 
3672     // 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)
3673     if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3674         IM_DELETE(g.IO.Fonts);
3675     g.IO.Fonts = NULL;
3676 
3677     // Cleanup of other data are conditional on actually having initialize ImGui.
3678     if (!g.Initialized)
3679         return;
3680 
3681     // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
3682     if (g.SettingsLoaded)
3683         SaveIniSettingsToDisk(g.IO.IniFilename);
3684 
3685     // Clear everything else
3686     for (int i = 0; i < g.Windows.Size; i++)
3687         IM_DELETE(g.Windows[i]);
3688     g.Windows.clear();
3689     g.WindowsSortBuffer.clear();
3690     g.CurrentWindow = NULL;
3691     g.CurrentWindowStack.clear();
3692     g.WindowsById.Clear();
3693     g.NavWindow = NULL;
3694     g.HoveredWindow = NULL;
3695     g.HoveredRootWindow = NULL;
3696     g.ActiveIdWindow = NULL;
3697     g.MovingWindow = NULL;
3698     g.ColorModifiers.clear();
3699     g.StyleModifiers.clear();
3700     g.FontStack.clear();
3701     g.OpenPopupStack.clear();
3702     g.CurrentPopupStack.clear();
3703     g.DrawDataBuilder.ClearFreeMemory();
3704     g.OverlayDrawList.ClearFreeMemory();
3705     g.PrivateClipboard.clear();
3706     g.InputTextState.Text.clear();
3707     g.InputTextState.InitialText.clear();
3708     g.InputTextState.TempTextBuffer.clear();
3709 
3710     for (int i = 0; i < g.SettingsWindows.Size; i++)
3711         IM_DELETE(g.SettingsWindows[i].Name);
3712     g.SettingsWindows.clear();
3713     g.SettingsHandlers.clear();
3714 
3715     if (g.LogFile && g.LogFile != stdout)
3716     {
3717         fclose(g.LogFile);
3718         g.LogFile = NULL;
3719     }
3720     if (g.LogClipboard)
3721         IM_DELETE(g.LogClipboard);
3722     g.LogClipboard = NULL;
3723 
3724     g.Initialized = false;
3725 }
3726 
FindWindowSettings(ImGuiID id)3727 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
3728 {
3729     ImGuiContext& g = *GImGui;
3730     for (int i = 0; i != g.SettingsWindows.Size; i++)
3731         if (g.SettingsWindows[i].Id == id)
3732             return &g.SettingsWindows[i];
3733     return NULL;
3734 }
3735 
AddWindowSettings(const char * name)3736 static ImGuiWindowSettings* AddWindowSettings(const char* name)
3737 {
3738     ImGuiContext& g = *GImGui;
3739     g.SettingsWindows.push_back(ImGuiWindowSettings());
3740     ImGuiWindowSettings* settings = &g.SettingsWindows.back();
3741     settings->Name = ImStrdup(name);
3742     settings->Id = ImHash(name, 0);
3743     return settings;
3744 }
3745 
LoadIniSettingsFromDisk(const char * ini_filename)3746 static void LoadIniSettingsFromDisk(const char* ini_filename)
3747 {
3748     if (!ini_filename)
3749         return;
3750     char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", NULL, +1);
3751     if (!file_data)
3752         return;
3753     LoadIniSettingsFromMemory(file_data);
3754     ImGui::MemFree(file_data);
3755 }
3756 
FindSettingsHandler(const char * type_name)3757 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
3758 {
3759     ImGuiContext& g = *GImGui;
3760     const ImGuiID type_hash = ImHash(type_name, 0, 0);
3761     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
3762         if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
3763             return &g.SettingsHandlers[handler_n];
3764     return NULL;
3765 }
3766 
3767 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * buf_readonly)3768 static void LoadIniSettingsFromMemory(const char* buf_readonly)
3769 {
3770     // For convenience and to make the code simpler, we'll write zero terminators inside the buffer. So let's create a writable copy.
3771     char* buf = ImStrdup(buf_readonly);
3772     char* buf_end = buf + strlen(buf);
3773 
3774     ImGuiContext& g = *GImGui;
3775     void* entry_data = NULL;
3776     ImGuiSettingsHandler* entry_handler = NULL;
3777 
3778     char* line_end = NULL;
3779     for (char* line = buf; line < buf_end; line = line_end + 1)
3780     {
3781         // Skip new lines markers, then find end of the line
3782         while (*line == '\n' || *line == '\r')
3783             line++;
3784         line_end = line;
3785         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
3786             line_end++;
3787         line_end[0] = 0;
3788 
3789         if (line[0] == '[' && line_end > line && line_end[-1] == ']')
3790         {
3791             // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
3792             line_end[-1] = 0;
3793             const char* name_end = line_end - 1;
3794             const char* type_start = line + 1;
3795             char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']');
3796             const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
3797             if (!type_end || !name_start)
3798             {
3799                 name_start = type_start; // Import legacy entries that have no type
3800                 type_start = "Window";
3801             }
3802             else
3803             {
3804                 *type_end = 0; // Overwrite first ']'
3805                 name_start++;  // Skip second '['
3806             }
3807             entry_handler = ImGui::FindSettingsHandler(type_start);
3808             entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
3809         }
3810         else if (entry_handler != NULL && entry_data != NULL)
3811         {
3812             // Let type handler parse the line
3813             entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
3814         }
3815     }
3816     ImGui::MemFree(buf);
3817     g.SettingsLoaded = true;
3818 }
3819 
SaveIniSettingsToDisk(const char * ini_filename)3820 static void SaveIniSettingsToDisk(const char* ini_filename)
3821 {
3822     ImGuiContext& g = *GImGui;
3823     g.SettingsDirtyTimer = 0.0f;
3824     if (!ini_filename)
3825         return;
3826 
3827     ImVector<char> buf;
3828     SaveIniSettingsToMemory(buf);
3829 
3830     FILE* f = ImFileOpen(ini_filename, "wt");
3831     if (!f)
3832         return;
3833     fwrite(buf.Data, sizeof(char), (size_t)buf.Size, f);
3834     fclose(f);
3835 }
3836 
SaveIniSettingsToMemory(ImVector<char> & out_buf)3837 static void SaveIniSettingsToMemory(ImVector<char>& out_buf)
3838 {
3839     ImGuiContext& g = *GImGui;
3840     g.SettingsDirtyTimer = 0.0f;
3841 
3842     ImGuiTextBuffer buf;
3843     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
3844     {
3845         ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
3846         handler->WriteAllFn(&g, handler, &buf);
3847     }
3848 
3849     buf.Buf.pop_back(); // Remove extra zero-terminator used by ImGuiTextBuffer
3850     out_buf.swap(buf.Buf);
3851 }
3852 
MarkIniSettingsDirty()3853 void ImGui::MarkIniSettingsDirty()
3854 {
3855     ImGuiContext& g = *GImGui;
3856     if (g.SettingsDirtyTimer <= 0.0f)
3857         g.SettingsDirtyTimer = g.IO.IniSavingRate;
3858 }
3859 
MarkIniSettingsDirty(ImGuiWindow * window)3860 static void MarkIniSettingsDirty(ImGuiWindow* window)
3861 {
3862     ImGuiContext& g = *GImGui;
3863     if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
3864         if (g.SettingsDirtyTimer <= 0.0f)
3865             g.SettingsDirtyTimer = g.IO.IniSavingRate;
3866 }
3867 
3868 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)3869 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
3870 {
3871     const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
3872     const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
3873     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
3874         return d;
3875     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
3876         return d;
3877     return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
3878 }
3879 
AddWindowToSortedBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)3880 static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
3881 {
3882     out_sorted_windows->push_back(window);
3883     if (window->Active)
3884     {
3885         int count = window->DC.ChildWindows.Size;
3886         if (count > 1)
3887             qsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
3888         for (int i = 0; i < count; i++)
3889         {
3890             ImGuiWindow* child = window->DC.ChildWindows[i];
3891             if (child->Active)
3892                 AddWindowToSortedBuffer(out_sorted_windows, child);
3893         }
3894     }
3895 }
3896 
AddDrawListToDrawData(ImVector<ImDrawList * > * out_render_list,ImDrawList * draw_list)3897 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_render_list, ImDrawList* draw_list)
3898 {
3899     if (draw_list->CmdBuffer.empty())
3900         return;
3901 
3902     // Remove trailing command if unused
3903     ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
3904     if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
3905     {
3906         draw_list->CmdBuffer.pop_back();
3907         if (draw_list->CmdBuffer.empty())
3908             return;
3909     }
3910 
3911     // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. May trigger for you if you are using PrimXXX functions incorrectly.
3912     IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
3913     IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
3914     IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
3915 
3916     // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
3917     // If this assert triggers because you are drawing lots of stuff manually:
3918     // A) Make sure you are coarse clipping, because ImDrawList let all your vertices pass. You can use the Metrics window to inspect draw list contents.
3919     // B) If you need/want meshes with more than 64K vertices, uncomment the '#define ImDrawIdx unsigned int' line in imconfig.h to set the index size to 4 bytes.
3920     //    You'll need to handle the 4-bytes indices to your renderer. For example, the OpenGL example code detect index size at compile-time by doing:
3921     //      glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
3922     //    Your own engine or render API may use different parameters or function calls to specify index sizes. 2 and 4 bytes indices are generally supported by most API.
3923     // C) If for some reason you cannot use 4 bytes indices or don't want to, a workaround is to call BeginChild()/EndChild() before reaching the 64K limit to split your draw commands in multiple draw lists.
3924     if (sizeof(ImDrawIdx) == 2)
3925         IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
3926 
3927     out_render_list->push_back(draw_list);
3928 }
3929 
AddWindowToDrawData(ImVector<ImDrawList * > * out_render_list,ImGuiWindow * window)3930 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
3931 {
3932     AddDrawListToDrawData(out_render_list, window->DrawList);
3933     for (int i = 0; i < window->DC.ChildWindows.Size; i++)
3934     {
3935         ImGuiWindow* child = window->DC.ChildWindows[i];
3936         if (child->Active && child->HiddenFrames == 0) // clipped children may have been marked not active
3937             AddWindowToDrawData(out_render_list, child);
3938     }
3939 }
3940 
AddWindowToDrawDataSelectLayer(ImGuiWindow * window)3941 static void AddWindowToDrawDataSelectLayer(ImGuiWindow* window)
3942 {
3943     ImGuiContext& g = *GImGui;
3944     g.IO.MetricsActiveWindows++;
3945     if (window->Flags & ImGuiWindowFlags_Tooltip)
3946         AddWindowToDrawData(&g.DrawDataBuilder.Layers[1], window);
3947     else
3948         AddWindowToDrawData(&g.DrawDataBuilder.Layers[0], window);
3949 }
3950 
FlattenIntoSingleLayer()3951 void ImDrawDataBuilder::FlattenIntoSingleLayer()
3952 {
3953     int n = Layers[0].Size;
3954     int size = n;
3955     for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
3956         size += Layers[i].Size;
3957     Layers[0].resize(size);
3958     for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
3959     {
3960         ImVector<ImDrawList*>& layer = Layers[layer_n];
3961         if (layer.empty())
3962             continue;
3963         memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
3964         n += layer.Size;
3965         layer.resize(0);
3966     }
3967 }
3968 
SetupDrawData(ImVector<ImDrawList * > * draw_lists,ImDrawData * out_draw_data)3969 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* out_draw_data)
3970 {
3971     out_draw_data->Valid = true;
3972     out_draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
3973     out_draw_data->CmdListsCount = draw_lists->Size;
3974     out_draw_data->TotalVtxCount = out_draw_data->TotalIdxCount = 0;
3975     for (int n = 0; n < draw_lists->Size; n++)
3976     {
3977         out_draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
3978         out_draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
3979     }
3980 }
3981 
3982 // When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result.
PushClipRect(const ImVec2 & clip_rect_min,const ImVec2 & clip_rect_max,bool intersect_with_current_clip_rect)3983 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
3984 {
3985     ImGuiWindow* window = GetCurrentWindow();
3986     window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
3987     window->ClipRect = window->DrawList->_ClipRectStack.back();
3988 }
3989 
PopClipRect()3990 void ImGui::PopClipRect()
3991 {
3992     ImGuiWindow* window = GetCurrentWindow();
3993     window->DrawList->PopClipRect();
3994     window->ClipRect = window->DrawList->_ClipRectStack.back();
3995 }
3996 
3997 // 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()3998 void ImGui::EndFrame()
3999 {
4000     ImGuiContext& g = *GImGui;
4001     IM_ASSERT(g.Initialized);                       // Forgot to call ImGui::NewFrame()
4002     if (g.FrameCountEnded == g.FrameCount)          // Don't process EndFrame() multiple times.
4003         return;
4004 
4005     // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
4006     if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.OsImePosRequest - g.OsImePosSet) > 0.0001f)
4007     {
4008         g.IO.ImeSetInputScreenPosFn((int)g.OsImePosRequest.x, (int)g.OsImePosRequest.y);
4009         g.OsImePosSet = g.OsImePosRequest;
4010     }
4011 
4012     // Hide implicit "Debug" window if it hasn't been used
4013     IM_ASSERT(g.CurrentWindowStack.Size == 1);    // Mismatched Begin()/End() calls
4014     if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
4015         g.CurrentWindow->Active = false;
4016     End();
4017 
4018     if (g.ActiveId == 0 && g.HoveredId == 0)
4019     {
4020         if (!g.NavWindow || !g.NavWindow->Appearing) // Unless we just made a window/popup appear
4021         {
4022             // Click to focus window and start moving (after we're done with all our widgets)
4023             if (g.IO.MouseClicked[0])
4024             {
4025                 if (g.HoveredRootWindow != NULL)
4026                 {
4027                     // Set ActiveId even if the _NoMove flag is set, without it dragging away from a window with _NoMove would activate hover on other windows.
4028                     FocusWindow(g.HoveredWindow);
4029                     SetActiveID(g.HoveredWindow->MoveId, g.HoveredWindow);
4030                     g.NavDisableHighlight = true;
4031                     g.ActiveIdClickOffset = g.IO.MousePos - g.HoveredRootWindow->Pos;
4032                     if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove) && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoMove))
4033                         g.MovingWindow = g.HoveredWindow;
4034                 }
4035                 else if (g.NavWindow != NULL && GetFrontMostPopupModal() == NULL)
4036                 {
4037                     // Clicking on void disable focus
4038                     FocusWindow(NULL);
4039                 }
4040             }
4041 
4042             // With right mouse button we close popups without changing focus
4043             // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger)
4044             if (g.IO.MouseClicked[1])
4045             {
4046                 // Find the top-most window between HoveredWindow and the front most Modal Window.
4047                 // This is where we can trim the popup stack.
4048                 ImGuiWindow* modal = GetFrontMostPopupModal();
4049                 bool hovered_window_above_modal = false;
4050                 if (modal == NULL)
4051                     hovered_window_above_modal = true;
4052                 for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
4053                 {
4054                     ImGuiWindow* window = g.Windows[i];
4055                     if (window == modal)
4056                         break;
4057                     if (window == g.HoveredWindow)
4058                         hovered_window_above_modal = true;
4059                 }
4060                 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal);
4061             }
4062         }
4063     }
4064 
4065     // Sort the window list so that all child windows are after their parent
4066     // We cannot do that on FocusWindow() because childs may not exist yet
4067     g.WindowsSortBuffer.resize(0);
4068     g.WindowsSortBuffer.reserve(g.Windows.Size);
4069     for (int i = 0; i != g.Windows.Size; i++)
4070     {
4071         ImGuiWindow* window = g.Windows[i];
4072         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
4073             continue;
4074         AddWindowToSortedBuffer(&g.WindowsSortBuffer, window);
4075     }
4076 
4077     IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size);  // we done something wrong
4078     g.Windows.swap(g.WindowsSortBuffer);
4079 
4080     // Clear Input data for next frame
4081     g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
4082     memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
4083     memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
4084 
4085     g.FrameCountEnded = g.FrameCount;
4086 }
4087 
Render()4088 void ImGui::Render()
4089 {
4090     ImGuiContext& g = *GImGui;
4091     IM_ASSERT(g.Initialized);   // Forgot to call ImGui::NewFrame()
4092 
4093     if (g.FrameCountEnded != g.FrameCount)
4094         ImGui::EndFrame();
4095     g.FrameCountRendered = g.FrameCount;
4096 
4097     // Gather windows to render
4098     g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0;
4099     g.DrawDataBuilder.Clear();
4100     ImGuiWindow* window_to_render_front_most = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget : NULL;
4101     for (int n = 0; n != g.Windows.Size; n++)
4102     {
4103         ImGuiWindow* window = g.Windows[n];
4104         if (window->Active && window->HiddenFrames == 0 && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != window_to_render_front_most)
4105             AddWindowToDrawDataSelectLayer(window);
4106     }
4107     if (window_to_render_front_most && window_to_render_front_most->Active && window_to_render_front_most->HiddenFrames == 0) // NavWindowingTarget is always temporarily displayed as the front-most window
4108         AddWindowToDrawDataSelectLayer(window_to_render_front_most);
4109     g.DrawDataBuilder.FlattenIntoSingleLayer();
4110 
4111     // Draw software mouse cursor if requested
4112     ImVec2 offset, size, uv[4];
4113     if (g.IO.MouseDrawCursor && g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &offset, &size, &uv[0], &uv[2]))
4114     {
4115         const ImVec2 pos = g.IO.MousePos - offset;
4116         const ImTextureID tex_id = g.IO.Fonts->TexID;
4117         const float sc = g.Style.MouseCursorScale;
4118         g.OverlayDrawList.PushTextureID(tex_id);
4119         g.OverlayDrawList.AddImage(tex_id, pos + ImVec2(1,0)*sc, pos+ImVec2(1,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48));        // Shadow
4120         g.OverlayDrawList.AddImage(tex_id, pos + ImVec2(2,0)*sc, pos+ImVec2(2,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48));        // Shadow
4121         g.OverlayDrawList.AddImage(tex_id, pos,                  pos + size*sc,                uv[2], uv[3], IM_COL32(0,0,0,255));       // Black border
4122         g.OverlayDrawList.AddImage(tex_id, pos,                  pos + size*sc,                uv[0], uv[1], IM_COL32(255,255,255,255)); // White fill
4123         g.OverlayDrawList.PopTextureID();
4124     }
4125     if (!g.OverlayDrawList.VtxBuffer.empty())
4126         AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.OverlayDrawList);
4127 
4128     // Setup ImDrawData structure for end-user
4129     SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
4130     g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
4131     g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
4132 
4133     // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData()
4134 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4135     if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
4136         g.IO.RenderDrawListsFn(&g.DrawData);
4137 #endif
4138 }
4139 
FindRenderedTextEnd(const char * text,const char * text_end)4140 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
4141 {
4142     const char* text_display_end = text;
4143     if (!text_end)
4144         text_end = (const char*)-1;
4145 
4146     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
4147         text_display_end++;
4148     return text_display_end;
4149 }
4150 
4151 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)4152 void ImGui::LogText(const char* fmt, ...)
4153 {
4154     ImGuiContext& g = *GImGui;
4155     if (!g.LogEnabled)
4156         return;
4157 
4158     va_list args;
4159     va_start(args, fmt);
4160     if (g.LogFile)
4161     {
4162         vfprintf(g.LogFile, fmt, args);
4163     }
4164     else
4165     {
4166         g.LogClipboard->appendfv(fmt, args);
4167     }
4168     va_end(args);
4169 }
4170 
4171 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
4172 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end=NULL)4173 static void LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL)
4174 {
4175     ImGuiContext& g = *GImGui;
4176     ImGuiWindow* window = g.CurrentWindow;
4177 
4178     if (!text_end)
4179         text_end = ImGui::FindRenderedTextEnd(text, text_end);
4180 
4181     const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1);
4182     if (ref_pos)
4183         window->DC.LogLinePosY = ref_pos->y;
4184 
4185     const char* text_remaining = text;
4186     if (g.LogStartDepth > window->DC.TreeDepth)  // Re-adjust padding if we have popped out of our starting depth
4187         g.LogStartDepth = window->DC.TreeDepth;
4188     const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth);
4189     for (;;)
4190     {
4191         // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
4192         const char* line_end = text_remaining;
4193         while (line_end < text_end)
4194             if (*line_end == '\n')
4195                 break;
4196             else
4197                 line_end++;
4198         if (line_end >= text_end)
4199             line_end = NULL;
4200 
4201         const bool is_first_line = (text == text_remaining);
4202         bool is_last_line = false;
4203         if (line_end == NULL)
4204         {
4205             is_last_line = true;
4206             line_end = text_end;
4207         }
4208         if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0))
4209         {
4210             const int char_count = (int)(line_end - text_remaining);
4211             if (log_new_line || !is_first_line)
4212                 ImGui::LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining);
4213             else
4214                 ImGui::LogText(" %.*s", char_count, text_remaining);
4215         }
4216 
4217         if (is_last_line)
4218             break;
4219         text_remaining = line_end + 1;
4220     }
4221 }
4222 
4223 // Internal ImGui functions to render text
4224 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)4225 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
4226 {
4227     ImGuiContext& g = *GImGui;
4228     ImGuiWindow* window = g.CurrentWindow;
4229 
4230     // Hide anything after a '##' string
4231     const char* text_display_end;
4232     if (hide_text_after_hash)
4233     {
4234         text_display_end = FindRenderedTextEnd(text, text_end);
4235     }
4236     else
4237     {
4238         if (!text_end)
4239             text_end = text + strlen(text); // FIXME-OPT
4240         text_display_end = text_end;
4241     }
4242 
4243     const int text_len = (int)(text_display_end - text);
4244     if (text_len > 0)
4245     {
4246         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
4247         if (g.LogEnabled)
4248             LogRenderedText(&pos, text, text_display_end);
4249     }
4250 }
4251 
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)4252 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
4253 {
4254     ImGuiContext& g = *GImGui;
4255     ImGuiWindow* window = g.CurrentWindow;
4256 
4257     if (!text_end)
4258         text_end = text + strlen(text); // FIXME-OPT
4259 
4260     const int text_len = (int)(text_end - text);
4261     if (text_len > 0)
4262     {
4263         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
4264         if (g.LogEnabled)
4265             LogRenderedText(&pos, text, text_end);
4266     }
4267 }
4268 
4269 // Default clip_rect uses (pos_min,pos_max)
4270 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
RenderTextClipped(const ImVec2 & pos_min,const ImVec2 & pos_max,const char * text,const char * text_end,const ImVec2 * text_size_if_known,const ImVec2 & align,const ImRect * clip_rect)4271 void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
4272 {
4273     // Hide anything after a '##' string
4274     const char* text_display_end = FindRenderedTextEnd(text, text_end);
4275     const int text_len = (int)(text_display_end - text);
4276     if (text_len == 0)
4277         return;
4278 
4279     ImGuiContext& g = *GImGui;
4280     ImGuiWindow* window = g.CurrentWindow;
4281 
4282     // Perform CPU side clipping for single clipped element to avoid using scissor state
4283     ImVec2 pos = pos_min;
4284     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
4285 
4286     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
4287     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
4288     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
4289     if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
4290         need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
4291 
4292     // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
4293     if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
4294     if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
4295 
4296     // Render
4297     if (need_clipping)
4298     {
4299         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
4300         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
4301     }
4302     else
4303     {
4304         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
4305     }
4306     if (g.LogEnabled)
4307         LogRenderedText(&pos, text, text_display_end);
4308 }
4309 
4310 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)4311 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
4312 {
4313     ImGuiContext& g = *GImGui;
4314     ImGuiWindow* window = g.CurrentWindow;
4315     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
4316     const float border_size = g.Style.FrameBorderSize;
4317     if (border && border_size > 0.0f)
4318     {
4319         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
4320         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
4321     }
4322 }
4323 
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)4324 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
4325 {
4326     ImGuiContext& g = *GImGui;
4327     ImGuiWindow* window = g.CurrentWindow;
4328     const float border_size = g.Style.FrameBorderSize;
4329     if (border_size > 0.0f)
4330     {
4331         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
4332         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
4333     }
4334 }
4335 
4336 // Render a triangle to denote expanded/collapsed state
RenderArrow(ImVec2 p_min,ImGuiDir dir,float scale)4337 void ImGui::RenderArrow(ImVec2 p_min, ImGuiDir dir, float scale)
4338 {
4339     ImGuiContext& g = *GImGui;
4340     ImGuiWindow* window = g.CurrentWindow;
4341 
4342     const float h = g.FontSize * 1.00f;
4343     float r = h * 0.40f * scale;
4344     ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale);
4345 
4346     ImVec2 a, b, c;
4347     switch (dir)
4348     {
4349     case ImGuiDir_Up:
4350     case ImGuiDir_Down:
4351         if (dir == ImGuiDir_Up) r = -r;
4352         center.y -= r * 0.25f;
4353         a = ImVec2(0,1) * r;
4354         b = ImVec2(-0.866f,-0.5f) * r;
4355         c = ImVec2(+0.866f,-0.5f) * r;
4356         break;
4357     case ImGuiDir_Left:
4358     case ImGuiDir_Right:
4359         if (dir == ImGuiDir_Left) r = -r;
4360         center.x -= r * 0.25f;
4361         a = ImVec2(1,0) * r;
4362         b = ImVec2(-0.500f,+0.866f) * r;
4363         c = ImVec2(-0.500f,-0.866f) * r;
4364         break;
4365     case ImGuiDir_None:
4366     case ImGuiDir_COUNT:
4367         IM_ASSERT(0);
4368         break;
4369     }
4370 
4371     window->DrawList->AddTriangleFilled(center + a, center + b, center + c, GetColorU32(ImGuiCol_Text));
4372 }
4373 
RenderBullet(ImVec2 pos)4374 void ImGui::RenderBullet(ImVec2 pos)
4375 {
4376     ImGuiContext& g = *GImGui;
4377     ImGuiWindow* window = g.CurrentWindow;
4378     window->DrawList->AddCircleFilled(pos, GImGui->FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8);
4379 }
4380 
RenderCheckMark(ImVec2 pos,ImU32 col,float sz)4381 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
4382 {
4383     ImGuiContext& g = *GImGui;
4384     ImGuiWindow* window = g.CurrentWindow;
4385 
4386     float thickness = ImMax(sz / 5.0f, 1.0f);
4387     sz -= thickness*0.5f;
4388     pos += ImVec2(thickness*0.25f, thickness*0.25f);
4389 
4390     float third = sz / 3.0f;
4391     float bx = pos.x + third;
4392     float by = pos.y + sz - third*0.5f;
4393     window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
4394     window->DrawList->PathLineTo(ImVec2(bx, by));
4395     window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2));
4396     window->DrawList->PathStroke(col, false, thickness);
4397 }
4398 
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)4399 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
4400 {
4401     ImGuiContext& g = *GImGui;
4402     if (id != g.NavId)
4403         return;
4404     if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
4405         return;
4406     ImGuiWindow* window = ImGui::GetCurrentWindow();
4407     if (window->DC.NavHideHighlightOneFrame)
4408         return;
4409 
4410     float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
4411     ImRect display_rect = bb;
4412     display_rect.ClipWith(window->ClipRect);
4413     if (flags & ImGuiNavHighlightFlags_TypeDefault)
4414     {
4415         const float THICKNESS = 2.0f;
4416         const float DISTANCE = 3.0f + THICKNESS * 0.5f;
4417         display_rect.Expand(ImVec2(DISTANCE,DISTANCE));
4418         bool fully_visible = window->ClipRect.Contains(display_rect);
4419         if (!fully_visible)
4420             window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
4421         window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS);
4422         if (!fully_visible)
4423             window->DrawList->PopClipRect();
4424     }
4425     if (flags & ImGuiNavHighlightFlags_TypeThin)
4426     {
4427         window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
4428     }
4429 }
4430 
4431 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4432 // 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)4433 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
4434 {
4435     ImGuiContext& g = *GImGui;
4436 
4437     const char* text_display_end;
4438     if (hide_text_after_double_hash)
4439         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
4440     else
4441         text_display_end = text_end;
4442 
4443     ImFont* font = g.Font;
4444     const float font_size = g.FontSize;
4445     if (text == text_display_end)
4446         return ImVec2(0.0f, font_size);
4447     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
4448 
4449     // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field)
4450     const float font_scale = font_size / font->FontSize;
4451     const float character_spacing_x = 1.0f * font_scale;
4452     if (text_size.x > 0.0f)
4453         text_size.x -= character_spacing_x;
4454     text_size.x = (float)(int)(text_size.x + 0.95f);
4455 
4456     return text_size;
4457 }
4458 
4459 // Helper to calculate coarse clipping of large list of evenly sized items.
4460 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
4461 // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX
CalcListClipping(int items_count,float items_height,int * out_items_display_start,int * out_items_display_end)4462 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
4463 {
4464     ImGuiContext& g = *GImGui;
4465     ImGuiWindow* window = g.CurrentWindow;
4466     if (g.LogEnabled)
4467     {
4468         // If logging is active, do not perform any clipping
4469         *out_items_display_start = 0;
4470         *out_items_display_end = items_count;
4471         return;
4472     }
4473     if (window->SkipItems)
4474     {
4475         *out_items_display_start = *out_items_display_end = 0;
4476         return;
4477     }
4478 
4479     const ImVec2 pos = window->DC.CursorPos;
4480     int start = (int)((window->ClipRect.Min.y - pos.y) / items_height);
4481     int end = (int)((window->ClipRect.Max.y - pos.y) / items_height);
4482     if (g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Up) // When performing a navigation request, ensure we have one item extra in the direction we are moving to
4483         start--;
4484     if (g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down)
4485         end++;
4486 
4487     start = ImClamp(start, 0, items_count);
4488     end = ImClamp(end + 1, start, items_count);
4489     *out_items_display_start = start;
4490     *out_items_display_end = end;
4491 }
4492 
4493 // Find window given position, search front-to-back
4494 // FIXME: Note that we have a lag here because WindowRectClipped is updated in Begin() so windows moved by user via SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is called, aka before the next Begin(). Moving window thankfully isn't affected.
FindHoveredWindow()4495 static ImGuiWindow* FindHoveredWindow()
4496 {
4497     ImGuiContext& g = *GImGui;
4498     for (int i = g.Windows.Size - 1; i >= 0; i--)
4499     {
4500         ImGuiWindow* window = g.Windows[i];
4501         if (!window->Active)
4502             continue;
4503         if (window->Flags & ImGuiWindowFlags_NoInputs)
4504             continue;
4505 
4506         // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4507         ImRect bb(window->WindowRectClipped.Min - g.Style.TouchExtraPadding, window->WindowRectClipped.Max + g.Style.TouchExtraPadding);
4508         if (bb.Contains(g.IO.MousePos))
4509             return window;
4510     }
4511     return NULL;
4512 }
4513 
4514 // Test if mouse cursor is hovering given rectangle
4515 // NB- Rectangle is clipped by our current clip setting
4516 // 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)4517 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4518 {
4519     ImGuiContext& g = *GImGui;
4520     ImGuiWindow* window = g.CurrentWindow;
4521 
4522     // Clip
4523     ImRect rect_clipped(r_min, r_max);
4524     if (clip)
4525         rect_clipped.ClipWith(window->ClipRect);
4526 
4527     // Expand for touch input
4528     const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4529     return rect_for_touch.Contains(g.IO.MousePos);
4530 }
4531 
IsKeyPressedMap(ImGuiKey key,bool repeat)4532 static bool IsKeyPressedMap(ImGuiKey key, bool repeat)
4533 {
4534     const int key_index = GImGui->IO.KeyMap[key];
4535     return (key_index >= 0) ? ImGui::IsKeyPressed(key_index, repeat) : false;
4536 }
4537 
GetKeyIndex(ImGuiKey imgui_key)4538 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4539 {
4540     IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4541     return GImGui->IO.KeyMap[imgui_key];
4542 }
4543 
4544 // Note that imgui doesn't know the semantic of each entry of io.KeyDown[]. Use your own indices/enums according to how your back-end/engine stored them into KeyDown[]!
IsKeyDown(int user_key_index)4545 bool ImGui::IsKeyDown(int user_key_index)
4546 {
4547     if (user_key_index < 0) return false;
4548     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown));
4549     return GImGui->IO.KeysDown[user_key_index];
4550 }
4551 
CalcTypematicPressedRepeatAmount(float t,float t_prev,float repeat_delay,float repeat_rate)4552 int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate)
4553 {
4554     if (t == 0.0f)
4555         return 1;
4556     if (t <= repeat_delay || repeat_rate <= 0.0f)
4557         return 0;
4558     const int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t_prev - repeat_delay) / repeat_rate);
4559     return (count > 0) ? count : 0;
4560 }
4561 
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)4562 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4563 {
4564     ImGuiContext& g = *GImGui;
4565     if (key_index < 0) return false;
4566     IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4567     const float t = g.IO.KeysDownDuration[key_index];
4568     return CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, repeat_delay, repeat_rate);
4569 }
4570 
IsKeyPressed(int user_key_index,bool repeat)4571 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4572 {
4573     ImGuiContext& g = *GImGui;
4574     if (user_key_index < 0) return false;
4575     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4576     const float t = g.IO.KeysDownDuration[user_key_index];
4577     if (t == 0.0f)
4578         return true;
4579     if (repeat && t > g.IO.KeyRepeatDelay)
4580         return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4581     return false;
4582 }
4583 
IsKeyReleased(int user_key_index)4584 bool ImGui::IsKeyReleased(int user_key_index)
4585 {
4586     ImGuiContext& g = *GImGui;
4587     if (user_key_index < 0) return false;
4588     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4589     return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4590 }
4591 
IsMouseDown(int button)4592 bool ImGui::IsMouseDown(int button)
4593 {
4594     ImGuiContext& g = *GImGui;
4595     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4596     return g.IO.MouseDown[button];
4597 }
4598 
IsAnyMouseDown()4599 bool ImGui::IsAnyMouseDown()
4600 {
4601     ImGuiContext& g = *GImGui;
4602     for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4603         if (g.IO.MouseDown[n])
4604             return true;
4605     return false;
4606 }
4607 
IsMouseClicked(int button,bool repeat)4608 bool ImGui::IsMouseClicked(int button, bool repeat)
4609 {
4610     ImGuiContext& g = *GImGui;
4611     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4612     const float t = g.IO.MouseDownDuration[button];
4613     if (t == 0.0f)
4614         return true;
4615 
4616     if (repeat && t > g.IO.KeyRepeatDelay)
4617     {
4618         float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
4619         if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
4620             return true;
4621     }
4622 
4623     return false;
4624 }
4625 
IsMouseReleased(int button)4626 bool ImGui::IsMouseReleased(int button)
4627 {
4628     ImGuiContext& g = *GImGui;
4629     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4630     return g.IO.MouseReleased[button];
4631 }
4632 
IsMouseDoubleClicked(int button)4633 bool ImGui::IsMouseDoubleClicked(int button)
4634 {
4635     ImGuiContext& g = *GImGui;
4636     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4637     return g.IO.MouseDoubleClicked[button];
4638 }
4639 
IsMouseDragging(int button,float lock_threshold)4640 bool ImGui::IsMouseDragging(int button, float lock_threshold)
4641 {
4642     ImGuiContext& g = *GImGui;
4643     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4644     if (!g.IO.MouseDown[button])
4645         return false;
4646     if (lock_threshold < 0.0f)
4647         lock_threshold = g.IO.MouseDragThreshold;
4648     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4649 }
4650 
GetMousePos()4651 ImVec2 ImGui::GetMousePos()
4652 {
4653     return GImGui->IO.MousePos;
4654 }
4655 
4656 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4657 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4658 {
4659     ImGuiContext& g = *GImGui;
4660     if (g.CurrentPopupStack.Size > 0)
4661         return g.OpenPopupStack[g.CurrentPopupStack.Size-1].OpenMousePos;
4662     return g.IO.MousePos;
4663 }
4664 
4665 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position
IsMousePosValid(const ImVec2 * mouse_pos)4666 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4667 {
4668     if (mouse_pos == NULL)
4669         mouse_pos = &GImGui->IO.MousePos;
4670     const float MOUSE_INVALID = -256000.0f;
4671     return mouse_pos->x >= MOUSE_INVALID && mouse_pos->y >= MOUSE_INVALID;
4672 }
4673 
4674 // NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window.
GetMouseDragDelta(int button,float lock_threshold)4675 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
4676 {
4677     ImGuiContext& g = *GImGui;
4678     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4679     if (lock_threshold < 0.0f)
4680         lock_threshold = g.IO.MouseDragThreshold;
4681     if (g.IO.MouseDown[button])
4682         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4683             return g.IO.MousePos - g.IO.MouseClickedPos[button];     // Assume we can only get active with left-mouse button (at the moment).
4684     return ImVec2(0.0f, 0.0f);
4685 }
4686 
ResetMouseDragDelta(int button)4687 void ImGui::ResetMouseDragDelta(int button)
4688 {
4689     ImGuiContext& g = *GImGui;
4690     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4691     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4692     g.IO.MouseClickedPos[button] = g.IO.MousePos;
4693 }
4694 
GetMouseCursor()4695 ImGuiMouseCursor ImGui::GetMouseCursor()
4696 {
4697     return GImGui->MouseCursor;
4698 }
4699 
SetMouseCursor(ImGuiMouseCursor cursor_type)4700 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4701 {
4702     GImGui->MouseCursor = cursor_type;
4703 }
4704 
CaptureKeyboardFromApp(bool capture)4705 void ImGui::CaptureKeyboardFromApp(bool capture)
4706 {
4707     GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4708 }
4709 
CaptureMouseFromApp(bool capture)4710 void ImGui::CaptureMouseFromApp(bool capture)
4711 {
4712     GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4713 }
4714 
IsItemActive()4715 bool ImGui::IsItemActive()
4716 {
4717     ImGuiContext& g = *GImGui;
4718     if (g.ActiveId)
4719     {
4720         ImGuiWindow* window = g.CurrentWindow;
4721         return g.ActiveId == window->DC.LastItemId;
4722     }
4723     return false;
4724 }
4725 
IsItemFocused()4726 bool ImGui::IsItemFocused()
4727 {
4728     ImGuiContext& g = *GImGui;
4729     return g.NavId && !g.NavDisableHighlight && g.NavId == g.CurrentWindow->DC.LastItemId;
4730 }
4731 
IsItemClicked(int mouse_button)4732 bool ImGui::IsItemClicked(int mouse_button)
4733 {
4734     return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_Default);
4735 }
4736 
IsAnyItemHovered()4737 bool ImGui::IsAnyItemHovered()
4738 {
4739     ImGuiContext& g = *GImGui;
4740     return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4741 }
4742 
IsAnyItemActive()4743 bool ImGui::IsAnyItemActive()
4744 {
4745     ImGuiContext& g = *GImGui;
4746     return g.ActiveId != 0;
4747 }
4748 
IsAnyItemFocused()4749 bool ImGui::IsAnyItemFocused()
4750 {
4751     ImGuiContext& g = *GImGui;
4752     return g.NavId != 0 && !g.NavDisableHighlight;
4753 }
4754 
IsItemVisible()4755 bool ImGui::IsItemVisible()
4756 {
4757     ImGuiWindow* window = GetCurrentWindowRead();
4758     return window->ClipRect.Overlaps(window->DC.LastItemRect);
4759 }
4760 
4761 // 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()4762 void ImGui::SetItemAllowOverlap()
4763 {
4764     ImGuiContext& g = *GImGui;
4765     if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
4766         g.HoveredIdAllowOverlap = true;
4767     if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
4768         g.ActiveIdAllowOverlap = true;
4769 }
4770 
GetItemRectMin()4771 ImVec2 ImGui::GetItemRectMin()
4772 {
4773     ImGuiWindow* window = GetCurrentWindowRead();
4774     return window->DC.LastItemRect.Min;
4775 }
4776 
GetItemRectMax()4777 ImVec2 ImGui::GetItemRectMax()
4778 {
4779     ImGuiWindow* window = GetCurrentWindowRead();
4780     return window->DC.LastItemRect.Max;
4781 }
4782 
GetItemRectSize()4783 ImVec2 ImGui::GetItemRectSize()
4784 {
4785     ImGuiWindow* window = GetCurrentWindowRead();
4786     return window->DC.LastItemRect.GetSize();
4787 }
4788 
GetViewportRect()4789 static ImRect GetViewportRect()
4790 {
4791     ImGuiContext& g = *GImGui;
4792     if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y)
4793         return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax);
4794     return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4795 }
4796 
4797 // Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first.
BeginTooltipEx(ImGuiWindowFlags extra_flags,bool override_previous_tooltip)4798 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip)
4799 {
4800     ImGuiContext& g = *GImGui;
4801     char window_name[16];
4802     ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
4803     if (override_previous_tooltip)
4804         if (ImGuiWindow* window = FindWindowByName(window_name))
4805             if (window->Active)
4806             {
4807                 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
4808                 window->HiddenFrames = 1;
4809                 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
4810             }
4811     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoNav;
4812     Begin(window_name, NULL, flags | extra_flags);
4813 }
4814 
SetTooltipV(const char * fmt,va_list args)4815 void ImGui::SetTooltipV(const char* fmt, va_list args)
4816 {
4817     BeginTooltipEx(0, true);
4818     TextV(fmt, args);
4819     EndTooltip();
4820 }
4821 
SetTooltip(const char * fmt,...)4822 void ImGui::SetTooltip(const char* fmt, ...)
4823 {
4824     va_list args;
4825     va_start(args, fmt);
4826     SetTooltipV(fmt, args);
4827     va_end(args);
4828 }
4829 
BeginTooltip()4830 void ImGui::BeginTooltip()
4831 {
4832     BeginTooltipEx(0, false);
4833 }
4834 
EndTooltip()4835 void ImGui::EndTooltip()
4836 {
4837     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
4838     End();
4839 }
4840 
4841 // Mark popup as open (toggle toward open state).
4842 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
4843 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
4844 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id)4845 void ImGui::OpenPopupEx(ImGuiID id)
4846 {
4847     ImGuiContext& g = *GImGui;
4848     ImGuiWindow* parent_window = g.CurrentWindow;
4849     int current_stack_size = g.CurrentPopupStack.Size;
4850     ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
4851     popup_ref.PopupId = id;
4852     popup_ref.Window = NULL;
4853     popup_ref.ParentWindow = parent_window;
4854     popup_ref.OpenFrameCount = g.FrameCount;
4855     popup_ref.OpenParentId = parent_window->IDStack.back();
4856     popup_ref.OpenMousePos = g.IO.MousePos;
4857     popup_ref.OpenPopupPos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : g.IO.MousePos;
4858 
4859     //printf("[%05d] OpenPopupEx(0x%08X)\n", g.FrameCount, id);
4860     if (g.OpenPopupStack.Size < current_stack_size + 1)
4861     {
4862         g.OpenPopupStack.push_back(popup_ref);
4863     }
4864     else
4865     {
4866         // Close child popups if any
4867         g.OpenPopupStack.resize(current_stack_size + 1);
4868 
4869         // Gently handle the user mistakenly calling OpenPopup() every frame. It is a programming mistake! However, if we were to run the regular code path, the ui
4870         // would become completely unusable because the popup will always be in hidden-while-calculating-size state _while_ claiming focus. Which would be a very confusing
4871         // situation for the programmer. Instead, we silently allow the popup to proceed, it will keep reappearing and the programming error will be more obvious to understand.
4872         if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
4873             g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
4874         else
4875             g.OpenPopupStack[current_stack_size] = popup_ref;
4876 
4877         // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
4878         // This is equivalent to what ClosePopupToLevel() does.
4879         //if (g.OpenPopupStack[current_stack_size].PopupId == id)
4880         //    FocusWindow(parent_window);
4881     }
4882 }
4883 
OpenPopup(const char * str_id)4884 void ImGui::OpenPopup(const char* str_id)
4885 {
4886     ImGuiContext& g = *GImGui;
4887     OpenPopupEx(g.CurrentWindow->GetID(str_id));
4888 }
4889 
ClosePopupsOverWindow(ImGuiWindow * ref_window)4890 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window)
4891 {
4892     ImGuiContext& g = *GImGui;
4893     if (g.OpenPopupStack.empty())
4894         return;
4895 
4896     // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
4897     // Don't close our own child popup windows.
4898     int n = 0;
4899     if (ref_window)
4900     {
4901         for (n = 0; n < g.OpenPopupStack.Size; n++)
4902         {
4903             ImGuiPopupRef& popup = g.OpenPopupStack[n];
4904             if (!popup.Window)
4905                 continue;
4906             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
4907             if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
4908                 continue;
4909 
4910             // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow)
4911             bool has_focus = false;
4912             for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++)
4913                 has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow);
4914             if (!has_focus)
4915                 break;
4916         }
4917     }
4918     if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the block below
4919         ClosePopupToLevel(n);
4920 }
4921 
GetFrontMostPopupModal()4922 ImGuiWindow* ImGui::GetFrontMostPopupModal()
4923 {
4924     ImGuiContext& g = *GImGui;
4925     for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
4926         if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
4927             if (popup->Flags & ImGuiWindowFlags_Modal)
4928                 return popup;
4929     return NULL;
4930 }
4931 
ClosePopupToLevel(int remaining)4932 static void ClosePopupToLevel(int remaining)
4933 {
4934     IM_ASSERT(remaining >= 0);
4935     ImGuiContext& g = *GImGui;
4936     ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow;
4937     if (g.NavLayer == 0)
4938         focus_window = NavRestoreLastChildNavWindow(focus_window);
4939     ImGui::FocusWindow(focus_window);
4940     focus_window->DC.NavHideHighlightOneFrame = true;
4941     g.OpenPopupStack.resize(remaining);
4942 }
4943 
ClosePopup(ImGuiID id)4944 void ImGui::ClosePopup(ImGuiID id)
4945 {
4946     if (!IsPopupOpen(id))
4947         return;
4948     ImGuiContext& g = *GImGui;
4949     ClosePopupToLevel(g.OpenPopupStack.Size - 1);
4950 }
4951 
4952 // Close the popup we have begin-ed into.
CloseCurrentPopup()4953 void ImGui::CloseCurrentPopup()
4954 {
4955     ImGuiContext& g = *GImGui;
4956     int popup_idx = g.CurrentPopupStack.Size - 1;
4957     if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
4958         return;
4959     while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu))
4960         popup_idx--;
4961     ClosePopupToLevel(popup_idx);
4962 }
4963 
BeginPopupEx(ImGuiID id,ImGuiWindowFlags extra_flags)4964 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
4965 {
4966     ImGuiContext& g = *GImGui;
4967     if (!IsPopupOpen(id))
4968     {
4969         g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
4970         return false;
4971     }
4972 
4973     char name[20];
4974     if (extra_flags & ImGuiWindowFlags_ChildMenu)
4975         ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.CurrentPopupStack.Size);    // Recycle windows based on depth
4976     else
4977         ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
4978 
4979     bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup);
4980     if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
4981         EndPopup();
4982 
4983     return is_open;
4984 }
4985 
BeginPopup(const char * str_id,ImGuiWindowFlags flags)4986 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
4987 {
4988     ImGuiContext& g = *GImGui;
4989     if (g.OpenPopupStack.Size <= g.CurrentPopupStack.Size) // Early out for performance
4990     {
4991         g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
4992         return false;
4993     }
4994     return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
4995 }
4996 
IsPopupOpen(ImGuiID id)4997 bool ImGui::IsPopupOpen(ImGuiID id)
4998 {
4999     ImGuiContext& g = *GImGui;
5000     return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id;
5001 }
5002 
IsPopupOpen(const char * str_id)5003 bool ImGui::IsPopupOpen(const char* str_id)
5004 {
5005     ImGuiContext& g = *GImGui;
5006     return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
5007 }
5008 
BeginPopupModal(const char * name,bool * p_open,ImGuiWindowFlags flags)5009 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
5010 {
5011     ImGuiContext& g = *GImGui;
5012     ImGuiWindow* window = g.CurrentWindow;
5013     const ImGuiID id = window->GetID(name);
5014     if (!IsPopupOpen(id))
5015     {
5016         g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
5017         return false;
5018     }
5019 
5020     // Center modal windows by default
5021     // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
5022     if (g.NextWindowData.PosCond == 0)
5023         SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
5024 
5025     bool is_open = Begin(name, p_open, flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings);
5026     if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
5027     {
5028         EndPopup();
5029         if (is_open)
5030             ClosePopup(id);
5031         return false;
5032     }
5033 
5034     return is_open;
5035 }
5036 
NavProcessMoveRequestWrapAround(ImGuiWindow * window)5037 static void NavProcessMoveRequestWrapAround(ImGuiWindow* window)
5038 {
5039     ImGuiContext& g = *GImGui;
5040     if (g.NavWindow == window && NavMoveRequestButNoResultYet())
5041         if ((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == 0)
5042         {
5043             g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
5044             ImGui::NavMoveRequestCancel();
5045             g.NavWindow->NavRectRel[0].Min.y = g.NavWindow->NavRectRel[0].Max.y = ((g.NavMoveDir == ImGuiDir_Up) ? ImMax(window->SizeFull.y, window->SizeContents.y) : 0.0f) - window->Scroll.y;
5046         }
5047 }
5048 
EndPopup()5049 void ImGui::EndPopup()
5050 {
5051     ImGuiContext& g = *GImGui; (void)g;
5052     IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
5053     IM_ASSERT(g.CurrentPopupStack.Size > 0);
5054 
5055     // Make all menus and popups wrap around for now, may need to expose that policy.
5056     NavProcessMoveRequestWrapAround(g.CurrentWindow);
5057 
5058     End();
5059 }
5060 
OpenPopupOnItemClick(const char * str_id,int mouse_button)5061 bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
5062 {
5063     ImGuiWindow* window = GImGui->CurrentWindow;
5064     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
5065     {
5066         ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
5067         IM_ASSERT(id != 0);                                                  // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
5068         OpenPopupEx(id);
5069         return true;
5070     }
5071     return false;
5072 }
5073 
5074 // This is a helper to handle the simplest case of associating one named popup to one given widget.
5075 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
5076 // You can pass a NULL str_id to use the identifier of the last item.
BeginPopupContextItem(const char * str_id,int mouse_button)5077 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
5078 {
5079     ImGuiWindow* window = GImGui->CurrentWindow;
5080     ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
5081     IM_ASSERT(id != 0);                                                  // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
5082     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
5083         OpenPopupEx(id);
5084     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
5085 }
5086 
BeginPopupContextWindow(const char * str_id,int mouse_button,bool also_over_items)5087 bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
5088 {
5089     if (!str_id)
5090         str_id = "window_context";
5091     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
5092     if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
5093         if (also_over_items || !IsAnyItemHovered())
5094             OpenPopupEx(id);
5095     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
5096 }
5097 
BeginPopupContextVoid(const char * str_id,int mouse_button)5098 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
5099 {
5100     if (!str_id)
5101         str_id = "void_context";
5102     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
5103     if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
5104         OpenPopupEx(id);
5105     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
5106 }
5107 
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)5108 static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5109 {
5110     ImGuiContext& g = *GImGui;
5111     ImGuiWindow* parent_window = ImGui::GetCurrentWindow();
5112     ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
5113     flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove);  // Inherit the NoMove flag
5114 
5115     const ImVec2 content_avail = ImGui::GetContentRegionAvail();
5116     ImVec2 size = ImFloor(size_arg);
5117     const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
5118     if (size.x <= 0.0f)
5119         size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
5120     if (size.y <= 0.0f)
5121         size.y = ImMax(content_avail.y + size.y, 4.0f);
5122 
5123     const float backup_border_size = g.Style.ChildBorderSize;
5124     if (!border)
5125         g.Style.ChildBorderSize = 0.0f;
5126     flags |= extra_flags;
5127 
5128     char title[256];
5129     if (name)
5130         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s", parent_window->Name, name);
5131     else
5132         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
5133 
5134     ImGui::SetNextWindowSize(size);
5135     bool ret = ImGui::Begin(title, NULL, flags);
5136     ImGuiWindow* child_window = ImGui::GetCurrentWindow();
5137     child_window->ChildId = id;
5138     child_window->AutoFitChildAxises = auto_fit_axises;
5139     g.Style.ChildBorderSize = backup_border_size;
5140 
5141     // Process navigation-in immediately so NavInit can run on first frame
5142     if (!(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll) && g.NavActivateId == id)
5143     {
5144         ImGui::FocusWindow(child_window);
5145         ImGui::NavInitWindow(child_window, false);
5146         ImGui::SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
5147         g.ActiveIdSource = ImGuiInputSource_Nav;
5148     }
5149 
5150     return ret;
5151 }
5152 
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)5153 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5154 {
5155     ImGuiWindow* window = GetCurrentWindow();
5156     return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
5157 }
5158 
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)5159 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5160 {
5161     IM_ASSERT(id != 0);
5162     return BeginChildEx(NULL, id, size_arg, border, extra_flags);
5163 }
5164 
EndChild()5165 void ImGui::EndChild()
5166 {
5167     ImGuiContext& g = *GImGui;
5168     ImGuiWindow* window = g.CurrentWindow;
5169 
5170     IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() callss
5171     if (window->BeginCount > 1)
5172     {
5173         End();
5174     }
5175     else
5176     {
5177         // 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.
5178         ImVec2 sz = GetWindowSize();
5179         if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
5180             sz.x = ImMax(4.0f, sz.x);
5181         if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
5182             sz.y = ImMax(4.0f, sz.y);
5183         End();
5184 
5185         ImGuiWindow* parent_window = g.CurrentWindow;
5186         ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
5187         ItemSize(sz);
5188         if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
5189         {
5190             ItemAdd(bb, window->ChildId);
5191             RenderNavHighlight(bb, window->ChildId);
5192 
5193             // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
5194             if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
5195                 RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
5196         }
5197         else
5198         {
5199             // Not navigable into
5200             ItemAdd(bb, 0);
5201         }
5202     }
5203 }
5204 
5205 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)5206 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
5207 {
5208     ImGuiContext& g = *GImGui;
5209     const ImGuiStyle& style = g.Style;
5210     PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
5211     PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
5212     PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
5213     PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
5214     return BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
5215 }
5216 
EndChildFrame()5217 void ImGui::EndChildFrame()
5218 {
5219     EndChild();
5220     PopStyleVar(3);
5221     PopStyleColor();
5222 }
5223 
5224 // Save and compare stack sizes on Begin()/End() to detect usage errors
CheckStacksSize(ImGuiWindow * window,bool write)5225 static void CheckStacksSize(ImGuiWindow* window, bool write)
5226 {
5227     // 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)
5228     ImGuiContext& g = *GImGui;
5229     int* p_backup = &window->DC.StackSizesBackup[0];
5230     { int current = window->IDStack.Size;       if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!");   p_backup++; }    // Too few or too many PopID()/TreePop()
5231     { int current = window->DC.GroupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!");                p_backup++; }    // Too few or too many EndGroup()
5232     { int current = g.CurrentPopupStack.Size;   if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup()
5233     { int current = g.ColorModifiers.Size;      if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleColor/PopStyleColor Mismatch!");       p_backup++; }    // Too few or too many PopStyleColor()
5234     { int current = g.StyleModifiers.Size;      if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleVar/PopStyleVar Mismatch!");           p_backup++; }    // Too few or too many PopStyleVar()
5235     { int current = g.FontStack.Size;           if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushFont/PopFont Mismatch!");                   p_backup++; }    // Too few or too many PopFont()
5236     IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
5237 }
5238 
5239 enum ImGuiPopupPositionPolicy
5240 {
5241     ImGuiPopupPositionPolicy_Default,
5242     ImGuiPopupPositionPolicy_ComboBox
5243 };
5244 
FindScreenRectForWindow(ImGuiWindow * window)5245 static ImRect FindScreenRectForWindow(ImGuiWindow* window)
5246 {
5247     ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
5248     ImRect r_screen = GetViewportRect();
5249     r_screen.Expand(ImVec2((window->Size.x - r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (window->Size.y - r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
5250     return r_screen;
5251 }
5252 
5253 // r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.)
5254 // r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it.
FindBestWindowPosForPopupEx(const ImVec2 & ref_pos,const ImVec2 & size,ImGuiDir * last_dir,const ImRect & r_outer,const ImRect & r_avoid,ImGuiPopupPositionPolicy policy=ImGuiPopupPositionPolicy_Default)5255 static ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy = ImGuiPopupPositionPolicy_Default)
5256 {
5257     ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
5258     //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
5259     //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
5260 
5261     // Combo Box policy (we want a connecting edge)
5262     if (policy == ImGuiPopupPositionPolicy_ComboBox)
5263     {
5264         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
5265         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
5266         {
5267             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
5268             if (n != -1 && dir == *last_dir) // Already tried this direction?
5269                 continue;
5270             ImVec2 pos;
5271             if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)
5272             if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
5273             if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
5274             if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
5275             if (!r_outer.Contains(ImRect(pos, pos + size)))
5276                 continue;
5277             *last_dir = dir;
5278             return pos;
5279         }
5280     }
5281 
5282     // Default popup policy
5283     const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
5284     for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
5285     {
5286         const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
5287         if (n != -1 && dir == *last_dir) // Already tried this direction?
5288             continue;
5289         float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x);
5290         float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y);
5291         if (avail_w < size.x || avail_h < size.y)
5292             continue;
5293         ImVec2 pos;
5294         pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
5295         pos.y = (dir == ImGuiDir_Up)   ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down)  ? r_avoid.Max.y : base_pos_clamped.y;
5296         *last_dir = dir;
5297         return pos;
5298     }
5299 
5300     // Fallback, try to keep within display
5301     *last_dir = ImGuiDir_None;
5302     ImVec2 pos = ref_pos;
5303     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
5304     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
5305     return pos;
5306 }
5307 
FindBestWindowPosForPopup(ImGuiWindow * window)5308 static ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window)
5309 {
5310     ImGuiContext& g = *GImGui;
5311 
5312     ImRect r_screen = FindScreenRectForWindow(window);
5313     if (window->Flags & ImGuiWindowFlags_ChildMenu)
5314     {
5315         // Child menus typically request _any_ position within the parent menu item, and then our FindBestPopupWindowPos() function will move the new menu outside the parent bounds.
5316         // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
5317         IM_ASSERT(g.CurrentWindow == window);
5318         ImGuiWindow* parent_menu = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
5319         float horizontal_overlap = g.Style.ItemSpacing.x;       // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x).
5320         ImRect r_avoid;
5321         if (parent_menu->DC.MenuBarAppending)
5322             r_avoid = ImRect(-FLT_MAX, parent_menu->Pos.y + parent_menu->TitleBarHeight(), FLT_MAX, parent_menu->Pos.y + parent_menu->TitleBarHeight() + parent_menu->MenuBarHeight());
5323         else
5324             r_avoid = ImRect(parent_menu->Pos.x + horizontal_overlap, -FLT_MAX, parent_menu->Pos.x + parent_menu->Size.x - horizontal_overlap - parent_menu->ScrollbarSizes.x, FLT_MAX);
5325         return FindBestWindowPosForPopupEx(window->PosFloat, window->Size, &window->AutoPosLastDirection, r_screen, r_avoid);
5326     }
5327     if (window->Flags & ImGuiWindowFlags_Popup)
5328     {
5329         ImRect r_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1);
5330         return FindBestWindowPosForPopupEx(window->PosFloat, window->Size, &window->AutoPosLastDirection, r_screen, r_avoid);
5331     }
5332     if (window->Flags & ImGuiWindowFlags_Tooltip)
5333     {
5334         // Position tooltip (always follows mouse)
5335         float sc = g.Style.MouseCursorScale;
5336         ImVec2 ref_pos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : g.IO.MousePos;
5337         ImRect r_avoid;
5338         if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
5339             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
5340         else
5341             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
5342         ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_screen, r_avoid);
5343         if (window->AutoPosLastDirection == ImGuiDir_None)
5344             pos = ref_pos + ImVec2(2, 2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
5345         return pos;
5346     }
5347     IM_ASSERT(0);
5348     return window->Pos;
5349 }
5350 
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)5351 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
5352 {
5353     window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);
5354     window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);
5355     window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
5356 }
5357 
FindWindowByName(const char * name)5358 ImGuiWindow* ImGui::FindWindowByName(const char* name)
5359 {
5360     ImGuiContext& g = *GImGui;
5361     ImGuiID id = ImHash(name, 0);
5362     return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
5363 }
5364 
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)5365 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
5366 {
5367     ImGuiContext& g = *GImGui;
5368 
5369     // Create window the first time
5370     ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
5371     window->Flags = flags;
5372     g.WindowsById.SetVoidPtr(window->ID, window);
5373 
5374     // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
5375     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
5376     {
5377         // Retrieve settings from .ini file
5378         // Use SetWindowPos() or SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
5379         window->Pos = window->PosFloat = ImVec2(60, 60);
5380 
5381         if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
5382         {
5383             SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
5384             window->PosFloat = settings->Pos;
5385             window->Pos = ImFloor(window->PosFloat);
5386             window->Collapsed = settings->Collapsed;
5387             if (ImLengthSqr(settings->Size) > 0.00001f)
5388                 size = settings->Size;
5389         }
5390     }
5391     window->Size = window->SizeFull = window->SizeFullAtLastBegin = size;
5392 
5393     if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
5394     {
5395         window->AutoFitFramesX = window->AutoFitFramesY = 2;
5396         window->AutoFitOnlyGrows = false;
5397     }
5398     else
5399     {
5400         if (window->Size.x <= 0.0f)
5401             window->AutoFitFramesX = 2;
5402         if (window->Size.y <= 0.0f)
5403             window->AutoFitFramesY = 2;
5404         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
5405     }
5406 
5407     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
5408         g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once
5409     else
5410         g.Windows.push_back(window);
5411     return window;
5412 }
5413 
CalcSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)5414 static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
5415 {
5416     ImGuiContext& g = *GImGui;
5417     if (g.NextWindowData.SizeConstraintCond != 0)
5418     {
5419         // Using -1,-1 on either X/Y axis to preserve the current size.
5420         ImRect cr = g.NextWindowData.SizeConstraintRect;
5421         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
5422         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
5423         if (g.NextWindowData.SizeCallback)
5424         {
5425             ImGuiSizeCallbackData data;
5426             data.UserData = g.NextWindowData.SizeCallbackUserData;
5427             data.Pos = window->Pos;
5428             data.CurrentSize = window->SizeFull;
5429             data.DesiredSize = new_size;
5430             g.NextWindowData.SizeCallback(&data);
5431             new_size = data.DesiredSize;
5432         }
5433     }
5434 
5435     // Minimum size
5436     if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
5437     {
5438         new_size = ImMax(new_size, g.Style.WindowMinSize);
5439         new_size.y = ImMax(new_size.y, window->TitleBarHeight() + window->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
5440     }
5441     return new_size;
5442 }
5443 
CalcSizeContents(ImGuiWindow * window)5444 static ImVec2 CalcSizeContents(ImGuiWindow* window)
5445 {
5446     ImVec2 sz;
5447     sz.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : (window->DC.CursorMaxPos.x - window->Pos.x + window->Scroll.x));
5448     sz.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : (window->DC.CursorMaxPos.y - window->Pos.y + window->Scroll.y));
5449     return sz + window->WindowPadding;
5450 }
5451 
CalcSizeAutoFit(ImGuiWindow * window,const ImVec2 & size_contents)5452 static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents)
5453 {
5454     ImGuiContext& g = *GImGui;
5455     ImGuiStyle& style = g.Style;
5456     ImGuiWindowFlags flags = window->Flags;
5457     ImVec2 size_auto_fit;
5458     if ((flags & ImGuiWindowFlags_Tooltip) != 0)
5459     {
5460         // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose.
5461         size_auto_fit = size_contents;
5462     }
5463     else
5464     {
5465         // When the window cannot fit all contents (either because of constraints, either because screen is too small): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
5466         size_auto_fit = ImClamp(size_contents, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding));
5467         ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit);
5468         if (size_auto_fit_after_constraint.x < size_contents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar))
5469             size_auto_fit.y += style.ScrollbarSize;
5470         if (size_auto_fit_after_constraint.y < size_contents.y && !(flags & ImGuiWindowFlags_NoScrollbar))
5471             size_auto_fit.x += style.ScrollbarSize;
5472     }
5473     return size_auto_fit;
5474 }
5475 
GetScrollMaxX(ImGuiWindow * window)5476 static float GetScrollMaxX(ImGuiWindow* window)
5477 {
5478     return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x));
5479 }
5480 
GetScrollMaxY(ImGuiWindow * window)5481 static float GetScrollMaxY(ImGuiWindow* window)
5482 {
5483     return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y));
5484 }
5485 
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window)5486 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
5487 {
5488     ImVec2 scroll = window->Scroll;
5489     float cr_x = window->ScrollTargetCenterRatio.x;
5490     float cr_y = window->ScrollTargetCenterRatio.y;
5491     if (window->ScrollTarget.x < FLT_MAX)
5492         scroll.x = window->ScrollTarget.x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
5493     if (window->ScrollTarget.y < FLT_MAX)
5494         scroll.y = window->ScrollTarget.y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y);
5495     scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
5496     if (!window->Collapsed && !window->SkipItems)
5497     {
5498         scroll.x = ImMin(scroll.x, GetScrollMaxX(window));
5499         scroll.y = ImMin(scroll.y, GetScrollMaxY(window));
5500     }
5501     return scroll;
5502 }
5503 
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)5504 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
5505 {
5506     if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
5507         return ImGuiCol_PopupBg;
5508     if (flags & ImGuiWindowFlags_ChildWindow)
5509         return ImGuiCol_ChildBg;
5510     return ImGuiCol_WindowBg;
5511 }
5512 
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)5513 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
5514 {
5515     ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left
5516     ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
5517     ImVec2 size_expected = pos_max - pos_min;
5518     ImVec2 size_constrained = CalcSizeAfterConstraint(window, size_expected);
5519     *out_pos = pos_min;
5520     if (corner_norm.x == 0.0f)
5521         out_pos->x -= (size_constrained.x - size_expected.x);
5522     if (corner_norm.y == 0.0f)
5523         out_pos->y -= (size_constrained.y - size_expected.y);
5524     *out_size = size_constrained;
5525 }
5526 
5527 struct ImGuiResizeGripDef
5528 {
5529     ImVec2  CornerPos;
5530     ImVec2  InnerDir;
5531     int     AngleMin12, AngleMax12;
5532 };
5533 
5534 const ImGuiResizeGripDef resize_grip_def[4] =
5535 {
5536     { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right
5537     { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left
5538     { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left
5539     { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right
5540 };
5541 
GetBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)5542 static ImRect GetBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
5543 {
5544     ImRect rect = window->Rect();
5545     if (thickness == 0.0f) rect.Max -= ImVec2(1,1);
5546     if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y,                rect.Max.x - perp_padding, rect.Min.y + thickness);
5547     if (border_n == 1) return ImRect(rect.Max.x - thickness,    rect.Min.y + perp_padding, rect.Max.x,                rect.Max.y - perp_padding);
5548     if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness,    rect.Max.x - perp_padding, rect.Max.y);
5549     if (border_n == 3) return ImRect(rect.Min.x,                rect.Min.y + perp_padding, rect.Min.x + thickness,    rect.Max.y - perp_padding);
5550     IM_ASSERT(0);
5551     return ImRect();
5552 }
5553 
5554 // Handle resize for: Resize Grips, Borders, Gamepad
UpdateManualResize(ImGuiWindow * window,const ImVec2 & size_auto_fit,int * border_held,int resize_grip_count,ImU32 resize_grip_col[4])5555 static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4])
5556 {
5557     ImGuiContext& g = *GImGui;
5558     ImGuiWindowFlags flags = window->Flags;
5559     if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5560         return;
5561 
5562     const int resize_border_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 4 : 0;
5563     const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
5564     const float grip_hover_size = (float)(int)(grip_draw_size * 0.75f);
5565 
5566     ImVec2 pos_target(FLT_MAX, FLT_MAX);
5567     ImVec2 size_target(FLT_MAX, FLT_MAX);
5568 
5569     // Manual resize grips
5570     PushID("#RESIZE");
5571     for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5572     {
5573         const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5574         const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos);
5575 
5576         // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
5577         ImRect resize_rect(corner, corner + grip.InnerDir * grip_hover_size);
5578         resize_rect.FixInverted();
5579         bool hovered, held;
5580         ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
5581         if (hovered || held)
5582             g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
5583 
5584         if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
5585         {
5586             // Manual auto-fit when double-clicking
5587             size_target = CalcSizeAfterConstraint(window, size_auto_fit);
5588             ClearActiveID();
5589         }
5590         else if (held)
5591         {
5592             // Resize from any of the four corners
5593             // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
5594             ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerPos; // Corner of the window corresponding to our corner grip
5595             CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target);
5596         }
5597         if (resize_grip_n == 0 || held || hovered)
5598             resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
5599     }
5600     for (int border_n = 0; border_n < resize_border_count; border_n++)
5601     {
5602         const float BORDER_SIZE = 5.0f;          // FIXME: Only works _inside_ window because of HoveredWindow check.
5603         const float BORDER_APPEAR_TIMER = 0.05f; // Reduce visual noise
5604         bool hovered, held;
5605         ImRect border_rect = GetBorderRect(window, border_n, grip_hover_size, BORDER_SIZE);
5606         ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
5607         if ((hovered && g.HoveredIdTimer > BORDER_APPEAR_TIMER) || held)
5608         {
5609             g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
5610             if (held) *border_held = border_n;
5611         }
5612         if (held)
5613         {
5614             ImVec2 border_target = window->Pos;
5615             ImVec2 border_posn;
5616             if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y); }
5617             if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + BORDER_SIZE); }
5618             if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + BORDER_SIZE); }
5619             if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x); }
5620             CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
5621         }
5622     }
5623     PopID();
5624 
5625     // Navigation resize (keyboard/gamepad)
5626     if (g.NavWindowingTarget == window)
5627     {
5628         ImVec2 nav_resize_delta;
5629         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
5630             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
5631         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
5632             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
5633         if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
5634         {
5635             const float NAV_RESIZE_SPEED = 600.0f;
5636             nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
5637             g.NavWindowingToggleLayer = false;
5638             g.NavDisableMouseHover = true;
5639             resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
5640             // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
5641             size_target = CalcSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
5642         }
5643     }
5644 
5645     // Apply back modified position/size to window
5646     if (size_target.x != FLT_MAX)
5647     {
5648         window->SizeFull = size_target;
5649         MarkIniSettingsDirty(window);
5650     }
5651     if (pos_target.x != FLT_MAX)
5652     {
5653         window->Pos = window->PosFloat = ImFloor(pos_target);
5654         MarkIniSettingsDirty(window);
5655     }
5656 
5657     window->Size = window->SizeFull;
5658 }
5659 
5660 // Push a new ImGui window to add widgets to.
5661 // - 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.
5662 // - Begin/End can be called multiple times during the frame with the same window name to append content.
5663 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
5664 //   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.
5665 // - 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.
5666 // - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed.
Begin(const char * name,bool * p_open,ImGuiWindowFlags flags)5667 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
5668 {
5669     ImGuiContext& g = *GImGui;
5670     const ImGuiStyle& style = g.Style;
5671     IM_ASSERT(name != NULL);                        // Window name required
5672     IM_ASSERT(g.Initialized);                       // Forgot to call ImGui::NewFrame()
5673     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
5674 
5675     // Find or create
5676     ImGuiWindow* window = FindWindowByName(name);
5677     const bool window_just_created = (window == NULL);
5678     if (window_just_created)
5679     {
5680         ImVec2 size_on_first_use = (g.NextWindowData.SizeCond != 0) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here.
5681         window = CreateNewWindow(name, size_on_first_use, flags);
5682     }
5683 
5684     // Automatically disable manual moving/resizing when NoInputs is set
5685     if (flags & ImGuiWindowFlags_NoInputs)
5686         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
5687 
5688     if (flags & ImGuiWindowFlags_NavFlattened)
5689         IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
5690 
5691     const int current_frame = g.FrameCount;
5692     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
5693     if (first_begin_of_the_frame)
5694         window->Flags = (ImGuiWindowFlags)flags;
5695     else
5696         flags = window->Flags;
5697 
5698     // Update the Appearing flag
5699     bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
5700     const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames > 0);
5701     if (flags & ImGuiWindowFlags_Popup)
5702     {
5703         ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
5704         window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
5705         window_just_activated_by_user |= (window != popup_ref.Window);
5706     }
5707     window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
5708     window->CloseButton = (p_open != NULL);
5709     if (window->Appearing)
5710         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
5711 
5712     // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack
5713     ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
5714     ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
5715     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
5716 
5717     // Add to stack
5718     g.CurrentWindowStack.push_back(window);
5719     SetCurrentWindow(window);
5720     CheckStacksSize(window, true);
5721     if (flags & ImGuiWindowFlags_Popup)
5722     {
5723         ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
5724         popup_ref.Window = window;
5725         g.CurrentPopupStack.push_back(popup_ref);
5726         window->PopupId = popup_ref.PopupId;
5727     }
5728 
5729     if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
5730         window->NavLastIds[0] = 0;
5731 
5732     // Process SetNextWindow***() calls
5733     bool window_pos_set_by_api = false;
5734     bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
5735     if (g.NextWindowData.PosCond)
5736     {
5737         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
5738         if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
5739         {
5740             // May be processed on the next frame if this is our first frame and we are measuring size
5741             // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
5742             window->SetWindowPosVal = g.NextWindowData.PosVal;
5743             window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
5744             window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5745         }
5746         else
5747         {
5748             SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
5749         }
5750         g.NextWindowData.PosCond = 0;
5751     }
5752     if (g.NextWindowData.SizeCond)
5753     {
5754         window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
5755         window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
5756         SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
5757         g.NextWindowData.SizeCond = 0;
5758     }
5759     if (g.NextWindowData.ContentSizeCond)
5760     {
5761         // Adjust passed "client size" to become a "window size"
5762         window->SizeContentsExplicit = g.NextWindowData.ContentSizeVal;
5763         if (window->SizeContentsExplicit.y != 0.0f)
5764             window->SizeContentsExplicit.y += window->TitleBarHeight() + window->MenuBarHeight();
5765         g.NextWindowData.ContentSizeCond = 0;
5766     }
5767     else if (first_begin_of_the_frame)
5768     {
5769         window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
5770     }
5771     if (g.NextWindowData.CollapsedCond)
5772     {
5773         SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
5774         g.NextWindowData.CollapsedCond = 0;
5775     }
5776     if (g.NextWindowData.FocusCond)
5777     {
5778         SetWindowFocus();
5779         g.NextWindowData.FocusCond = 0;
5780     }
5781     if (window->Appearing)
5782         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
5783 
5784     // When reusing window again multiple times a frame, just append content (don't need to setup again)
5785     if (first_begin_of_the_frame)
5786     {
5787         const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
5788 
5789         // Initialize
5790         window->ParentWindow = parent_window;
5791         window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForTabbing = window->RootWindowForNav = window;
5792         if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !window_is_child_tooltip)
5793             window->RootWindow = parent_window->RootWindow;
5794         if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
5795             window->RootWindowForTitleBarHighlight = window->RootWindowForTabbing = parent_window->RootWindowForTitleBarHighlight; // Same value in master branch, will differ for docking
5796         while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
5797             window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
5798 
5799         window->Active = true;
5800         window->BeginOrderWithinParent = 0;
5801         window->BeginOrderWithinContext = g.WindowsActiveCount++;
5802         window->BeginCount = 0;
5803         window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
5804         window->LastFrameActive = current_frame;
5805         window->IDStack.resize(1);
5806 
5807         // Lock window rounding, border size and rounding so that altering the border sizes for children doesn't have side-effects.
5808         window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
5809         window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
5810         window->WindowPadding = style.WindowPadding;
5811         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
5812             window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
5813 
5814         // Collapse window by double-clicking on title bar
5815         // 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
5816         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
5817         {
5818             ImRect title_bar_rect = window->TitleBarRect();
5819             if (window->CollapseToggleWanted || (g.HoveredWindow == window && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0]))
5820             {
5821                 window->Collapsed = !window->Collapsed;
5822                 MarkIniSettingsDirty(window);
5823                 FocusWindow(window);
5824             }
5825         }
5826         else
5827         {
5828             window->Collapsed = false;
5829         }
5830         window->CollapseToggleWanted = false;
5831 
5832         // SIZE
5833 
5834         // Update contents size from last frame for auto-fitting (unless explicitly specified)
5835         window->SizeContents = CalcSizeContents(window);
5836 
5837         // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
5838         if (window->HiddenFrames > 0)
5839             window->HiddenFrames--;
5840         if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
5841         {
5842             window->HiddenFrames = 1;
5843             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
5844             {
5845                 if (!window_size_x_set_by_api)
5846                     window->Size.x = window->SizeFull.x = 0.f;
5847                 if (!window_size_y_set_by_api)
5848                     window->Size.y = window->SizeFull.y = 0.f;
5849                 window->SizeContents = ImVec2(0.f, 0.f);
5850             }
5851         }
5852 
5853         // Hide new windows for one frame until they calculate their size
5854         if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
5855             window->HiddenFrames = 1;
5856 
5857         // Calculate auto-fit size, handle automatic resize
5858         const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->SizeContents);
5859         ImVec2 size_full_modified(FLT_MAX, FLT_MAX);
5860         if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
5861         {
5862             // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
5863             if (!window_size_x_set_by_api)
5864                 window->SizeFull.x = size_full_modified.x = size_auto_fit.x;
5865             if (!window_size_y_set_by_api)
5866                 window->SizeFull.y = size_full_modified.y = size_auto_fit.y;
5867         }
5868         else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5869         {
5870             // Auto-fit may only grow window during the first few frames
5871             // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
5872             if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
5873                 window->SizeFull.x = size_full_modified.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
5874             if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
5875                 window->SizeFull.y = size_full_modified.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
5876             if (!window->Collapsed)
5877                 MarkIniSettingsDirty(window);
5878         }
5879 
5880         // Apply minimum/maximum window size constraints and final size
5881         window->SizeFull = CalcSizeAfterConstraint(window, window->SizeFull);
5882         window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
5883 
5884         // SCROLLBAR STATUS
5885 
5886         // Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size).
5887         if (!window->Collapsed)
5888         {
5889             // When reading the current size we need to read it after size constraints have been applied
5890             float size_x_for_scrollbars = size_full_modified.x != FLT_MAX ? window->SizeFull.x : window->SizeFullAtLastBegin.x;
5891             float size_y_for_scrollbars = size_full_modified.y != FLT_MAX ? window->SizeFull.y : window->SizeFullAtLastBegin.y;
5892             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
5893             window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
5894             if (window->ScrollbarX && !window->ScrollbarY)
5895                 window->ScrollbarY = (window->SizeContents.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
5896             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
5897         }
5898 
5899         // POSITION
5900 
5901         // Popup latch its initial position, will position itself when it appears next frame
5902         if (window_just_activated_by_user)
5903         {
5904             window->AutoPosLastDirection = ImGuiDir_None;
5905             if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
5906                 window->Pos = window->PosFloat = g.CurrentPopupStack.back().OpenPopupPos;
5907         }
5908 
5909         // Position child window
5910         if (flags & ImGuiWindowFlags_ChildWindow)
5911         {
5912             window->BeginOrderWithinParent = parent_window->DC.ChildWindows.Size;
5913             parent_window->DC.ChildWindows.push_back(window);
5914             if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
5915                 window->Pos = window->PosFloat = parent_window->DC.CursorPos;
5916         }
5917 
5918         const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFrames == 0);
5919         if (window_pos_with_pivot)
5920             SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0); // Position given a pivot (e.g. for centering)
5921         else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
5922             window->PosFloat = FindBestWindowPosForPopup(window);
5923         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
5924             window->PosFloat = FindBestWindowPosForPopup(window);
5925         else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
5926             window->PosFloat = FindBestWindowPosForPopup(window);
5927 
5928         // Clamp position so it stays visible
5929         if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
5930         {
5931             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.
5932             {
5933                 ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
5934                 window->PosFloat = ImMax(window->PosFloat + window->Size, padding) - window->Size;
5935                 window->PosFloat = ImMin(window->PosFloat, g.IO.DisplaySize - padding);
5936             }
5937         }
5938         window->Pos = ImFloor(window->PosFloat);
5939 
5940         // Default item width. Make it proportional to window size if window manually resizes
5941         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
5942             window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
5943         else
5944             window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
5945 
5946         // Prepare for focus requests
5947         window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1);
5948         window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1);
5949         window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1;
5950         window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX;
5951 
5952         // Apply scrolling
5953         window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
5954         window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
5955 
5956         // Apply focus, new windows appears in front
5957         bool want_focus = false;
5958         if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
5959             if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
5960                 want_focus = true;
5961 
5962         // Handle manual resize: Resize Grips, Borders, Gamepad
5963         int border_held = -1;
5964         ImU32 resize_grip_col[4] = { 0 };
5965         const int resize_grip_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 2 : 1; // 4
5966         const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
5967         if (!window->Collapsed)
5968             UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]);
5969 
5970         // DRAWING
5971 
5972         // Setup draw list and outer clipping rectangle
5973         window->DrawList->Clear();
5974         window->DrawList->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
5975         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
5976         ImRect viewport_rect(GetViewportRect());
5977         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
5978             PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
5979         else
5980             PushClipRect(viewport_rect.Min, viewport_rect.Max, true);
5981 
5982         // Draw modal window background (darkens what is behind them)
5983         if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostPopupModal())
5984             window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio));
5985 
5986         // Draw navigation selection/windowing rectangle background
5987         if (g.NavWindowingTarget == window)
5988         {
5989             ImRect bb = window->Rect();
5990             bb.Expand(g.FontSize);
5991             if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
5992                 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
5993         }
5994 
5995         // Draw window + handle manual resize
5996         const float window_rounding = window->WindowRounding;
5997         const float window_border_size = window->WindowBorderSize;
5998         const bool title_bar_is_highlight = want_focus || (g.NavWindow && window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
5999         const ImRect title_bar_rect = window->TitleBarRect();
6000         if (window->Collapsed)
6001         {
6002             // Title bar only
6003             float backup_border_size = style.FrameBorderSize;
6004             g.Style.FrameBorderSize = window->WindowBorderSize;
6005             ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
6006             RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
6007             g.Style.FrameBorderSize = backup_border_size;
6008         }
6009         else
6010         {
6011             // Window background
6012             ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
6013             if (g.NextWindowData.BgAlphaCond != 0)
6014             {
6015                 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT);
6016                 g.NextWindowData.BgAlphaCond = 0;
6017             }
6018             window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
6019 
6020             // Title bar
6021             ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
6022             if (!(flags & ImGuiWindowFlags_NoTitleBar))
6023                 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
6024 
6025             // Menu bar
6026             if (flags & ImGuiWindowFlags_MenuBar)
6027             {
6028                 ImRect menu_bar_rect = window->MenuBarRect();
6029                 menu_bar_rect.ClipWith(window->Rect());  // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them.
6030                 window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top);
6031                 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
6032                     window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
6033             }
6034 
6035             // Scrollbars
6036             if (window->ScrollbarX)
6037                 Scrollbar(ImGuiLayoutType_Horizontal);
6038             if (window->ScrollbarY)
6039                 Scrollbar(ImGuiLayoutType_Vertical);
6040 
6041             // Render resize grips (after their input handling so we don't have a frame of latency)
6042             if (!(flags & ImGuiWindowFlags_NoResize))
6043             {
6044                 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
6045                 {
6046                     const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
6047                     const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos);
6048                     window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, grip_draw_size) : ImVec2(grip_draw_size, window_border_size)));
6049                     window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(grip_draw_size, window_border_size) : ImVec2(window_border_size, grip_draw_size)));
6050                     window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12);
6051                     window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
6052                 }
6053             }
6054 
6055             // Borders
6056             if (window_border_size > 0.0f)
6057                 window->DrawList->AddRect(window->Pos, window->Pos+window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size);
6058             if (border_held != -1)
6059             {
6060                 ImRect border = GetBorderRect(window, border_held, grip_draw_size, 0.0f);
6061                 window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size));
6062             }
6063             if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar))
6064                 window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize,-1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
6065         }
6066 
6067         // Draw navigation selection/windowing rectangle border
6068         if (g.NavWindowingTarget == window)
6069         {
6070             float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
6071             ImRect bb = window->Rect();
6072             bb.Expand(g.FontSize);
6073             if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
6074             {
6075                 bb.Expand(-g.FontSize - 1.0f);
6076                 rounding = window->WindowRounding;
6077             }
6078             window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
6079         }
6080 
6081         // Store a backup of SizeFull which we will use next frame to decide if we need scrollbars.
6082         window->SizeFullAtLastBegin = window->SizeFull;
6083 
6084         // Update ContentsRegionMax. All the variable it depends on are set above in this function.
6085         window->ContentsRegionRect.Min.x = -window->Scroll.x + window->WindowPadding.x;
6086         window->ContentsRegionRect.Min.y = -window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
6087         window->ContentsRegionRect.Max.x = -window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x));
6088         window->ContentsRegionRect.Max.y = -window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y));
6089 
6090         // Setup drawing context
6091         // (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.)
6092         window->DC.IndentX = 0.0f + window->WindowPadding.x - window->Scroll.x;
6093         window->DC.GroupOffsetX = 0.0f;
6094         window->DC.ColumnsOffsetX = 0.0f;
6095         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.IndentX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y);
6096         window->DC.CursorPos = window->DC.CursorStartPos;
6097         window->DC.CursorPosPrevLine = window->DC.CursorPos;
6098         window->DC.CursorMaxPos = window->DC.CursorStartPos;
6099         window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f;
6100         window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
6101         window->DC.NavHideHighlightOneFrame = false;
6102         window->DC.NavHasScroll = (GetScrollMaxY() > 0.0f);
6103         window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
6104         window->DC.NavLayerActiveMaskNext = 0x00;
6105         window->DC.MenuBarAppending = false;
6106         window->DC.MenuBarOffsetX = ImMax(window->WindowPadding.x, style.ItemSpacing.x);
6107         window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
6108         window->DC.ChildWindows.resize(0);
6109         window->DC.LayoutType = ImGuiLayoutType_Vertical;
6110         window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
6111         window->DC.ItemFlags = ImGuiItemFlags_Default_;
6112         window->DC.ItemWidth = window->ItemWidthDefault;
6113         window->DC.TextWrapPos = -1.0f; // disabled
6114         window->DC.ItemFlagsStack.resize(0);
6115         window->DC.ItemWidthStack.resize(0);
6116         window->DC.TextWrapPosStack.resize(0);
6117         window->DC.ColumnsSet = NULL;
6118         window->DC.TreeDepth = 0;
6119         window->DC.TreeDepthMayJumpToParentOnPop = 0x00;
6120         window->DC.StateStorage = &window->StateStorage;
6121         window->DC.GroupStack.resize(0);
6122         window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
6123 
6124         if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
6125         {
6126             window->DC.ItemFlags = parent_window->DC.ItemFlags;
6127             window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
6128         }
6129 
6130         if (window->AutoFitFramesX > 0)
6131             window->AutoFitFramesX--;
6132         if (window->AutoFitFramesY > 0)
6133             window->AutoFitFramesY--;
6134 
6135         // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
6136         if (want_focus)
6137         {
6138             FocusWindow(window);
6139             NavInitWindow(window, false);
6140         }
6141 
6142         // Title bar
6143         if (!(flags & ImGuiWindowFlags_NoTitleBar))
6144         {
6145             // Close & collapse button are on layer 1 (same as menus) and don't default focus
6146             const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
6147             window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
6148             window->DC.NavLayerCurrent++;
6149             window->DC.NavLayerCurrentMask <<= 1;
6150 
6151             // Collapse button
6152             if (!(flags & ImGuiWindowFlags_NoCollapse))
6153             {
6154                 ImGuiID id = window->GetID("#COLLAPSE");
6155                 ImRect bb(window->Pos + style.FramePadding + ImVec2(1,1), window->Pos + style.FramePadding + ImVec2(g.FontSize,g.FontSize) - ImVec2(1,1));
6156                 ItemAdd(bb, id); // To allow navigation
6157                 if (ButtonBehavior(bb, id, NULL, NULL))
6158                     window->CollapseToggleWanted = true; // Defer collapsing to next frame as we are too far in the Begin() function
6159                 RenderNavHighlight(bb, id);
6160                 RenderArrow(window->Pos + style.FramePadding, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f);
6161             }
6162 
6163             // Close button
6164             if (p_open != NULL)
6165             {
6166                 const float pad = style.FramePadding.y;
6167                 const float rad = g.FontSize * 0.5f;
6168                 if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad + 1))
6169                     *p_open = false;
6170             }
6171 
6172             window->DC.NavLayerCurrent--;
6173             window->DC.NavLayerCurrentMask >>= 1;
6174             window->DC.ItemFlags = item_flags_backup;
6175 
6176             // Title text (FIXME: refactor text alignment facilities along with RenderText helpers)
6177             ImVec2 text_size = CalcTextSize(name, NULL, true);
6178             ImRect text_r = title_bar_rect;
6179             float pad_left = (flags & ImGuiWindowFlags_NoCollapse) == 0 ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x;
6180             float pad_right = (p_open != NULL) ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x;
6181             if (style.WindowTitleAlign.x > 0.0f) pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x);
6182             text_r.Min.x += pad_left;
6183             text_r.Max.x -= pad_right;
6184             ImRect clip_rect = text_r;
6185             clip_rect.Max.x = window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x); // Match the size of CloseButton()
6186             RenderTextClipped(text_r.Min, text_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect);
6187         }
6188 
6189         // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
6190         window->WindowRectClipped = window->Rect();
6191         window->WindowRectClipped.ClipWith(window->ClipRect);
6192 
6193         // Pressing CTRL+C while holding on a window copy its content to the clipboard
6194         // 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.
6195         // Maybe we can support CTRL+C on every element?
6196         /*
6197         if (g.ActiveId == move_id)
6198             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
6199                 ImGui::LogToClipboard();
6200         */
6201 
6202         // Inner rectangle
6203         // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
6204         // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior.
6205         window->InnerRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize;
6206         window->InnerRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
6207         window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - window->WindowBorderSize;
6208         window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y - window->WindowBorderSize;
6209         //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE);
6210 
6211         // Inner clipping rectangle
6212         // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
6213         window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
6214         window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y);
6215         window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
6216         window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y);
6217 
6218         // After Begin() we fill the last item / hovered data using the title bar data. Make that a standard behavior (to allow usage of context menus on title bar only, etc.).
6219         window->DC.LastItemId = window->MoveId;
6220         window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
6221         window->DC.LastItemRect = title_bar_rect;
6222     }
6223 
6224     PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
6225 
6226     // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused)
6227     if (first_begin_of_the_frame)
6228         window->WriteAccessed = false;
6229 
6230     window->BeginCount++;
6231     g.NextWindowData.SizeConstraintCond = 0;
6232 
6233     // Child window can be out of sight and have "negative" clip windows.
6234     // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar).
6235     if (flags & ImGuiWindowFlags_ChildWindow)
6236     {
6237         IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
6238         window->Collapsed = parent_window && parent_window->Collapsed;
6239 
6240         if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
6241             window->Collapsed |= (window->WindowRectClipped.Min.x >= window->WindowRectClipped.Max.x || window->WindowRectClipped.Min.y >= window->WindowRectClipped.Max.y);
6242 
6243         // We also hide the window from rendering because we've already added its border to the command list.
6244         // (we could perform the check earlier in the function but it is simpler at this point)
6245         if (window->Collapsed)
6246             window->Active = false;
6247     }
6248     if (style.Alpha <= 0.0f)
6249         window->Active = false;
6250 
6251     // Return false if we don't intend to display anything to allow user to perform an early out optimization
6252     window->SkipItems = (window->Collapsed || !window->Active) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0;
6253     return !window->SkipItems;
6254 }
6255 
6256 // Old Begin() API with 5 parameters, avoid calling this version directly! Use SetNextWindowSize()/SetNextWindowBgAlpha() + Begin() instead.
6257 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
Begin(const char * name,bool * p_open,const ImVec2 & size_first_use,float bg_alpha_override,ImGuiWindowFlags flags)6258 bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_first_use, float bg_alpha_override, ImGuiWindowFlags flags)
6259 {
6260     // Old API feature: we could pass the initial window size as a parameter. This was misleading because it only had an effect if the window didn't have data in the .ini file.
6261     if (size_first_use.x != 0.0f || size_first_use.y != 0.0f)
6262         ImGui::SetNextWindowSize(size_first_use, ImGuiCond_FirstUseEver);
6263 
6264     // Old API feature: override the window background alpha with a parameter.
6265     if (bg_alpha_override >= 0.0f)
6266         ImGui::SetNextWindowBgAlpha(bg_alpha_override);
6267 
6268     return ImGui::Begin(name, p_open, flags);
6269 }
6270 #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
6271 
End()6272 void ImGui::End()
6273 {
6274     ImGuiContext& g = *GImGui;
6275     ImGuiWindow* window = g.CurrentWindow;
6276 
6277     if (window->DC.ColumnsSet != NULL)
6278         EndColumns();
6279     PopClipRect();   // Inner window clip rectangle
6280 
6281     // Stop logging
6282     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
6283         LogFinish();
6284 
6285     // Pop from window stack
6286     g.CurrentWindowStack.pop_back();
6287     if (window->Flags & ImGuiWindowFlags_Popup)
6288         g.CurrentPopupStack.pop_back();
6289     CheckStacksSize(window, false);
6290     SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
6291 }
6292 
6293 // Vertical scrollbar
6294 // The entire piece of code below is rather confusing because:
6295 // - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
6296 // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar
6297 // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.
Scrollbar(ImGuiLayoutType direction)6298 void ImGui::Scrollbar(ImGuiLayoutType direction)
6299 {
6300     ImGuiContext& g = *GImGui;
6301     ImGuiWindow* window = g.CurrentWindow;
6302 
6303     const bool horizontal = (direction == ImGuiLayoutType_Horizontal);
6304     const ImGuiStyle& style = g.Style;
6305     const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY");
6306 
6307     // Render background
6308     bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX);
6309     float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f;
6310     const ImRect window_rect = window->Rect();
6311     const float border_size = window->WindowBorderSize;
6312     ImRect bb = horizontal
6313         ? 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)
6314         : 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);
6315     if (!horizontal)
6316         bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f);
6317     if (bb.GetWidth() <= 0.0f || bb.GetHeight() <= 0.0f)
6318         return;
6319 
6320     int window_rounding_corners;
6321     if (horizontal)
6322         window_rounding_corners = ImDrawCornerFlags_BotLeft | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight);
6323     else
6324         window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0) | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight);
6325     window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, window_rounding_corners);
6326     bb.Expand(ImVec2(-ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f)));
6327 
6328     // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
6329     float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight();
6330     float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y;
6331     float win_size_avail_v = (horizontal ? window->SizeFull.x : window->SizeFull.y) - other_scrollbar_size_w;
6332     float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y;
6333 
6334     // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount)
6335     // But we maintain a minimum size in pixel to allow for the user to still aim inside.
6336     IM_ASSERT(ImMax(win_size_contents_v, win_size_avail_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers.
6337     const float win_size_v = ImMax(ImMax(win_size_contents_v, win_size_avail_v), 1.0f);
6338     const float grab_h_pixels = ImClamp(scrollbar_size_v * (win_size_avail_v / win_size_v), style.GrabMinSize, scrollbar_size_v);
6339     const float grab_h_norm = grab_h_pixels / scrollbar_size_v;
6340 
6341     // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().
6342     bool held = false;
6343     bool hovered = false;
6344     const bool previously_held = (g.ActiveId == id);
6345     ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus);
6346 
6347     float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v);
6348     float scroll_ratio = ImSaturate(scroll_v / scroll_max);
6349     float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
6350     if (held && grab_h_norm < 1.0f)
6351     {
6352         float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y;
6353         float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
6354         float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y;
6355 
6356         // Click position in scrollbar normalized space (0.0f->1.0f)
6357         const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
6358         SetHoveredID(id);
6359 
6360         bool seek_absolute = false;
6361         if (!previously_held)
6362         {
6363             // On initial click calculate the distance between mouse and the center of the grab
6364             if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm)
6365             {
6366                 *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
6367             }
6368             else
6369             {
6370                 seek_absolute = true;
6371                 *click_delta_to_grab_center_v = 0.0f;
6372             }
6373         }
6374 
6375         // Apply scroll
6376         // 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
6377         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));
6378         scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v));
6379         if (horizontal)
6380             window->Scroll.x = scroll_v;
6381         else
6382             window->Scroll.y = scroll_v;
6383 
6384         // Update values for rendering
6385         scroll_ratio = ImSaturate(scroll_v / scroll_max);
6386         grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
6387 
6388         // Update distance to grab now that we have seeked and saturated
6389         if (seek_absolute)
6390             *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
6391     }
6392 
6393     // Render
6394     const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab);
6395     ImRect grab_rect;
6396     if (horizontal)
6397         grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImMin(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, window_rect.Max.x), bb.Max.y);
6398     else
6399         grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImMin(ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels, window_rect.Max.y));
6400     window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding);
6401 }
6402 
BringWindowToFront(ImGuiWindow * window)6403 void ImGui::BringWindowToFront(ImGuiWindow* window)
6404 {
6405     ImGuiContext& g = *GImGui;
6406     ImGuiWindow* current_front_window = g.Windows.back();
6407     if (current_front_window == window || current_front_window->RootWindow == window)
6408         return;
6409     for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window
6410         if (g.Windows[i] == window)
6411         {
6412             g.Windows.erase(g.Windows.Data + i);
6413             g.Windows.push_back(window);
6414             break;
6415         }
6416 }
6417 
BringWindowToBack(ImGuiWindow * window)6418 void ImGui::BringWindowToBack(ImGuiWindow* window)
6419 {
6420     ImGuiContext& g = *GImGui;
6421     if (g.Windows[0] == window)
6422         return;
6423     for (int i = 0; i < g.Windows.Size; i++)
6424         if (g.Windows[i] == window)
6425         {
6426             memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
6427             g.Windows[0] = window;
6428             break;
6429         }
6430 }
6431 
6432 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)6433 void ImGui::FocusWindow(ImGuiWindow* window)
6434 {
6435     ImGuiContext& g = *GImGui;
6436 
6437     if (g.NavWindow != window)
6438     {
6439         g.NavWindow = window;
6440         if (window && g.NavDisableMouseHover)
6441             g.NavMousePosDirty = true;
6442         g.NavInitRequest = false;
6443         g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
6444         g.NavIdIsAlive = false;
6445         g.NavLayer = 0;
6446         //printf("[%05d] FocusWindow(\"%s\")\n", g.FrameCount, window ? window->Name : NULL);
6447     }
6448 
6449     // Passing NULL allow to disable keyboard focus
6450     if (!window)
6451         return;
6452 
6453     // Move the root window to the top of the pile
6454     if (window->RootWindow)
6455         window = window->RootWindow;
6456 
6457     // Steal focus on active widgets
6458     if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
6459         if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
6460             ClearActiveID();
6461 
6462     // Bring to front
6463     if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
6464         BringWindowToFront(window);
6465 }
6466 
FocusFrontMostActiveWindow(ImGuiWindow * ignore_window)6467 void ImGui::FocusFrontMostActiveWindow(ImGuiWindow* ignore_window)
6468 {
6469     ImGuiContext& g = *GImGui;
6470     for (int i = g.Windows.Size - 1; i >= 0; i--)
6471         if (g.Windows[i] != ignore_window && g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow))
6472         {
6473             ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(g.Windows[i]);
6474             FocusWindow(focus_window);
6475             return;
6476         }
6477 }
6478 
PushItemWidth(float item_width)6479 void ImGui::PushItemWidth(float item_width)
6480 {
6481     ImGuiWindow* window = GetCurrentWindow();
6482     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
6483     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
6484 }
6485 
PushMultiItemsWidths(int components,float w_full)6486 void ImGui::PushMultiItemsWidths(int components, float w_full)
6487 {
6488     ImGuiWindow* window = GetCurrentWindow();
6489     const ImGuiStyle& style = GImGui->Style;
6490     if (w_full <= 0.0f)
6491         w_full = CalcItemWidth();
6492     const float w_item_one  = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
6493     const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
6494     window->DC.ItemWidthStack.push_back(w_item_last);
6495     for (int i = 0; i < components-1; i++)
6496         window->DC.ItemWidthStack.push_back(w_item_one);
6497     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
6498 }
6499 
PopItemWidth()6500 void ImGui::PopItemWidth()
6501 {
6502     ImGuiWindow* window = GetCurrentWindow();
6503     window->DC.ItemWidthStack.pop_back();
6504     window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
6505 }
6506 
CalcItemWidth()6507 float ImGui::CalcItemWidth()
6508 {
6509     ImGuiWindow* window = GetCurrentWindowRead();
6510     float w = window->DC.ItemWidth;
6511     if (w < 0.0f)
6512     {
6513         // 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.
6514         float width_to_right_edge = GetContentRegionAvail().x;
6515         w = ImMax(1.0f, width_to_right_edge + w);
6516     }
6517     w = (float)(int)w;
6518     return w;
6519 }
6520 
GetDefaultFont()6521 static ImFont* GetDefaultFont()
6522 {
6523     ImGuiContext& g = *GImGui;
6524     return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0];
6525 }
6526 
SetCurrentFont(ImFont * font)6527 void ImGui::SetCurrentFont(ImFont* font)
6528 {
6529     ImGuiContext& g = *GImGui;
6530     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
6531     IM_ASSERT(font->Scale > 0.0f);
6532     g.Font = font;
6533     g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale;
6534     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
6535 
6536     ImFontAtlas* atlas = g.Font->ContainerAtlas;
6537     g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
6538     g.DrawListSharedData.Font = g.Font;
6539     g.DrawListSharedData.FontSize = g.FontSize;
6540 }
6541 
PushFont(ImFont * font)6542 void ImGui::PushFont(ImFont* font)
6543 {
6544     ImGuiContext& g = *GImGui;
6545     if (!font)
6546         font = GetDefaultFont();
6547     SetCurrentFont(font);
6548     g.FontStack.push_back(font);
6549     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
6550 }
6551 
PopFont()6552 void  ImGui::PopFont()
6553 {
6554     ImGuiContext& g = *GImGui;
6555     g.CurrentWindow->DrawList->PopTextureID();
6556     g.FontStack.pop_back();
6557     SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
6558 }
6559 
PushItemFlag(ImGuiItemFlags option,bool enabled)6560 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
6561 {
6562     ImGuiWindow* window = GetCurrentWindow();
6563     if (enabled)
6564         window->DC.ItemFlags |= option;
6565     else
6566         window->DC.ItemFlags &= ~option;
6567     window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
6568 }
6569 
PopItemFlag()6570 void ImGui::PopItemFlag()
6571 {
6572     ImGuiWindow* window = GetCurrentWindow();
6573     window->DC.ItemFlagsStack.pop_back();
6574     window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
6575 }
6576 
PushAllowKeyboardFocus(bool allow_keyboard_focus)6577 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
6578 {
6579     PushItemFlag(ImGuiItemFlags_AllowKeyboardFocus, allow_keyboard_focus);
6580 }
6581 
PopAllowKeyboardFocus()6582 void ImGui::PopAllowKeyboardFocus()
6583 {
6584     PopItemFlag();
6585 }
6586 
PushButtonRepeat(bool repeat)6587 void ImGui::PushButtonRepeat(bool repeat)
6588 {
6589     PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
6590 }
6591 
PopButtonRepeat()6592 void ImGui::PopButtonRepeat()
6593 {
6594     PopItemFlag();
6595 }
6596 
PushTextWrapPos(float wrap_pos_x)6597 void ImGui::PushTextWrapPos(float wrap_pos_x)
6598 {
6599     ImGuiWindow* window = GetCurrentWindow();
6600     window->DC.TextWrapPos = wrap_pos_x;
6601     window->DC.TextWrapPosStack.push_back(wrap_pos_x);
6602 }
6603 
PopTextWrapPos()6604 void ImGui::PopTextWrapPos()
6605 {
6606     ImGuiWindow* window = GetCurrentWindow();
6607     window->DC.TextWrapPosStack.pop_back();
6608     window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
6609 }
6610 
6611 // FIXME: This may incur a round-trip (if the end user got their data from a float4) but eventually we aim to store the in-flight colors as ImU32
PushStyleColor(ImGuiCol idx,ImU32 col)6612 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
6613 {
6614     ImGuiContext& g = *GImGui;
6615     ImGuiColMod backup;
6616     backup.Col = idx;
6617     backup.BackupValue = g.Style.Colors[idx];
6618     g.ColorModifiers.push_back(backup);
6619     g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
6620 }
6621 
PushStyleColor(ImGuiCol idx,const ImVec4 & col)6622 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
6623 {
6624     ImGuiContext& g = *GImGui;
6625     ImGuiColMod backup;
6626     backup.Col = idx;
6627     backup.BackupValue = g.Style.Colors[idx];
6628     g.ColorModifiers.push_back(backup);
6629     g.Style.Colors[idx] = col;
6630 }
6631 
PopStyleColor(int count)6632 void ImGui::PopStyleColor(int count)
6633 {
6634     ImGuiContext& g = *GImGui;
6635     while (count > 0)
6636     {
6637         ImGuiColMod& backup = g.ColorModifiers.back();
6638         g.Style.Colors[backup.Col] = backup.BackupValue;
6639         g.ColorModifiers.pop_back();
6640         count--;
6641     }
6642 }
6643 
6644 struct ImGuiStyleVarInfo
6645 {
6646     ImGuiDataType   Type;
6647     ImU32           Count;
6648     ImU32           Offset;
GetVarPtrImGuiStyleVarInfo6649     void*           GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
6650 };
6651 
6652 static const ImGuiStyleVarInfo GStyleVarInfo[] =
6653 {
6654     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) },              // ImGuiStyleVar_Alpha
6655     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) },      // ImGuiStyleVar_WindowPadding
6656     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) },     // ImGuiStyleVar_WindowRounding
6657     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) },   // ImGuiStyleVar_WindowBorderSize
6658     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) },      // ImGuiStyleVar_WindowMinSize
6659     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) },   // ImGuiStyleVar_WindowTitleAlign
6660     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) },      // ImGuiStyleVar_ChildRounding
6661     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) },    // ImGuiStyleVar_ChildBorderSize
6662     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) },      // ImGuiStyleVar_PopupRounding
6663     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) },    // ImGuiStyleVar_PopupBorderSize
6664     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) },       // ImGuiStyleVar_FramePadding
6665     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) },      // ImGuiStyleVar_FrameRounding
6666     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) },    // ImGuiStyleVar_FrameBorderSize
6667     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) },        // ImGuiStyleVar_ItemSpacing
6668     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) },   // ImGuiStyleVar_ItemInnerSpacing
6669     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) },      // ImGuiStyleVar_IndentSpacing
6670     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) },      // ImGuiStyleVar_ScrollbarSize
6671     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) },  // ImGuiStyleVar_ScrollbarRounding
6672     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },        // ImGuiStyleVar_GrabMinSize
6673     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) },       // ImGuiStyleVar_GrabRounding
6674     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },    // ImGuiStyleVar_ButtonTextAlign
6675 };
6676 
GetStyleVarInfo(ImGuiStyleVar idx)6677 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
6678 {
6679     IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
6680     IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
6681     return &GStyleVarInfo[idx];
6682 }
6683 
PushStyleVar(ImGuiStyleVar idx,float val)6684 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
6685 {
6686     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
6687     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
6688     {
6689         ImGuiContext& g = *GImGui;
6690         float* pvar = (float*)var_info->GetVarPtr(&g.Style);
6691         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
6692         *pvar = val;
6693         return;
6694     }
6695     IM_ASSERT(0); // Called function with wrong-type? Variable is not a float.
6696 }
6697 
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)6698 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
6699 {
6700     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
6701     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
6702     {
6703         ImGuiContext& g = *GImGui;
6704         ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
6705         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
6706         *pvar = val;
6707         return;
6708     }
6709     IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2.
6710 }
6711 
PopStyleVar(int count)6712 void ImGui::PopStyleVar(int count)
6713 {
6714     ImGuiContext& g = *GImGui;
6715     while (count > 0)
6716     {
6717         // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
6718         ImGuiStyleMod& backup = g.StyleModifiers.back();
6719         const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
6720         void* data = info->GetVarPtr(&g.Style);
6721         if (info->Type == ImGuiDataType_Float && info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }
6722         else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
6723         g.StyleModifiers.pop_back();
6724         count--;
6725     }
6726 }
6727 
GetStyleColorName(ImGuiCol idx)6728 const char* ImGui::GetStyleColorName(ImGuiCol idx)
6729 {
6730     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
6731     switch (idx)
6732     {
6733     case ImGuiCol_Text: return "Text";
6734     case ImGuiCol_TextDisabled: return "TextDisabled";
6735     case ImGuiCol_WindowBg: return "WindowBg";
6736     case ImGuiCol_ChildBg: return "ChildBg";
6737     case ImGuiCol_PopupBg: return "PopupBg";
6738     case ImGuiCol_Border: return "Border";
6739     case ImGuiCol_BorderShadow: return "BorderShadow";
6740     case ImGuiCol_FrameBg: return "FrameBg";
6741     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
6742     case ImGuiCol_FrameBgActive: return "FrameBgActive";
6743     case ImGuiCol_TitleBg: return "TitleBg";
6744     case ImGuiCol_TitleBgActive: return "TitleBgActive";
6745     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
6746     case ImGuiCol_MenuBarBg: return "MenuBarBg";
6747     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
6748     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
6749     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
6750     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
6751     case ImGuiCol_CheckMark: return "CheckMark";
6752     case ImGuiCol_SliderGrab: return "SliderGrab";
6753     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
6754     case ImGuiCol_Button: return "Button";
6755     case ImGuiCol_ButtonHovered: return "ButtonHovered";
6756     case ImGuiCol_ButtonActive: return "ButtonActive";
6757     case ImGuiCol_Header: return "Header";
6758     case ImGuiCol_HeaderHovered: return "HeaderHovered";
6759     case ImGuiCol_HeaderActive: return "HeaderActive";
6760     case ImGuiCol_Separator: return "Separator";
6761     case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
6762     case ImGuiCol_SeparatorActive: return "SeparatorActive";
6763     case ImGuiCol_ResizeGrip: return "ResizeGrip";
6764     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
6765     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
6766     case ImGuiCol_PlotLines: return "PlotLines";
6767     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
6768     case ImGuiCol_PlotHistogram: return "PlotHistogram";
6769     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
6770     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
6771     case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening";
6772     case ImGuiCol_DragDropTarget: return "DragDropTarget";
6773     case ImGuiCol_NavHighlight: return "NavHighlight";
6774     case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
6775     }
6776     IM_ASSERT(0);
6777     return "Unknown";
6778 }
6779 
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)6780 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
6781 {
6782     if (window->RootWindow == potential_parent)
6783         return true;
6784     while (window != NULL)
6785     {
6786         if (window == potential_parent)
6787             return true;
6788         window = window->ParentWindow;
6789     }
6790     return false;
6791 }
6792 
IsWindowHovered(ImGuiHoveredFlags flags)6793 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
6794 {
6795     IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0);   // Flags not supported by this function
6796     ImGuiContext& g = *GImGui;
6797 
6798     if (flags & ImGuiHoveredFlags_AnyWindow)
6799     {
6800         if (g.HoveredWindow == NULL)
6801             return false;
6802     }
6803     else
6804     {
6805         switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
6806         {
6807         case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
6808             if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
6809                 return false;
6810             break;
6811         case ImGuiHoveredFlags_RootWindow:
6812             if (g.HoveredWindow != g.CurrentWindow->RootWindow)
6813                 return false;
6814             break;
6815         case ImGuiHoveredFlags_ChildWindows:
6816             if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
6817                 return false;
6818             break;
6819         default:
6820             if (g.HoveredWindow != g.CurrentWindow)
6821                 return false;
6822             break;
6823         }
6824     }
6825 
6826     if (!IsWindowContentHoverable(g.HoveredRootWindow, flags))
6827         return false;
6828     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
6829         if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
6830             return false;
6831     return true;
6832 }
6833 
IsWindowFocused(ImGuiFocusedFlags flags)6834 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
6835 {
6836     ImGuiContext& g = *GImGui;
6837     IM_ASSERT(g.CurrentWindow);     // Not inside a Begin()/End()
6838 
6839     if (flags & ImGuiFocusedFlags_AnyWindow)
6840         return g.NavWindow != NULL;
6841 
6842     switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
6843     {
6844     case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
6845         return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
6846     case ImGuiFocusedFlags_RootWindow:
6847         return g.NavWindow == g.CurrentWindow->RootWindow;
6848     case ImGuiFocusedFlags_ChildWindows:
6849         return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
6850     default:
6851         return g.NavWindow == g.CurrentWindow;
6852     }
6853 }
6854 
6855 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
IsWindowNavFocusable(ImGuiWindow * window)6856 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
6857 {
6858     ImGuiContext& g = *GImGui;
6859     return window->Active && window == window->RootWindowForTabbing && (!(window->Flags & ImGuiWindowFlags_NoNavFocus) || window == g.NavWindow);
6860 }
6861 
GetWindowWidth()6862 float ImGui::GetWindowWidth()
6863 {
6864     ImGuiWindow* window = GImGui->CurrentWindow;
6865     return window->Size.x;
6866 }
6867 
GetWindowHeight()6868 float ImGui::GetWindowHeight()
6869 {
6870     ImGuiWindow* window = GImGui->CurrentWindow;
6871     return window->Size.y;
6872 }
6873 
GetWindowPos()6874 ImVec2 ImGui::GetWindowPos()
6875 {
6876     ImGuiContext& g = *GImGui;
6877     ImGuiWindow* window = g.CurrentWindow;
6878     return window->Pos;
6879 }
6880 
SetWindowScrollX(ImGuiWindow * window,float new_scroll_x)6881 static void SetWindowScrollX(ImGuiWindow* window, float new_scroll_x)
6882 {
6883     window->DC.CursorMaxPos.x += window->Scroll.x; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it.
6884     window->Scroll.x = new_scroll_x;
6885     window->DC.CursorMaxPos.x -= window->Scroll.x;
6886 }
6887 
SetWindowScrollY(ImGuiWindow * window,float new_scroll_y)6888 static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
6889 {
6890     window->DC.CursorMaxPos.y += window->Scroll.y; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it.
6891     window->Scroll.y = new_scroll_y;
6892     window->DC.CursorMaxPos.y -= window->Scroll.y;
6893 }
6894 
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)6895 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
6896 {
6897     // Test condition (NB: bit 0 is always true) and clear flags for next time
6898     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
6899         return;
6900 
6901     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6902     window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6903     window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
6904 
6905     // Set
6906     const ImVec2 old_pos = window->Pos;
6907     window->PosFloat = pos;
6908     window->Pos = ImFloor(pos);
6909     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
6910     window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected.
6911 }
6912 
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)6913 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
6914 {
6915     ImGuiWindow* window = GetCurrentWindowRead();
6916     SetWindowPos(window, pos, cond);
6917 }
6918 
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)6919 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
6920 {
6921     if (ImGuiWindow* window = FindWindowByName(name))
6922         SetWindowPos(window, pos, cond);
6923 }
6924 
GetWindowSize()6925 ImVec2 ImGui::GetWindowSize()
6926 {
6927     ImGuiWindow* window = GetCurrentWindowRead();
6928     return window->Size;
6929 }
6930 
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)6931 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
6932 {
6933     // Test condition (NB: bit 0 is always true) and clear flags for next time
6934     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
6935         return;
6936 
6937     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6938     window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6939 
6940     // Set
6941     if (size.x > 0.0f)
6942     {
6943         window->AutoFitFramesX = 0;
6944         window->SizeFull.x = size.x;
6945     }
6946     else
6947     {
6948         window->AutoFitFramesX = 2;
6949         window->AutoFitOnlyGrows = false;
6950     }
6951     if (size.y > 0.0f)
6952     {
6953         window->AutoFitFramesY = 0;
6954         window->SizeFull.y = size.y;
6955     }
6956     else
6957     {
6958         window->AutoFitFramesY = 2;
6959         window->AutoFitOnlyGrows = false;
6960     }
6961 }
6962 
SetWindowSize(const ImVec2 & size,ImGuiCond cond)6963 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
6964 {
6965     SetWindowSize(GImGui->CurrentWindow, size, cond);
6966 }
6967 
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)6968 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
6969 {
6970     if (ImGuiWindow* window = FindWindowByName(name))
6971         SetWindowSize(window, size, cond);
6972 }
6973 
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)6974 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
6975 {
6976     // Test condition (NB: bit 0 is always true) and clear flags for next time
6977     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
6978         return;
6979     window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6980 
6981     // Set
6982     window->Collapsed = collapsed;
6983 }
6984 
SetWindowCollapsed(bool collapsed,ImGuiCond cond)6985 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
6986 {
6987     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
6988 }
6989 
IsWindowCollapsed()6990 bool ImGui::IsWindowCollapsed()
6991 {
6992     ImGuiWindow* window = GetCurrentWindowRead();
6993     return window->Collapsed;
6994 }
6995 
IsWindowAppearing()6996 bool ImGui::IsWindowAppearing()
6997 {
6998     ImGuiWindow* window = GetCurrentWindowRead();
6999     return window->Appearing;
7000 }
7001 
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)7002 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
7003 {
7004     if (ImGuiWindow* window = FindWindowByName(name))
7005         SetWindowCollapsed(window, collapsed, cond);
7006 }
7007 
SetWindowFocus()7008 void ImGui::SetWindowFocus()
7009 {
7010     FocusWindow(GImGui->CurrentWindow);
7011 }
7012 
SetWindowFocus(const char * name)7013 void ImGui::SetWindowFocus(const char* name)
7014 {
7015     if (name)
7016     {
7017         if (ImGuiWindow* window = FindWindowByName(name))
7018             FocusWindow(window);
7019     }
7020     else
7021     {
7022         FocusWindow(NULL);
7023     }
7024 }
7025 
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)7026 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
7027 {
7028     ImGuiContext& g = *GImGui;
7029     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7030     g.NextWindowData.PosVal = pos;
7031     g.NextWindowData.PosPivotVal = pivot;
7032     g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
7033 }
7034 
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)7035 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
7036 {
7037     ImGuiContext& g = *GImGui;
7038     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7039     g.NextWindowData.SizeVal = size;
7040     g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
7041 }
7042 
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)7043 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
7044 {
7045     ImGuiContext& g = *GImGui;
7046     g.NextWindowData.SizeConstraintCond = ImGuiCond_Always;
7047     g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
7048     g.NextWindowData.SizeCallback = custom_callback;
7049     g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
7050 }
7051 
SetNextWindowContentSize(const ImVec2 & size)7052 void ImGui::SetNextWindowContentSize(const ImVec2& size)
7053 {
7054     ImGuiContext& g = *GImGui;
7055     g.NextWindowData.ContentSizeVal = size;  // In Begin() we will add the size of window decorations (title bar, menu etc.) to that to form a SizeContents value.
7056     g.NextWindowData.ContentSizeCond = ImGuiCond_Always;
7057 }
7058 
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)7059 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
7060 {
7061     ImGuiContext& g = *GImGui;
7062     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7063     g.NextWindowData.CollapsedVal = collapsed;
7064     g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
7065 }
7066 
SetNextWindowFocus()7067 void ImGui::SetNextWindowFocus()
7068 {
7069     ImGuiContext& g = *GImGui;
7070     g.NextWindowData.FocusCond = ImGuiCond_Always;   // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
7071 }
7072 
SetNextWindowBgAlpha(float alpha)7073 void ImGui::SetNextWindowBgAlpha(float alpha)
7074 {
7075     ImGuiContext& g = *GImGui;
7076     g.NextWindowData.BgAlphaVal = alpha;
7077     g.NextWindowData.BgAlphaCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
7078 }
7079 
7080 // In window space (not screen space!)
GetContentRegionMax()7081 ImVec2 ImGui::GetContentRegionMax()
7082 {
7083     ImGuiWindow* window = GetCurrentWindowRead();
7084     ImVec2 mx = window->ContentsRegionRect.Max;
7085     if (window->DC.ColumnsSet)
7086         mx.x = GetColumnOffset(window->DC.ColumnsSet->Current + 1) - window->WindowPadding.x;
7087     return mx;
7088 }
7089 
GetContentRegionAvail()7090 ImVec2 ImGui::GetContentRegionAvail()
7091 {
7092     ImGuiWindow* window = GetCurrentWindowRead();
7093     return GetContentRegionMax() - (window->DC.CursorPos - window->Pos);
7094 }
7095 
GetContentRegionAvailWidth()7096 float ImGui::GetContentRegionAvailWidth()
7097 {
7098     return GetContentRegionAvail().x;
7099 }
7100 
7101 // In window space (not screen space!)
GetWindowContentRegionMin()7102 ImVec2 ImGui::GetWindowContentRegionMin()
7103 {
7104     ImGuiWindow* window = GetCurrentWindowRead();
7105     return window->ContentsRegionRect.Min;
7106 }
7107 
GetWindowContentRegionMax()7108 ImVec2 ImGui::GetWindowContentRegionMax()
7109 {
7110     ImGuiWindow* window = GetCurrentWindowRead();
7111     return window->ContentsRegionRect.Max;
7112 }
7113 
GetWindowContentRegionWidth()7114 float ImGui::GetWindowContentRegionWidth()
7115 {
7116     ImGuiWindow* window = GetCurrentWindowRead();
7117     return window->ContentsRegionRect.Max.x - window->ContentsRegionRect.Min.x;
7118 }
7119 
GetTextLineHeight()7120 float ImGui::GetTextLineHeight()
7121 {
7122     ImGuiContext& g = *GImGui;
7123     return g.FontSize;
7124 }
7125 
GetTextLineHeightWithSpacing()7126 float ImGui::GetTextLineHeightWithSpacing()
7127 {
7128     ImGuiContext& g = *GImGui;
7129     return g.FontSize + g.Style.ItemSpacing.y;
7130 }
7131 
GetFrameHeight()7132 float ImGui::GetFrameHeight()
7133 {
7134     ImGuiContext& g = *GImGui;
7135     return g.FontSize + g.Style.FramePadding.y * 2.0f;
7136 }
7137 
GetFrameHeightWithSpacing()7138 float ImGui::GetFrameHeightWithSpacing()
7139 {
7140     ImGuiContext& g = *GImGui;
7141     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
7142 }
7143 
GetWindowDrawList()7144 ImDrawList* ImGui::GetWindowDrawList()
7145 {
7146     ImGuiWindow* window = GetCurrentWindow();
7147     return window->DrawList;
7148 }
7149 
GetFont()7150 ImFont* ImGui::GetFont()
7151 {
7152     return GImGui->Font;
7153 }
7154 
GetFontSize()7155 float ImGui::GetFontSize()
7156 {
7157     return GImGui->FontSize;
7158 }
7159 
GetFontTexUvWhitePixel()7160 ImVec2 ImGui::GetFontTexUvWhitePixel()
7161 {
7162     return GImGui->DrawListSharedData.TexUvWhitePixel;
7163 }
7164 
SetWindowFontScale(float scale)7165 void ImGui::SetWindowFontScale(float scale)
7166 {
7167     ImGuiContext& g = *GImGui;
7168     ImGuiWindow* window = GetCurrentWindow();
7169     window->FontWindowScale = scale;
7170     g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
7171 }
7172 
7173 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
7174 // 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()7175 ImVec2 ImGui::GetCursorPos()
7176 {
7177     ImGuiWindow* window = GetCurrentWindowRead();
7178     return window->DC.CursorPos - window->Pos + window->Scroll;
7179 }
7180 
GetCursorPosX()7181 float ImGui::GetCursorPosX()
7182 {
7183     ImGuiWindow* window = GetCurrentWindowRead();
7184     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
7185 }
7186 
GetCursorPosY()7187 float ImGui::GetCursorPosY()
7188 {
7189     ImGuiWindow* window = GetCurrentWindowRead();
7190     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
7191 }
7192 
SetCursorPos(const ImVec2 & local_pos)7193 void ImGui::SetCursorPos(const ImVec2& local_pos)
7194 {
7195     ImGuiWindow* window = GetCurrentWindow();
7196     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
7197     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7198 }
7199 
SetCursorPosX(float x)7200 void ImGui::SetCursorPosX(float x)
7201 {
7202     ImGuiWindow* window = GetCurrentWindow();
7203     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
7204     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
7205 }
7206 
SetCursorPosY(float y)7207 void ImGui::SetCursorPosY(float y)
7208 {
7209     ImGuiWindow* window = GetCurrentWindow();
7210     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
7211     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
7212 }
7213 
GetCursorStartPos()7214 ImVec2 ImGui::GetCursorStartPos()
7215 {
7216     ImGuiWindow* window = GetCurrentWindowRead();
7217     return window->DC.CursorStartPos - window->Pos;
7218 }
7219 
GetCursorScreenPos()7220 ImVec2 ImGui::GetCursorScreenPos()
7221 {
7222     ImGuiWindow* window = GetCurrentWindowRead();
7223     return window->DC.CursorPos;
7224 }
7225 
SetCursorScreenPos(const ImVec2 & screen_pos)7226 void ImGui::SetCursorScreenPos(const ImVec2& screen_pos)
7227 {
7228     ImGuiWindow* window = GetCurrentWindow();
7229     window->DC.CursorPos = screen_pos;
7230     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7231 }
7232 
GetScrollX()7233 float ImGui::GetScrollX()
7234 {
7235     return GImGui->CurrentWindow->Scroll.x;
7236 }
7237 
GetScrollY()7238 float ImGui::GetScrollY()
7239 {
7240     return GImGui->CurrentWindow->Scroll.y;
7241 }
7242 
GetScrollMaxX()7243 float ImGui::GetScrollMaxX()
7244 {
7245     return GetScrollMaxX(GImGui->CurrentWindow);
7246 }
7247 
GetScrollMaxY()7248 float ImGui::GetScrollMaxY()
7249 {
7250     return GetScrollMaxY(GImGui->CurrentWindow);
7251 }
7252 
SetScrollX(float scroll_x)7253 void ImGui::SetScrollX(float scroll_x)
7254 {
7255     ImGuiWindow* window = GetCurrentWindow();
7256     window->ScrollTarget.x = scroll_x;
7257     window->ScrollTargetCenterRatio.x = 0.0f;
7258 }
7259 
SetScrollY(float scroll_y)7260 void ImGui::SetScrollY(float scroll_y)
7261 {
7262     ImGuiWindow* window = GetCurrentWindow();
7263     window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY
7264     window->ScrollTargetCenterRatio.y = 0.0f;
7265 }
7266 
SetScrollFromPosY(float pos_y,float center_y_ratio)7267 void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio)
7268 {
7269     // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
7270     ImGuiWindow* window = GetCurrentWindow();
7271     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
7272     window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y);
7273     window->ScrollTargetCenterRatio.y = center_y_ratio;
7274 
7275     // Minor hack to to make scrolling to top/bottom of window take account of WindowPadding, it looks more right to the user this way
7276     if (center_y_ratio <= 0.0f && window->ScrollTarget.y <= window->WindowPadding.y)
7277         window->ScrollTarget.y = 0.0f;
7278     else if (center_y_ratio >= 1.0f && window->ScrollTarget.y >= window->SizeContents.y - window->WindowPadding.y + GImGui->Style.ItemSpacing.y)
7279         window->ScrollTarget.y = window->SizeContents.y;
7280 }
7281 
7282 // 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)7283 void ImGui::SetScrollHere(float center_y_ratio)
7284 {
7285     ImGuiWindow* window = GetCurrentWindow();
7286     float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
7287     target_y += (window->DC.PrevLineHeight * center_y_ratio) + (GImGui->Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line.
7288     SetScrollFromPosY(target_y, center_y_ratio);
7289 }
7290 
ActivateItem(ImGuiID id)7291 void ImGui::ActivateItem(ImGuiID id)
7292 {
7293     ImGuiContext& g = *GImGui;
7294     g.NavNextActivateId = id;
7295 }
7296 
SetKeyboardFocusHere(int offset)7297 void ImGui::SetKeyboardFocusHere(int offset)
7298 {
7299     IM_ASSERT(offset >= -1);    // -1 is allowed but not below
7300     ImGuiWindow* window = GetCurrentWindow();
7301     window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
7302     window->FocusIdxTabRequestNext = INT_MAX;
7303 }
7304 
SetItemDefaultFocus()7305 void ImGui::SetItemDefaultFocus()
7306 {
7307     ImGuiContext& g = *GImGui;
7308     ImGuiWindow* window = g.CurrentWindow;
7309     if (!window->Appearing)
7310         return;
7311     if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
7312     {
7313         g.NavInitRequest = false;
7314         g.NavInitResultId = g.NavWindow->DC.LastItemId;
7315         g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
7316         NavUpdateAnyRequestFlag();
7317         if (!IsItemVisible())
7318             SetScrollHere();
7319     }
7320 }
7321 
SetStateStorage(ImGuiStorage * tree)7322 void ImGui::SetStateStorage(ImGuiStorage* tree)
7323 {
7324     ImGuiWindow* window = GetCurrentWindow();
7325     window->DC.StateStorage = tree ? tree : &window->StateStorage;
7326 }
7327 
GetStateStorage()7328 ImGuiStorage* ImGui::GetStateStorage()
7329 {
7330     ImGuiWindow* window = GetCurrentWindowRead();
7331     return window->DC.StateStorage;
7332 }
7333 
TextV(const char * fmt,va_list args)7334 void ImGui::TextV(const char* fmt, va_list args)
7335 {
7336     ImGuiWindow* window = GetCurrentWindow();
7337     if (window->SkipItems)
7338         return;
7339 
7340     ImGuiContext& g = *GImGui;
7341     const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
7342     TextUnformatted(g.TempBuffer, text_end);
7343 }
7344 
Text(const char * fmt,...)7345 void ImGui::Text(const char* fmt, ...)
7346 {
7347     va_list args;
7348     va_start(args, fmt);
7349     TextV(fmt, args);
7350     va_end(args);
7351 }
7352 
TextColoredV(const ImVec4 & col,const char * fmt,va_list args)7353 void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args)
7354 {
7355     PushStyleColor(ImGuiCol_Text, col);
7356     TextV(fmt, args);
7357     PopStyleColor();
7358 }
7359 
TextColored(const ImVec4 & col,const char * fmt,...)7360 void ImGui::TextColored(const ImVec4& col, const char* fmt, ...)
7361 {
7362     va_list args;
7363     va_start(args, fmt);
7364     TextColoredV(col, fmt, args);
7365     va_end(args);
7366 }
7367 
TextDisabledV(const char * fmt,va_list args)7368 void ImGui::TextDisabledV(const char* fmt, va_list args)
7369 {
7370     PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]);
7371     TextV(fmt, args);
7372     PopStyleColor();
7373 }
7374 
TextDisabled(const char * fmt,...)7375 void ImGui::TextDisabled(const char* fmt, ...)
7376 {
7377     va_list args;
7378     va_start(args, fmt);
7379     TextDisabledV(fmt, args);
7380     va_end(args);
7381 }
7382 
TextWrappedV(const char * fmt,va_list args)7383 void ImGui::TextWrappedV(const char* fmt, va_list args)
7384 {
7385     bool need_wrap = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f);    // Keep existing wrap position is one ia already set
7386     if (need_wrap) PushTextWrapPos(0.0f);
7387     TextV(fmt, args);
7388     if (need_wrap) PopTextWrapPos();
7389 }
7390 
TextWrapped(const char * fmt,...)7391 void ImGui::TextWrapped(const char* fmt, ...)
7392 {
7393     va_list args;
7394     va_start(args, fmt);
7395     TextWrappedV(fmt, args);
7396     va_end(args);
7397 }
7398 
TextUnformatted(const char * text,const char * text_end)7399 void ImGui::TextUnformatted(const char* text, const char* text_end)
7400 {
7401     ImGuiWindow* window = GetCurrentWindow();
7402     if (window->SkipItems)
7403         return;
7404 
7405     ImGuiContext& g = *GImGui;
7406     IM_ASSERT(text != NULL);
7407     const char* text_begin = text;
7408     if (text_end == NULL)
7409         text_end = text + strlen(text); // FIXME-OPT
7410 
7411     const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset);
7412     const float wrap_pos_x = window->DC.TextWrapPos;
7413     const bool wrap_enabled = wrap_pos_x >= 0.0f;
7414     if (text_end - text > 2000 && !wrap_enabled)
7415     {
7416         // Long text!
7417         // Perform manual coarse clipping to optimize for long multi-line text
7418         // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.
7419         // 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.
7420         const char* line = text;
7421         const float line_height = GetTextLineHeight();
7422         const ImRect clip_rect = window->ClipRect;
7423         ImVec2 text_size(0,0);
7424 
7425         if (text_pos.y <= clip_rect.Max.y)
7426         {
7427             ImVec2 pos = text_pos;
7428 
7429             // Lines to skip (can't skip when logging text)
7430             if (!g.LogEnabled)
7431             {
7432                 int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height);
7433                 if (lines_skippable > 0)
7434                 {
7435                     int lines_skipped = 0;
7436                     while (line < text_end && lines_skipped < lines_skippable)
7437                     {
7438                         const char* line_end = strchr(line, '\n');
7439                         if (!line_end)
7440                             line_end = text_end;
7441                         line = line_end + 1;
7442                         lines_skipped++;
7443                     }
7444                     pos.y += lines_skipped * line_height;
7445                 }
7446             }
7447 
7448             // Lines to render
7449             if (line < text_end)
7450             {
7451                 ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height));
7452                 while (line < text_end)
7453                 {
7454                     const char* line_end = strchr(line, '\n');
7455                     if (IsClippedEx(line_rect, 0, false))
7456                         break;
7457 
7458                     const ImVec2 line_size = CalcTextSize(line, line_end, false);
7459                     text_size.x = ImMax(text_size.x, line_size.x);
7460                     RenderText(pos, line, line_end, false);
7461                     if (!line_end)
7462                         line_end = text_end;
7463                     line = line_end + 1;
7464                     line_rect.Min.y += line_height;
7465                     line_rect.Max.y += line_height;
7466                     pos.y += line_height;
7467                 }
7468 
7469                 // Count remaining lines
7470                 int lines_skipped = 0;
7471                 while (line < text_end)
7472                 {
7473                     const char* line_end = strchr(line, '\n');
7474                     if (!line_end)
7475                         line_end = text_end;
7476                     line = line_end + 1;
7477                     lines_skipped++;
7478                 }
7479                 pos.y += lines_skipped * line_height;
7480             }
7481 
7482             text_size.y += (pos - text_pos).y;
7483         }
7484 
7485         ImRect bb(text_pos, text_pos + text_size);
7486         ItemSize(bb);
7487         ItemAdd(bb, 0);
7488     }
7489     else
7490     {
7491         const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;
7492         const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);
7493 
7494         // Account of baseline offset
7495         ImRect bb(text_pos, text_pos + text_size);
7496         ItemSize(text_size);
7497         if (!ItemAdd(bb, 0))
7498             return;
7499 
7500         // Render (we don't hide text after ## in this end-user function)
7501         RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width);
7502     }
7503 }
7504 
AlignTextToFramePadding()7505 void ImGui::AlignTextToFramePadding()
7506 {
7507     ImGuiWindow* window = GetCurrentWindow();
7508     if (window->SkipItems)
7509         return;
7510 
7511     ImGuiContext& g = *GImGui;
7512     window->DC.CurrentLineHeight = ImMax(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y * 2);
7513     window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
7514 }
7515 
7516 // Add a label+text combo aligned to other label+value widgets
LabelTextV(const char * label,const char * fmt,va_list args)7517 void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
7518 {
7519     ImGuiWindow* window = GetCurrentWindow();
7520     if (window->SkipItems)
7521         return;
7522 
7523     ImGuiContext& g = *GImGui;
7524     const ImGuiStyle& style = g.Style;
7525     const float w = CalcItemWidth();
7526 
7527     const ImVec2 label_size = CalcTextSize(label, NULL, true);
7528     const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2));
7529     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);
7530     ItemSize(total_bb, style.FramePadding.y);
7531     if (!ItemAdd(total_bb, 0))
7532         return;
7533 
7534     // Render
7535     const char* value_text_begin = &g.TempBuffer[0];
7536     const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
7537     RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f,0.5f));
7538     if (label_size.x > 0.0f)
7539         RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
7540 }
7541 
LabelText(const char * label,const char * fmt,...)7542 void ImGui::LabelText(const char* label, const char* fmt, ...)
7543 {
7544     va_list args;
7545     va_start(args, fmt);
7546     LabelTextV(label, fmt, args);
7547     va_end(args);
7548 }
7549 
ButtonBehavior(const ImRect & bb,ImGuiID id,bool * out_hovered,bool * out_held,ImGuiButtonFlags flags)7550 bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)
7551 {
7552     ImGuiContext& g = *GImGui;
7553     ImGuiWindow* window = GetCurrentWindow();
7554 
7555     if (flags & ImGuiButtonFlags_Disabled)
7556     {
7557         if (out_hovered) *out_hovered = false;
7558         if (out_held) *out_held = false;
7559         if (g.ActiveId == id) ClearActiveID();
7560         return false;
7561     }
7562 
7563     // Default behavior requires click+release on same spot
7564     if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0)
7565         flags |= ImGuiButtonFlags_PressedOnClickRelease;
7566 
7567     ImGuiWindow* backup_hovered_window = g.HoveredWindow;
7568     if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window)
7569         g.HoveredWindow = window;
7570 
7571     bool pressed = false;
7572     bool hovered = ItemHoverable(bb, id);
7573 
7574     // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button
7575     if ((flags & ImGuiButtonFlags_PressedOnDragDropHold) && g.DragDropActive && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers))
7576         if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
7577         {
7578             hovered = true;
7579             SetHoveredID(id);
7580             if (CalcTypematicPressedRepeatAmount(g.HoveredIdTimer + 0.0001f, g.HoveredIdTimer + 0.0001f - g.IO.DeltaTime, 0.01f, 0.70f)) // FIXME: Our formula for CalcTypematicPressedRepeatAmount() is fishy
7581             {
7582                 pressed = true;
7583                 FocusWindow(window);
7584             }
7585         }
7586 
7587     if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window)
7588         g.HoveredWindow = backup_hovered_window;
7589 
7590     // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one.
7591     if (hovered && (flags & ImGuiButtonFlags_AllowItemOverlap) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0))
7592         hovered = false;
7593 
7594     // Mouse
7595     if (hovered)
7596     {
7597         if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
7598         {
7599             //                        | CLICKING        | HOLDING with ImGuiButtonFlags_Repeat
7600             // PressedOnClickRelease  |  <on release>*  |  <on repeat> <on repeat> .. (NOT on release)  <-- MOST COMMON! (*) only if both click/release were over bounds
7601             // PressedOnClick         |  <on click>     |  <on click> <on repeat> <on repeat> ..
7602             // PressedOnRelease       |  <on release>   |  <on repeat> <on repeat> .. (NOT on release)
7603             // PressedOnDoubleClick   |  <on dclick>    |  <on dclick> <on repeat> <on repeat> ..
7604             // FIXME-NAV: We don't honor those different behaviors.
7605             if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0])
7606             {
7607                 SetActiveID(id, window);
7608                 if (!(flags & ImGuiButtonFlags_NoNavFocus))
7609                     SetFocusID(id, window);
7610                 FocusWindow(window);
7611             }
7612             if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0]))
7613             {
7614                 pressed = true;
7615                 if (flags & ImGuiButtonFlags_NoHoldingActiveID)
7616                     ClearActiveID();
7617                 else
7618                     SetActiveID(id, window); // Hold on ID
7619                 FocusWindow(window);
7620             }
7621             if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0])
7622             {
7623                 if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay))  // Repeat mode trumps <on release>
7624                     pressed = true;
7625                 ClearActiveID();
7626             }
7627 
7628             // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above).
7629             // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings.
7630             if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true))
7631                 pressed = true;
7632         }
7633 
7634         if (pressed)
7635             g.NavDisableHighlight = true;
7636     }
7637 
7638     // Gamepad/Keyboard navigation
7639     // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse.
7640     if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId))
7641         hovered = true;
7642 
7643     if (g.NavActivateDownId == id)
7644     {
7645         bool nav_activated_by_code = (g.NavActivateId == id);
7646         bool nav_activated_by_inputs = IsNavInputPressed(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiInputReadMode_Repeat : ImGuiInputReadMode_Pressed);
7647         if (nav_activated_by_code || nav_activated_by_inputs)
7648             pressed = true;
7649         if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id)
7650         {
7651             // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button.
7652             g.NavActivateId = id; // This is so SetActiveId assign a Nav source
7653             SetActiveID(id, window);
7654             if (!(flags & ImGuiButtonFlags_NoNavFocus))
7655                 SetFocusID(id, window);
7656             g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
7657         }
7658     }
7659 
7660     bool held = false;
7661     if (g.ActiveId == id)
7662     {
7663         if (g.ActiveIdSource == ImGuiInputSource_Mouse)
7664         {
7665             if (g.ActiveIdIsJustActivated)
7666                 g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;
7667             if (g.IO.MouseDown[0])
7668             {
7669                 held = true;
7670             }
7671             else
7672             {
7673                 if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease))
7674                     if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay))  // Repeat mode trumps <on release>
7675                         if (!g.DragDropActive)
7676                             pressed = true;
7677                 ClearActiveID();
7678             }
7679             if (!(flags & ImGuiButtonFlags_NoNavFocus))
7680                 g.NavDisableHighlight = true;
7681         }
7682         else if (g.ActiveIdSource == ImGuiInputSource_Nav)
7683         {
7684             if (g.NavActivateDownId != id)
7685                 ClearActiveID();
7686         }
7687     }
7688 
7689     if (out_hovered) *out_hovered = hovered;
7690     if (out_held) *out_held = held;
7691 
7692     return pressed;
7693 }
7694 
ButtonEx(const char * label,const ImVec2 & size_arg,ImGuiButtonFlags flags)7695 bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags)
7696 {
7697     ImGuiWindow* window = GetCurrentWindow();
7698     if (window->SkipItems)
7699         return false;
7700 
7701     ImGuiContext& g = *GImGui;
7702     const ImGuiStyle& style = g.Style;
7703     const ImGuiID id = window->GetID(label);
7704     const ImVec2 label_size = CalcTextSize(label, NULL, true);
7705 
7706     ImVec2 pos = window->DC.CursorPos;
7707     if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
7708         pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y;
7709     ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);
7710 
7711     const ImRect bb(pos, pos + size);
7712     ItemSize(bb, style.FramePadding.y);
7713     if (!ItemAdd(bb, id))
7714         return false;
7715 
7716     if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat)
7717         flags |= ImGuiButtonFlags_Repeat;
7718     bool hovered, held;
7719     bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
7720 
7721     // Render
7722     const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
7723     RenderNavHighlight(bb, id);
7724     RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
7725     RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb);
7726 
7727     // Automatically close popups
7728     //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
7729     //    CloseCurrentPopup();
7730 
7731     return pressed;
7732 }
7733 
Button(const char * label,const ImVec2 & size_arg)7734 bool ImGui::Button(const char* label, const ImVec2& size_arg)
7735 {
7736     return ButtonEx(label, size_arg, 0);
7737 }
7738 
7739 // Small buttons fits within text without additional vertical spacing.
SmallButton(const char * label)7740 bool ImGui::SmallButton(const char* label)
7741 {
7742     ImGuiContext& g = *GImGui;
7743     float backup_padding_y = g.Style.FramePadding.y;
7744     g.Style.FramePadding.y = 0.0f;
7745     bool pressed = ButtonEx(label, ImVec2(0,0), ImGuiButtonFlags_AlignTextBaseLine);
7746     g.Style.FramePadding.y = backup_padding_y;
7747     return pressed;
7748 }
7749 
ArrowButton(const char * str_id,ImGuiDir dir)7750 bool ImGui::ArrowButton(const char* str_id, ImGuiDir dir)
7751 {
7752     ImGuiWindow* window = GetCurrentWindow();
7753     if (window->SkipItems)
7754         return false;
7755 
7756     ImGuiContext& g = *GImGui;
7757     const ImGuiID id = window->GetID(str_id);
7758     float sz = ImGui::GetFrameHeight();
7759     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(sz, sz));
7760     ItemSize(bb);
7761     if (!ItemAdd(bb, id))
7762         return false;
7763 
7764     bool hovered, held;
7765     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
7766 
7767     // Render
7768     const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
7769     RenderNavHighlight(bb, id);
7770     RenderFrame(bb.Min, bb.Max, col, true, g.Style.FrameRounding);
7771     RenderArrow(bb.Min + g.Style.FramePadding, dir);
7772 
7773     return pressed;
7774 }
7775 
7776 // Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack.
7777 // 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)7778 bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg)
7779 {
7780     ImGuiWindow* window = GetCurrentWindow();
7781     if (window->SkipItems)
7782         return false;
7783 
7784     const ImGuiID id = window->GetID(str_id);
7785     ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f);
7786     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
7787     ItemSize(bb);
7788     if (!ItemAdd(bb, id))
7789         return false;
7790 
7791     bool hovered, held;
7792     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
7793 
7794     return pressed;
7795 }
7796 
7797 // Button to close a window
CloseButton(ImGuiID id,const ImVec2 & pos,float radius)7798 bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius)
7799 {
7800     ImGuiContext& g = *GImGui;
7801     ImGuiWindow* window = g.CurrentWindow;
7802 
7803     // We intentionally allow interaction when clipped so that a mechanical Alt,Right,Validate sequence close a window.
7804     // (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible).
7805     const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius));
7806     bool is_clipped = !ItemAdd(bb, id);
7807 
7808     bool hovered, held;
7809     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
7810     if (is_clipped)
7811         return pressed;
7812 
7813     // Render
7814     ImVec2 center = bb.GetCenter();
7815     if (hovered)
7816         window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered), 9);
7817 
7818     float cross_extent = (radius * 0.7071f) - 1.0f;
7819     ImU32 cross_col = GetColorU32(ImGuiCol_Text);
7820     center -= ImVec2(0.5f, 0.5f);
7821     window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), cross_col, 1.0f);
7822     window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), cross_col, 1.0f);
7823 
7824     return pressed;
7825 }
7826 
Image(ImTextureID user_texture_id,const ImVec2 & size,const ImVec2 & uv0,const ImVec2 & uv1,const ImVec4 & tint_col,const ImVec4 & border_col)7827 void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
7828 {
7829     ImGuiWindow* window = GetCurrentWindow();
7830     if (window->SkipItems)
7831         return;
7832 
7833     ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
7834     if (border_col.w > 0.0f)
7835         bb.Max += ImVec2(2,2);
7836     ItemSize(bb);
7837     if (!ItemAdd(bb, 0))
7838         return;
7839 
7840     if (border_col.w > 0.0f)
7841     {
7842         window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f);
7843         window->DrawList->AddImage(user_texture_id, bb.Min+ImVec2(1,1), bb.Max-ImVec2(1,1), uv0, uv1, GetColorU32(tint_col));
7844     }
7845     else
7846     {
7847         window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col));
7848     }
7849 }
7850 
7851 // frame_padding < 0: uses FramePadding from style (default)
7852 // frame_padding = 0: no framing
7853 // frame_padding > 0: set framing size
7854 // 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)7855 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)
7856 {
7857     ImGuiWindow* window = GetCurrentWindow();
7858     if (window->SkipItems)
7859         return false;
7860 
7861     ImGuiContext& g = *GImGui;
7862     const ImGuiStyle& style = g.Style;
7863 
7864     // Default to using texture ID as ID. User can still push string/integer prefixes.
7865     // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV.
7866     PushID((void *)user_texture_id);
7867     const ImGuiID id = window->GetID("#image");
7868     PopID();
7869 
7870     const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding;
7871     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding*2);
7872     const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size);
7873     ItemSize(bb);
7874     if (!ItemAdd(bb, id))
7875         return false;
7876 
7877     bool hovered, held;
7878     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
7879 
7880     // Render
7881     const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
7882     RenderNavHighlight(bb, id);
7883     RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding));
7884     if (bg_col.w > 0.0f)
7885         window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col));
7886     window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col));
7887 
7888     return pressed;
7889 }
7890 
7891 // Start logging ImGui output to TTY
LogToTTY(int max_depth)7892 void ImGui::LogToTTY(int max_depth)
7893 {
7894     ImGuiContext& g = *GImGui;
7895     if (g.LogEnabled)
7896         return;
7897     ImGuiWindow* window = g.CurrentWindow;
7898 
7899     IM_ASSERT(g.LogFile == NULL);
7900     g.LogFile = stdout;
7901     g.LogEnabled = true;
7902     g.LogStartDepth = window->DC.TreeDepth;
7903     if (max_depth >= 0)
7904         g.LogAutoExpandMaxDepth = max_depth;
7905 }
7906 
7907 // Start logging ImGui output to given file
LogToFile(int max_depth,const char * filename)7908 void ImGui::LogToFile(int max_depth, const char* filename)
7909 {
7910     ImGuiContext& g = *GImGui;
7911     if (g.LogEnabled)
7912         return;
7913     ImGuiWindow* window = g.CurrentWindow;
7914 
7915     if (!filename)
7916     {
7917         filename = g.IO.LogFilename;
7918         if (!filename)
7919             return;
7920     }
7921 
7922     IM_ASSERT(g.LogFile == NULL);
7923     g.LogFile = ImFileOpen(filename, "ab");
7924     if (!g.LogFile)
7925     {
7926         IM_ASSERT(g.LogFile != NULL); // Consider this an error
7927         return;
7928     }
7929     g.LogEnabled = true;
7930     g.LogStartDepth = window->DC.TreeDepth;
7931     if (max_depth >= 0)
7932         g.LogAutoExpandMaxDepth = max_depth;
7933 }
7934 
7935 // Start logging ImGui output to clipboard
LogToClipboard(int max_depth)7936 void ImGui::LogToClipboard(int max_depth)
7937 {
7938     ImGuiContext& g = *GImGui;
7939     if (g.LogEnabled)
7940         return;
7941     ImGuiWindow* window = g.CurrentWindow;
7942 
7943     IM_ASSERT(g.LogFile == NULL);
7944     g.LogFile = NULL;
7945     g.LogEnabled = true;
7946     g.LogStartDepth = window->DC.TreeDepth;
7947     if (max_depth >= 0)
7948         g.LogAutoExpandMaxDepth = max_depth;
7949 }
7950 
LogFinish()7951 void ImGui::LogFinish()
7952 {
7953     ImGuiContext& g = *GImGui;
7954     if (!g.LogEnabled)
7955         return;
7956 
7957     LogText(IM_NEWLINE);
7958     if (g.LogFile != NULL)
7959     {
7960         if (g.LogFile == stdout)
7961             fflush(g.LogFile);
7962         else
7963             fclose(g.LogFile);
7964         g.LogFile = NULL;
7965     }
7966     if (g.LogClipboard->size() > 1)
7967     {
7968         SetClipboardText(g.LogClipboard->begin());
7969         g.LogClipboard->clear();
7970     }
7971     g.LogEnabled = false;
7972 }
7973 
7974 // Helper to display logging buttons
LogButtons()7975 void ImGui::LogButtons()
7976 {
7977     ImGuiContext& g = *GImGui;
7978 
7979     PushID("LogButtons");
7980     const bool log_to_tty = Button("Log To TTY"); SameLine();
7981     const bool log_to_file = Button("Log To File"); SameLine();
7982     const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
7983     PushItemWidth(80.0f);
7984     PushAllowKeyboardFocus(false);
7985     SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL);
7986     PopAllowKeyboardFocus();
7987     PopItemWidth();
7988     PopID();
7989 
7990     // Start logging at the end of the function so that the buttons don't appear in the log
7991     if (log_to_tty)
7992         LogToTTY(g.LogAutoExpandMaxDepth);
7993     if (log_to_file)
7994         LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename);
7995     if (log_to_clipboard)
7996         LogToClipboard(g.LogAutoExpandMaxDepth);
7997 }
7998 
TreeNodeBehaviorIsOpen(ImGuiID id,ImGuiTreeNodeFlags flags)7999 bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags)
8000 {
8001     if (flags & ImGuiTreeNodeFlags_Leaf)
8002         return true;
8003 
8004     // We only write to the tree storage if the user clicks (or explicitely use SetNextTreeNode*** functions)
8005     ImGuiContext& g = *GImGui;
8006     ImGuiWindow* window = g.CurrentWindow;
8007     ImGuiStorage* storage = window->DC.StateStorage;
8008 
8009     bool is_open;
8010     if (g.NextTreeNodeOpenCond != 0)
8011     {
8012         if (g.NextTreeNodeOpenCond & ImGuiCond_Always)
8013         {
8014             is_open = g.NextTreeNodeOpenVal;
8015             storage->SetInt(id, is_open);
8016         }
8017         else
8018         {
8019             // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently.
8020             const int stored_value = storage->GetInt(id, -1);
8021             if (stored_value == -1)
8022             {
8023                 is_open = g.NextTreeNodeOpenVal;
8024                 storage->SetInt(id, is_open);
8025             }
8026             else
8027             {
8028                 is_open = stored_value != 0;
8029             }
8030         }
8031         g.NextTreeNodeOpenCond = 0;
8032     }
8033     else
8034     {
8035         is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;
8036     }
8037 
8038     // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior).
8039     // NB- If we are above max depth we still allow manually opened nodes to be logged.
8040     if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth)
8041         is_open = true;
8042 
8043     return is_open;
8044 }
8045 
TreeNodeBehavior(ImGuiID id,ImGuiTreeNodeFlags flags,const char * label,const char * label_end)8046 bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end)
8047 {
8048     ImGuiWindow* window = GetCurrentWindow();
8049     if (window->SkipItems)
8050         return false;
8051 
8052     ImGuiContext& g = *GImGui;
8053     const ImGuiStyle& style = g.Style;
8054     const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;
8055     const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f);
8056 
8057     if (!label_end)
8058         label_end = FindRenderedTextEnd(label);
8059     const ImVec2 label_size = CalcTextSize(label, label_end, false);
8060 
8061     // We vertically grow up to current line height up the typical widget height.
8062     const float text_base_offset_y = ImMax(padding.y, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
8063     const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2);
8064     ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height));
8065     if (display_frame)
8066     {
8067         // Framed header expand a little outside the default padding
8068         frame_bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1;
8069         frame_bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1;
8070     }
8071 
8072     const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2));   // Collapser arrow width + Spacing
8073     const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f);   // Include collapser
8074     ItemSize(ImVec2(text_width, frame_height), text_base_offset_y);
8075 
8076     // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
8077     // (Ideally we'd want to add a flag for the user to specify if we want the hit test to be done up to the right side of the content or not)
8078     const ImRect interact_bb = display_frame ? frame_bb : ImRect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + text_width + style.ItemSpacing.x*2, frame_bb.Max.y);
8079     bool is_open = TreeNodeBehaviorIsOpen(id, flags);
8080 
8081     // Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child.
8082     // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop().
8083     // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero.
8084     if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
8085         window->DC.TreeDepthMayJumpToParentOnPop |= (1 << window->DC.TreeDepth);
8086 
8087     bool item_add = ItemAdd(interact_bb, id);
8088     window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDisplayRect;
8089     window->DC.LastItemDisplayRect = frame_bb;
8090 
8091     if (!item_add)
8092     {
8093         if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
8094             TreePushRawID(id);
8095         return is_open;
8096     }
8097 
8098     // Flags that affects opening behavior:
8099     // - 0(default) ..................... single-click anywhere to open
8100     // - OpenOnDoubleClick .............. double-click anywhere to open
8101     // - OpenOnArrow .................... single-click on arrow to open
8102     // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open
8103     ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowItemOverlap) ? ImGuiButtonFlags_AllowItemOverlap : 0);
8104     if (!(flags & ImGuiTreeNodeFlags_Leaf))
8105         button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
8106     if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
8107         button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0);
8108 
8109     bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags);
8110     if (!(flags & ImGuiTreeNodeFlags_Leaf))
8111     {
8112         bool toggled = false;
8113         if (pressed)
8114         {
8115             toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) || (g.NavActivateId == id);
8116             if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
8117                 toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)) && (!g.NavDisableMouseHover);
8118             if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
8119                 toggled |= g.IO.MouseDoubleClicked[0];
8120             if (g.DragDropActive && is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again.
8121                 toggled = false;
8122         }
8123 
8124         if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open)
8125         {
8126             toggled = true;
8127             NavMoveRequestCancel();
8128         }
8129         if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority?
8130         {
8131             toggled = true;
8132             NavMoveRequestCancel();
8133         }
8134 
8135         if (toggled)
8136         {
8137             is_open = !is_open;
8138             window->DC.StateStorage->SetInt(id, is_open);
8139         }
8140     }
8141     if (flags & ImGuiTreeNodeFlags_AllowItemOverlap)
8142         SetItemAllowOverlap();
8143 
8144     // Render
8145     const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
8146     const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y);
8147     if (display_frame)
8148     {
8149         // Framed type
8150         RenderFrame(frame_bb.Min, frame_bb.Max, col, true, style.FrameRounding);
8151         RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin);
8152         RenderArrow(frame_bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f);
8153         if (g.LogEnabled)
8154         {
8155             // 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.
8156             const char log_prefix[] = "\n##";
8157             const char log_suffix[] = "##";
8158             LogRenderedText(&text_pos, log_prefix, log_prefix+3);
8159             RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
8160             LogRenderedText(&text_pos, log_suffix+1, log_suffix+3);
8161         }
8162         else
8163         {
8164             RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
8165         }
8166     }
8167     else
8168     {
8169         // Unframed typed for tree nodes
8170         if (hovered || (flags & ImGuiTreeNodeFlags_Selected))
8171         {
8172             RenderFrame(frame_bb.Min, frame_bb.Max, col, false);
8173             RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin);
8174         }
8175 
8176         if (flags & ImGuiTreeNodeFlags_Bullet)
8177             RenderBullet(frame_bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y));
8178         else if (!(flags & ImGuiTreeNodeFlags_Leaf))
8179             RenderArrow(frame_bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f);
8180         if (g.LogEnabled)
8181             LogRenderedText(&text_pos, ">");
8182         RenderText(text_pos, label, label_end, false);
8183     }
8184 
8185     if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
8186         TreePushRawID(id);
8187     return is_open;
8188 }
8189 
8190 // CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag).
8191 // This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode().
CollapsingHeader(const char * label,ImGuiTreeNodeFlags flags)8192 bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags)
8193 {
8194     ImGuiWindow* window = GetCurrentWindow();
8195     if (window->SkipItems)
8196         return false;
8197 
8198     return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen, label);
8199 }
8200 
CollapsingHeader(const char * label,bool * p_open,ImGuiTreeNodeFlags flags)8201 bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags)
8202 {
8203     ImGuiWindow* window = GetCurrentWindow();
8204     if (window->SkipItems)
8205         return false;
8206 
8207     if (p_open && !*p_open)
8208         return false;
8209 
8210     ImGuiID id = window->GetID(label);
8211     bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen | (p_open ? ImGuiTreeNodeFlags_AllowItemOverlap : 0), label);
8212     if (p_open)
8213     {
8214         // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.
8215         ImGuiContext& g = *GImGui;
8216         float button_sz = g.FontSize * 0.5f;
8217         ImGuiItemHoveredDataBackup last_item_backup;
8218         if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_sz, window->DC.LastItemRect.Min.y + g.Style.FramePadding.y + button_sz), button_sz))
8219             *p_open = false;
8220         last_item_backup.Restore();
8221     }
8222 
8223     return is_open;
8224 }
8225 
TreeNodeEx(const char * label,ImGuiTreeNodeFlags flags)8226 bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags)
8227 {
8228     ImGuiWindow* window = GetCurrentWindow();
8229     if (window->SkipItems)
8230         return false;
8231 
8232     return TreeNodeBehavior(window->GetID(label), flags, label, NULL);
8233 }
8234 
TreeNodeExV(const char * str_id,ImGuiTreeNodeFlags flags,const char * fmt,va_list args)8235 bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
8236 {
8237     ImGuiWindow* window = GetCurrentWindow();
8238     if (window->SkipItems)
8239         return false;
8240 
8241     ImGuiContext& g = *GImGui;
8242     const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
8243     return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end);
8244 }
8245 
TreeNodeExV(const void * ptr_id,ImGuiTreeNodeFlags flags,const char * fmt,va_list args)8246 bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
8247 {
8248     ImGuiWindow* window = GetCurrentWindow();
8249     if (window->SkipItems)
8250         return false;
8251 
8252     ImGuiContext& g = *GImGui;
8253     const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
8254     return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end);
8255 }
8256 
TreeNodeV(const char * str_id,const char * fmt,va_list args)8257 bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)
8258 {
8259     return TreeNodeExV(str_id, 0, fmt, args);
8260 }
8261 
TreeNodeV(const void * ptr_id,const char * fmt,va_list args)8262 bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)
8263 {
8264     return TreeNodeExV(ptr_id, 0, fmt, args);
8265 }
8266 
TreeNodeEx(const char * str_id,ImGuiTreeNodeFlags flags,const char * fmt,...)8267 bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
8268 {
8269     va_list args;
8270     va_start(args, fmt);
8271     bool is_open = TreeNodeExV(str_id, flags, fmt, args);
8272     va_end(args);
8273     return is_open;
8274 }
8275 
TreeNodeEx(const void * ptr_id,ImGuiTreeNodeFlags flags,const char * fmt,...)8276 bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
8277 {
8278     va_list args;
8279     va_start(args, fmt);
8280     bool is_open = TreeNodeExV(ptr_id, flags, fmt, args);
8281     va_end(args);
8282     return is_open;
8283 }
8284 
TreeNode(const char * str_id,const char * fmt,...)8285 bool ImGui::TreeNode(const char* str_id, const char* fmt, ...)
8286 {
8287     va_list args;
8288     va_start(args, fmt);
8289     bool is_open = TreeNodeExV(str_id, 0, fmt, args);
8290     va_end(args);
8291     return is_open;
8292 }
8293 
TreeNode(const void * ptr_id,const char * fmt,...)8294 bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)
8295 {
8296     va_list args;
8297     va_start(args, fmt);
8298     bool is_open = TreeNodeExV(ptr_id, 0, fmt, args);
8299     va_end(args);
8300     return is_open;
8301 }
8302 
TreeNode(const char * label)8303 bool ImGui::TreeNode(const char* label)
8304 {
8305     ImGuiWindow* window = GetCurrentWindow();
8306     if (window->SkipItems)
8307         return false;
8308     return TreeNodeBehavior(window->GetID(label), 0, label, NULL);
8309 }
8310 
TreeAdvanceToLabelPos()8311 void ImGui::TreeAdvanceToLabelPos()
8312 {
8313     ImGuiContext& g = *GImGui;
8314     g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing();
8315 }
8316 
8317 // Horizontal distance preceding label when using TreeNode() or Bullet()
GetTreeNodeToLabelSpacing()8318 float ImGui::GetTreeNodeToLabelSpacing()
8319 {
8320     ImGuiContext& g = *GImGui;
8321     return g.FontSize + (g.Style.FramePadding.x * 2.0f);
8322 }
8323 
SetNextTreeNodeOpen(bool is_open,ImGuiCond cond)8324 void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiCond cond)
8325 {
8326     ImGuiContext& g = *GImGui;
8327     if (g.CurrentWindow->SkipItems)
8328         return;
8329     g.NextTreeNodeOpenVal = is_open;
8330     g.NextTreeNodeOpenCond = cond ? cond : ImGuiCond_Always;
8331 }
8332 
PushID(const char * str_id)8333 void ImGui::PushID(const char* str_id)
8334 {
8335     ImGuiWindow* window = GetCurrentWindowRead();
8336     window->IDStack.push_back(window->GetID(str_id));
8337 }
8338 
PushID(const char * str_id_begin,const char * str_id_end)8339 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
8340 {
8341     ImGuiWindow* window = GetCurrentWindowRead();
8342     window->IDStack.push_back(window->GetID(str_id_begin, str_id_end));
8343 }
8344 
PushID(const void * ptr_id)8345 void ImGui::PushID(const void* ptr_id)
8346 {
8347     ImGuiWindow* window = GetCurrentWindowRead();
8348     window->IDStack.push_back(window->GetID(ptr_id));
8349 }
8350 
PushID(int int_id)8351 void ImGui::PushID(int int_id)
8352 {
8353     const void* ptr_id = (void*)(intptr_t)int_id;
8354     ImGuiWindow* window = GetCurrentWindowRead();
8355     window->IDStack.push_back(window->GetID(ptr_id));
8356 }
8357 
PopID()8358 void ImGui::PopID()
8359 {
8360     ImGuiWindow* window = GetCurrentWindowRead();
8361     window->IDStack.pop_back();
8362 }
8363 
GetID(const char * str_id)8364 ImGuiID ImGui::GetID(const char* str_id)
8365 {
8366     return GImGui->CurrentWindow->GetID(str_id);
8367 }
8368 
GetID(const char * str_id_begin,const char * str_id_end)8369 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
8370 {
8371     return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end);
8372 }
8373 
GetID(const void * ptr_id)8374 ImGuiID ImGui::GetID(const void* ptr_id)
8375 {
8376     return GImGui->CurrentWindow->GetID(ptr_id);
8377 }
8378 
Bullet()8379 void ImGui::Bullet()
8380 {
8381     ImGuiWindow* window = GetCurrentWindow();
8382     if (window->SkipItems)
8383         return;
8384 
8385     ImGuiContext& g = *GImGui;
8386     const ImGuiStyle& style = g.Style;
8387     const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
8388     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height));
8389     ItemSize(bb);
8390     if (!ItemAdd(bb, 0))
8391     {
8392         SameLine(0, style.FramePadding.x*2);
8393         return;
8394     }
8395 
8396     // Render and stay on same line
8397     RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
8398     SameLine(0, style.FramePadding.x*2);
8399 }
8400 
8401 // Text with a little bullet aligned to the typical tree node.
BulletTextV(const char * fmt,va_list args)8402 void ImGui::BulletTextV(const char* fmt, va_list args)
8403 {
8404     ImGuiWindow* window = GetCurrentWindow();
8405     if (window->SkipItems)
8406         return;
8407 
8408     ImGuiContext& g = *GImGui;
8409     const ImGuiStyle& style = g.Style;
8410 
8411     const char* text_begin = g.TempBuffer;
8412     const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
8413     const ImVec2 label_size = CalcTextSize(text_begin, text_end, false);
8414     const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
8415     const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
8416     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
8417     ItemSize(bb);
8418     if (!ItemAdd(bb, 0))
8419         return;
8420 
8421     // Render
8422     RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
8423     RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end, false);
8424 }
8425 
BulletText(const char * fmt,...)8426 void ImGui::BulletText(const char* fmt, ...)
8427 {
8428     va_list args;
8429     va_start(args, fmt);
8430     BulletTextV(fmt, args);
8431     va_end(args);
8432 }
8433 
DataTypeFormatString(ImGuiDataType data_type,void * data_ptr,const char * display_format,char * buf,int buf_size)8434 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size)
8435 {
8436     if (data_type == ImGuiDataType_Int)
8437         ImFormatString(buf, buf_size, display_format, *(int*)data_ptr);
8438     else if (data_type == ImGuiDataType_Float)
8439         ImFormatString(buf, buf_size, display_format, *(float*)data_ptr);
8440     else if (data_type == ImGuiDataType_Double)
8441         ImFormatString(buf, buf_size, display_format, *(double*)data_ptr);
8442 }
8443 
DataTypeFormatString(ImGuiDataType data_type,void * data_ptr,int decimal_precision,char * buf,int buf_size)8444 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size)
8445 {
8446     if (data_type == ImGuiDataType_Int)
8447     {
8448         if (decimal_precision < 0)
8449             ImFormatString(buf, buf_size, "%d", *(int*)data_ptr);
8450         else
8451             ImFormatString(buf, buf_size, "%.*d", decimal_precision, *(int*)data_ptr);
8452     }
8453     else if (data_type == ImGuiDataType_Float)
8454     {
8455         if (decimal_precision < 0)
8456             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?
8457         else
8458             ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(float*)data_ptr);
8459     }
8460     else if (data_type == ImGuiDataType_Double)
8461     {
8462         if (decimal_precision < 0)
8463             ImFormatString(buf, buf_size, "%f", *(double*)data_ptr);
8464         else
8465             ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(double*)data_ptr);
8466     }
8467 }
8468 
DataTypeApplyOp(ImGuiDataType data_type,int op,void * output,void * arg1,const void * arg2)8469 static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2)
8470 {
8471     IM_ASSERT(op == '+' || op == '-');
8472     if (data_type == ImGuiDataType_Int)
8473     {
8474         if (op == '+')      *(int*)output = *(int*)arg1 + *(const int*)arg2;
8475         else if (op == '-') *(int*)output = *(int*)arg1 - *(const int*)arg2;
8476     }
8477     else if (data_type == ImGuiDataType_Float)
8478     {
8479         if (op == '+')      *(float*)output = *(float*)arg1 + *(const float*)arg2;
8480         else if (op == '-') *(float*)output = *(float*)arg1 - *(const float*)arg2;
8481     }
8482     else if (data_type == ImGuiDataType_Double)
8483     {
8484         if (op == '+')      *(double*)output = *(double*)arg1 + *(const double*)arg2;
8485         else if (op == '-') *(double*)output = *(double*)arg1 - *(const double*)arg2;
8486     }
8487 }
8488 
8489 static size_t GDataTypeSize[ImGuiDataType_COUNT] =
8490 {
8491     sizeof(int),
8492     sizeof(float),
8493     sizeof(double)
8494 };
8495 
8496 // User can input math operators (e.g. +100) to edit a numerical values.
8497 // NB: This is _not_ a full expression evaluator. We should probably add one though..
DataTypeApplyOpFromText(const char * buf,const char * initial_value_buf,ImGuiDataType data_type,void * data_ptr,const char * scalar_format)8498 static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format)
8499 {
8500     while (ImCharIsSpace((unsigned int)*buf))
8501         buf++;
8502 
8503     // We don't support '-' op because it would conflict with inputing negative value.
8504     // Instead you can use +-100 to subtract from an existing value
8505     char op = buf[0];
8506     if (op == '+' || op == '*' || op == '/')
8507     {
8508         buf++;
8509         while (ImCharIsSpace((unsigned int)*buf))
8510             buf++;
8511     }
8512     else
8513     {
8514         op = 0;
8515     }
8516     if (!buf[0])
8517         return false;
8518 
8519     IM_ASSERT(data_type < ImGuiDataType_COUNT);
8520     int data_backup[2];
8521     IM_ASSERT(GDataTypeSize[data_type] <= sizeof(data_backup));
8522     memcpy(data_backup, data_ptr, GDataTypeSize[data_type]);
8523 
8524     if (data_type == ImGuiDataType_Int)
8525     {
8526         if (!scalar_format)
8527             scalar_format = "%d";
8528         int* v = (int*)data_ptr;
8529         int arg0i = *v;
8530         if (op && sscanf(initial_value_buf, scalar_format, &arg0i) < 1)
8531             return false;
8532         // 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
8533         float arg1f = 0.0f;
8534         if (op == '+')      { if (sscanf(buf, "%f", &arg1f) == 1) *v = (int)(arg0i + arg1f); }                 // Add (use "+-" to subtract)
8535         else if (op == '*') { if (sscanf(buf, "%f", &arg1f) == 1) *v = (int)(arg0i * arg1f); }                 // Multiply
8536         else if (op == '/') { if (sscanf(buf, "%f", &arg1f) == 1 && arg1f != 0.0f) *v = (int)(arg0i / arg1f); }// Divide
8537         else                { if (sscanf(buf, scalar_format, &arg0i) == 1) *v = arg0i; }                       // Assign integer constant
8538     }
8539     else if (data_type == ImGuiDataType_Float)
8540     {
8541         // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in
8542         scalar_format = "%f";
8543         float* v = (float*)data_ptr;
8544         float arg0f = *v, arg1f = 0.0f;
8545         if (op && sscanf(initial_value_buf, scalar_format, &arg0f) < 1)
8546             return false;
8547         if (sscanf(buf, scalar_format, &arg1f) < 1)
8548             return false;
8549         if (op == '+')      { *v = arg0f + arg1f; }                    // Add (use "+-" to subtract)
8550         else if (op == '*') { *v = arg0f * arg1f; }                    // Multiply
8551         else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide
8552         else                { *v = arg1f; }                            // Assign constant
8553     }
8554     else if (data_type == ImGuiDataType_Double)
8555     {
8556         scalar_format = "%lf";
8557         double* v = (double*)data_ptr;
8558         double arg0f = *v, arg1f = 0.0f;
8559         if (op && sscanf(initial_value_buf, scalar_format, &arg0f) < 1)
8560             return false;
8561         if (sscanf(buf, scalar_format, &arg1f) < 1)
8562             return false;
8563         if (op == '+')      { *v = arg0f + arg1f; }                    // Add (use "+-" to subtract)
8564         else if (op == '*') { *v = arg0f * arg1f; }                    // Multiply
8565         else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide
8566         else                { *v = arg1f; }                            // Assign constant
8567     }
8568     return memcmp(data_backup, data_ptr, GDataTypeSize[data_type]) != 0;
8569 }
8570 
8571 // Create text input in place of a slider (when CTRL+Clicking on slider)
8572 // FIXME: Logic is messy and confusing.
InputScalarAsWidgetReplacement(const ImRect & aabb,const char * label,ImGuiDataType data_type,void * data_ptr,ImGuiID id,int decimal_precision)8573 bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision)
8574 {
8575     ImGuiContext& g = *GImGui;
8576     ImGuiWindow* window = GetCurrentWindow();
8577 
8578     // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen)
8579     // On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id
8580     SetActiveID(g.ScalarAsInputTextId, window);
8581     g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
8582     SetHoveredID(0);
8583     FocusableItemUnregister(window);
8584 
8585     char buf[32];
8586     DataTypeFormatString(data_type, data_ptr, decimal_precision, buf, IM_ARRAYSIZE(buf));
8587     bool text_value_changed = InputTextEx(label, buf, IM_ARRAYSIZE(buf), aabb.GetSize(), ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll);
8588     if (g.ScalarAsInputTextId == 0)     // First frame we started displaying the InputText widget
8589     {
8590         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)
8591         g.ScalarAsInputTextId = g.ActiveId;
8592         SetHoveredID(id);
8593     }
8594     if (text_value_changed)
8595         return DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, NULL);
8596     return false;
8597 }
8598 
8599 // Parse display precision back from the display format string
ParseFormatPrecision(const char * fmt,int default_precision)8600 int ImGui::ParseFormatPrecision(const char* fmt, int default_precision)
8601 {
8602     int precision = default_precision;
8603     while ((fmt = strchr(fmt, '%')) != NULL)
8604     {
8605         fmt++;
8606         if (fmt[0] == '%') { fmt++; continue; } // Ignore "%%"
8607         while (*fmt >= '0' && *fmt <= '9')
8608             fmt++;
8609         if (*fmt == '.')
8610         {
8611             fmt = ImAtoi(fmt + 1, &precision);
8612             if (precision < 0 || precision > 10)
8613                 precision = default_precision;
8614         }
8615         if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation
8616             precision = -1;
8617         break;
8618     }
8619     return precision;
8620 }
8621 
GetMinimumStepAtDecimalPrecision(int decimal_precision)8622 static float GetMinimumStepAtDecimalPrecision(int decimal_precision)
8623 {
8624     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 };
8625     return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision);
8626 }
8627 
RoundScalar(float value,int decimal_precision)8628 float ImGui::RoundScalar(float value, int decimal_precision)
8629 {
8630     // Round past decimal precision
8631     // So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0
8632     // FIXME: Investigate better rounding methods
8633     if (decimal_precision < 0)
8634         return value;
8635     const float min_step = GetMinimumStepAtDecimalPrecision(decimal_precision);
8636     bool negative = value < 0.0f;
8637     value = fabsf(value);
8638     float remainder = fmodf(value, min_step);
8639     if (remainder <= min_step*0.5f)
8640         value -= remainder;
8641     else
8642         value += (min_step - remainder);
8643     return negative ? -value : value;
8644 }
8645 
SliderBehaviorCalcRatioFromValue(float v,float v_min,float v_max,float power,float linear_zero_pos)8646 static inline float SliderBehaviorCalcRatioFromValue(float v, float v_min, float v_max, float power, float linear_zero_pos)
8647 {
8648     if (v_min == v_max)
8649         return 0.0f;
8650 
8651     const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f);
8652     const float v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min);
8653     if (is_non_linear)
8654     {
8655         if (v_clamped < 0.0f)
8656         {
8657             const float f = 1.0f - (v_clamped - v_min) / (ImMin(0.0f,v_max) - v_min);
8658             return (1.0f - powf(f, 1.0f/power)) * linear_zero_pos;
8659         }
8660         else
8661         {
8662             const float f = (v_clamped - ImMax(0.0f,v_min)) / (v_max - ImMax(0.0f,v_min));
8663             return linear_zero_pos + powf(f, 1.0f/power) * (1.0f - linear_zero_pos);
8664         }
8665     }
8666 
8667     // Linear slider
8668     return (v_clamped - v_min) / (v_max - v_min);
8669 }
8670 
SliderBehavior(const ImRect & frame_bb,ImGuiID id,float * v,float v_min,float v_max,float power,int decimal_precision,ImGuiSliderFlags flags)8671 bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags)
8672 {
8673     ImGuiContext& g = *GImGui;
8674     ImGuiWindow* window = GetCurrentWindow();
8675     const ImGuiStyle& style = g.Style;
8676 
8677     // Draw frame
8678     const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
8679     RenderNavHighlight(frame_bb, id);
8680     RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
8681 
8682     const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f);
8683     const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0;
8684 
8685     const float grab_padding = 2.0f;
8686     const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f);
8687     float grab_sz;
8688     if (decimal_precision != 0)
8689         grab_sz = ImMin(style.GrabMinSize, slider_sz);
8690     else
8691         grab_sz = ImMin(ImMax(1.0f * (slider_sz / ((v_min < v_max ? v_max - v_min : v_min - v_max) + 1.0f)), style.GrabMinSize), slider_sz);  // Integer sliders, if possible have the grab size represent 1 unit
8692     const float slider_usable_sz = slider_sz - grab_sz;
8693     const float slider_usable_pos_min = (is_horizontal ? frame_bb.Min.x : frame_bb.Min.y) + grab_padding + grab_sz*0.5f;
8694     const float slider_usable_pos_max = (is_horizontal ? frame_bb.Max.x : frame_bb.Max.y) - grab_padding - grab_sz*0.5f;
8695 
8696     // For logarithmic sliders that cross over sign boundary we want the exponential increase to be symmetric around 0.0f
8697     float linear_zero_pos = 0.0f;   // 0.0->1.0f
8698     if (v_min * v_max < 0.0f)
8699     {
8700         // Different sign
8701         const float linear_dist_min_to_0 = powf(fabsf(0.0f - v_min), 1.0f/power);
8702         const float linear_dist_max_to_0 = powf(fabsf(v_max - 0.0f), 1.0f/power);
8703         linear_zero_pos = linear_dist_min_to_0 / (linear_dist_min_to_0+linear_dist_max_to_0);
8704     }
8705     else
8706     {
8707         // Same sign
8708         linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f;
8709     }
8710 
8711     // Process interacting with the slider
8712     bool value_changed = false;
8713     if (g.ActiveId == id)
8714     {
8715         bool set_new_value = false;
8716         float clicked_t = 0.0f;
8717         if (g.ActiveIdSource == ImGuiInputSource_Mouse)
8718         {
8719             if (!g.IO.MouseDown[0])
8720             {
8721                 ClearActiveID();
8722             }
8723             else
8724             {
8725                 const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
8726                 clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f;
8727                 if (!is_horizontal)
8728                     clicked_t = 1.0f - clicked_t;
8729                 set_new_value = true;
8730             }
8731         }
8732         else if (g.ActiveIdSource == ImGuiInputSource_Nav)
8733         {
8734             const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f);
8735             float delta = is_horizontal ? delta2.x : -delta2.y;
8736             if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)
8737             {
8738                 ClearActiveID();
8739             }
8740             else if (delta != 0.0f)
8741             {
8742                 clicked_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos);
8743                 if (decimal_precision == 0 && !is_non_linear)
8744                 {
8745                     if (fabsf(v_max - v_min) <= 100.0f || IsNavInputDown(ImGuiNavInput_TweakSlow))
8746                         delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (v_max - v_min); // Gamepad/keyboard tweak speeds in integer steps
8747                     else
8748                         delta /= 100.0f;
8749                 }
8750                 else
8751                 {
8752                     delta /= 100.0f;    // Gamepad/keyboard tweak speeds in % of slider bounds
8753                     if (IsNavInputDown(ImGuiNavInput_TweakSlow))
8754                         delta /= 10.0f;
8755                 }
8756                 if (IsNavInputDown(ImGuiNavInput_TweakFast))
8757                     delta *= 10.0f;
8758                 set_new_value = true;
8759                 if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits
8760                     set_new_value = false;
8761                 else
8762                     clicked_t = ImSaturate(clicked_t + delta);
8763             }
8764         }
8765 
8766         if (set_new_value)
8767         {
8768             float new_value;
8769             if (is_non_linear)
8770             {
8771                 // Account for logarithmic scale on both sides of the zero
8772                 if (clicked_t < linear_zero_pos)
8773                 {
8774                     // Negative: rescale to the negative range before powering
8775                     float a = 1.0f - (clicked_t / linear_zero_pos);
8776                     a = powf(a, power);
8777                     new_value = ImLerp(ImMin(v_max,0.0f), v_min, a);
8778                 }
8779                 else
8780                 {
8781                     // Positive: rescale to the positive range before powering
8782                     float a;
8783                     if (fabsf(linear_zero_pos - 1.0f) > 1.e-6f)
8784                         a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos);
8785                     else
8786                         a = clicked_t;
8787                     a = powf(a, power);
8788                     new_value = ImLerp(ImMax(v_min,0.0f), v_max, a);
8789                 }
8790             }
8791             else
8792             {
8793                 // Linear slider
8794                 new_value = ImLerp(v_min, v_max, clicked_t);
8795             }
8796 
8797             // Round past decimal precision
8798             new_value = RoundScalar(new_value, decimal_precision);
8799             if (*v != new_value)
8800             {
8801                 *v = new_value;
8802                 value_changed = true;
8803             }
8804         }
8805     }
8806 
8807     // Draw
8808     float grab_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos);
8809     if (!is_horizontal)
8810         grab_t = 1.0f - grab_t;
8811     const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
8812     ImRect grab_bb;
8813     if (is_horizontal)
8814         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));
8815     else
8816         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));
8817     window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
8818 
8819     return value_changed;
8820 }
8821 
8822 // Use power!=1.0 for logarithmic sliders.
8823 // Adjust display_format to decorate the value with a prefix or a suffix.
8824 //   "%.3f"         1.234
8825 //   "%5.2f secs"   01.23 secs
8826 //   "Gold: %.0f"   Gold: 1
SliderFloat(const char * label,float * v,float v_min,float v_max,const char * display_format,float power)8827 bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format, float power)
8828 {
8829     ImGuiWindow* window = GetCurrentWindow();
8830     if (window->SkipItems)
8831         return false;
8832 
8833     ImGuiContext& g = *GImGui;
8834     const ImGuiStyle& style = g.Style;
8835     const ImGuiID id = window->GetID(label);
8836     const float w = CalcItemWidth();
8837 
8838     const ImVec2 label_size = CalcTextSize(label, NULL, true);
8839     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
8840     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));
8841 
8842     // NB- we don't call ItemSize() yet because we may turn into a text edit box below
8843     if (!ItemAdd(total_bb, id, &frame_bb))
8844     {
8845         ItemSize(total_bb, style.FramePadding.y);
8846         return false;
8847     }
8848     const bool hovered = ItemHoverable(frame_bb, id);
8849 
8850     if (!display_format)
8851         display_format = "%.3f";
8852     int decimal_precision = ParseFormatPrecision(display_format, 3);
8853 
8854     // Tabbing or CTRL-clicking on Slider turns it into an input box
8855     bool start_text_input = false;
8856     const bool tab_focus_requested = FocusableItemRegister(window, id);
8857     if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id))
8858     {
8859         SetActiveID(id, window);
8860         SetFocusID(id, window);
8861         FocusWindow(window);
8862         g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
8863         if (tab_focus_requested || g.IO.KeyCtrl || g.NavInputId == id)
8864         {
8865             start_text_input = true;
8866             g.ScalarAsInputTextId = 0;
8867         }
8868     }
8869     if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
8870         return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
8871 
8872     // Actual slider behavior + render grab
8873     ItemSize(total_bb, style.FramePadding.y);
8874     const bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision);
8875 
8876     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
8877     char value_buf[64];
8878     const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
8879     RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
8880 
8881     if (label_size.x > 0.0f)
8882         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
8883 
8884     return value_changed;
8885 }
8886 
VSliderFloat(const char * label,const ImVec2 & size,float * v,float v_min,float v_max,const char * display_format,float power)8887 bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format, float power)
8888 {
8889     ImGuiWindow* window = GetCurrentWindow();
8890     if (window->SkipItems)
8891         return false;
8892 
8893     ImGuiContext& g = *GImGui;
8894     const ImGuiStyle& style = g.Style;
8895     const ImGuiID id = window->GetID(label);
8896 
8897     const ImVec2 label_size = CalcTextSize(label, NULL, true);
8898     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
8899     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));
8900 
8901     ItemSize(bb, style.FramePadding.y);
8902     if (!ItemAdd(frame_bb, id))
8903         return false;
8904     const bool hovered = ItemHoverable(frame_bb, id);
8905 
8906     if (!display_format)
8907         display_format = "%.3f";
8908     int decimal_precision = ParseFormatPrecision(display_format, 3);
8909 
8910     if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id)
8911     {
8912         SetActiveID(id, window);
8913         SetFocusID(id, window);
8914         FocusWindow(window);
8915         g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
8916     }
8917 
8918     // Actual slider behavior + render grab
8919     bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision, ImGuiSliderFlags_Vertical);
8920 
8921     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
8922     // For the vertical slider we allow centered text to overlap the frame padding
8923     char value_buf[64];
8924     char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
8925     RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.0f));
8926     if (label_size.x > 0.0f)
8927         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
8928 
8929     return value_changed;
8930 }
8931 
SliderAngle(const char * label,float * v_rad,float v_degrees_min,float v_degrees_max)8932 bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max)
8933 {
8934     float v_deg = (*v_rad) * 360.0f / (2*IM_PI);
8935     bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f);
8936     *v_rad = v_deg * (2*IM_PI) / 360.0f;
8937     return value_changed;
8938 }
8939 
SliderInt(const char * label,int * v,int v_min,int v_max,const char * display_format)8940 bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format)
8941 {
8942     if (!display_format)
8943         display_format = "%.0f";
8944     float v_f = (float)*v;
8945     bool value_changed = SliderFloat(label, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
8946     *v = (int)v_f;
8947     return value_changed;
8948 }
8949 
VSliderInt(const char * label,const ImVec2 & size,int * v,int v_min,int v_max,const char * display_format)8950 bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format)
8951 {
8952     if (!display_format)
8953         display_format = "%.0f";
8954     float v_f = (float)*v;
8955     bool value_changed = VSliderFloat(label, size, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
8956     *v = (int)v_f;
8957     return value_changed;
8958 }
8959 
8960 // 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)8961 bool ImGui::SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power)
8962 {
8963     ImGuiWindow* window = GetCurrentWindow();
8964     if (window->SkipItems)
8965         return false;
8966 
8967     ImGuiContext& g = *GImGui;
8968     bool value_changed = false;
8969     BeginGroup();
8970     PushID(label);
8971     PushMultiItemsWidths(components);
8972     for (int i = 0; i < components; i++)
8973     {
8974         PushID(i);
8975         value_changed |= SliderFloat("##v", &v[i], v_min, v_max, display_format, power);
8976         SameLine(0, g.Style.ItemInnerSpacing.x);
8977         PopID();
8978         PopItemWidth();
8979     }
8980     PopID();
8981 
8982     TextUnformatted(label, FindRenderedTextEnd(label));
8983     EndGroup();
8984 
8985     return value_changed;
8986 }
8987 
SliderFloat2(const char * label,float v[2],float v_min,float v_max,const char * display_format,float power)8988 bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format, float power)
8989 {
8990     return SliderFloatN(label, v, 2, v_min, v_max, display_format, power);
8991 }
8992 
SliderFloat3(const char * label,float v[3],float v_min,float v_max,const char * display_format,float power)8993 bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format, float power)
8994 {
8995     return SliderFloatN(label, v, 3, v_min, v_max, display_format, power);
8996 }
8997 
SliderFloat4(const char * label,float v[4],float v_min,float v_max,const char * display_format,float power)8998 bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format, float power)
8999 {
9000     return SliderFloatN(label, v, 4, v_min, v_max, display_format, power);
9001 }
9002 
SliderIntN(const char * label,int * v,int components,int v_min,int v_max,const char * display_format)9003 bool ImGui::SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format)
9004 {
9005     ImGuiWindow* window = GetCurrentWindow();
9006     if (window->SkipItems)
9007         return false;
9008 
9009     ImGuiContext& g = *GImGui;
9010     bool value_changed = false;
9011     BeginGroup();
9012     PushID(label);
9013     PushMultiItemsWidths(components);
9014     for (int i = 0; i < components; i++)
9015     {
9016         PushID(i);
9017         value_changed |= SliderInt("##v", &v[i], v_min, v_max, display_format);
9018         SameLine(0, g.Style.ItemInnerSpacing.x);
9019         PopID();
9020         PopItemWidth();
9021     }
9022     PopID();
9023 
9024     TextUnformatted(label, FindRenderedTextEnd(label));
9025     EndGroup();
9026 
9027     return value_changed;
9028 }
9029 
SliderInt2(const char * label,int v[2],int v_min,int v_max,const char * display_format)9030 bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format)
9031 {
9032     return SliderIntN(label, v, 2, v_min, v_max, display_format);
9033 }
9034 
SliderInt3(const char * label,int v[3],int v_min,int v_max,const char * display_format)9035 bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format)
9036 {
9037     return SliderIntN(label, v, 3, v_min, v_max, display_format);
9038 }
9039 
SliderInt4(const char * label,int v[4],int v_min,int v_max,const char * display_format)9040 bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format)
9041 {
9042     return SliderIntN(label, v, 4, v_min, v_max, display_format);
9043 }
9044 
DragBehavior(const ImRect & frame_bb,ImGuiID id,float * v,float v_speed,float v_min,float v_max,int decimal_precision,float power)9045 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)
9046 {
9047     ImGuiContext& g = *GImGui;
9048     const ImGuiStyle& style = g.Style;
9049 
9050     // Draw frame
9051     const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
9052     RenderNavHighlight(frame_bb, id);
9053     RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
9054 
9055     bool value_changed = false;
9056 
9057     // Process interacting with the drag
9058     if (g.ActiveId == id)
9059     {
9060         if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0])
9061             ClearActiveID();
9062         else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)
9063             ClearActiveID();
9064     }
9065     if (g.ActiveId == id)
9066     {
9067         if (g.ActiveIdIsJustActivated)
9068         {
9069             // Lock current value on click
9070             g.DragCurrentValue = *v;
9071             g.DragLastMouseDelta = ImVec2(0.f, 0.f);
9072         }
9073 
9074         if (v_speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX)
9075             v_speed = (v_max - v_min) * g.DragSpeedDefaultRatio;
9076 
9077         float v_cur = g.DragCurrentValue;
9078         const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f);
9079         float adjust_delta = 0.0f;
9080         if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid())
9081         {
9082             adjust_delta = mouse_drag_delta.x - g.DragLastMouseDelta.x;
9083             if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f)
9084                 adjust_delta *= g.DragSpeedScaleFast;
9085             if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f)
9086                 adjust_delta *= g.DragSpeedScaleSlow;
9087             g.DragLastMouseDelta.x = mouse_drag_delta.x;
9088         }
9089         if (g.ActiveIdSource == ImGuiInputSource_Nav)
9090         {
9091             adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard|ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x;
9092             if (v_min < v_max && ((v_cur >= v_max && adjust_delta > 0.0f) || (v_cur <= v_min && adjust_delta < 0.0f))) // This is to avoid applying the saturation when already past the limits
9093                 adjust_delta = 0.0f;
9094             v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision));
9095         }
9096         adjust_delta *= v_speed;
9097 
9098         if (fabsf(adjust_delta) > 0.0f)
9099         {
9100             if (fabsf(power - 1.0f) > 0.001f)
9101             {
9102                 // Logarithmic curve on both side of 0.0
9103                 float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur;
9104                 float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f;
9105                 float v1 = powf(v0_abs, 1.0f / power) + (adjust_delta * v0_sign);
9106                 float v1_abs = v1 >= 0.0f ? v1 : -v1;
9107                 float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f;          // Crossed sign line
9108                 v_cur = powf(v1_abs, power) * v0_sign * v1_sign;    // Reapply sign
9109             }
9110             else
9111             {
9112                 v_cur += adjust_delta;
9113             }
9114 
9115             // Clamp
9116             if (v_min < v_max)
9117                 v_cur = ImClamp(v_cur, v_min, v_max);
9118             g.DragCurrentValue = v_cur;
9119         }
9120 
9121         // Round to user desired precision, then apply
9122         v_cur = RoundScalar(v_cur, decimal_precision);
9123         if (*v != v_cur)
9124         {
9125             *v = v_cur;
9126             value_changed = true;
9127         }
9128     }
9129 
9130     return value_changed;
9131 }
9132 
DragFloat(const char * label,float * v,float v_speed,float v_min,float v_max,const char * display_format,float power)9133 bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* display_format, float power)
9134 {
9135     ImGuiWindow* window = GetCurrentWindow();
9136     if (window->SkipItems)
9137         return false;
9138 
9139     ImGuiContext& g = *GImGui;
9140     const ImGuiStyle& style = g.Style;
9141     const ImGuiID id = window->GetID(label);
9142     const float w = CalcItemWidth();
9143 
9144     const ImVec2 label_size = CalcTextSize(label, NULL, true);
9145     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
9146     const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
9147     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));
9148 
9149     // NB- we don't call ItemSize() yet because we may turn into a text edit box below
9150     if (!ItemAdd(total_bb, id, &frame_bb))
9151     {
9152         ItemSize(total_bb, style.FramePadding.y);
9153         return false;
9154     }
9155     const bool hovered = ItemHoverable(frame_bb, id);
9156 
9157     if (!display_format)
9158         display_format = "%.3f";
9159     int decimal_precision = ParseFormatPrecision(display_format, 3);
9160 
9161     // Tabbing or CTRL-clicking on Drag turns it into an input box
9162     bool start_text_input = false;
9163     const bool tab_focus_requested = FocusableItemRegister(window, id);
9164     if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id))
9165     {
9166         SetActiveID(id, window);
9167         SetFocusID(id, window);
9168         FocusWindow(window);
9169         g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
9170         if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id)
9171         {
9172             start_text_input = true;
9173             g.ScalarAsInputTextId = 0;
9174         }
9175     }
9176     if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
9177         return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
9178 
9179     // Actual drag behavior
9180     ItemSize(total_bb, style.FramePadding.y);
9181     const bool value_changed = DragBehavior(frame_bb, id, v, v_speed, v_min, v_max, decimal_precision, power);
9182 
9183     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
9184     char value_buf[64];
9185     const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
9186     RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
9187 
9188     if (label_size.x > 0.0f)
9189         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
9190 
9191     return value_changed;
9192 }
9193 
DragFloatN(const char * label,float * v,int components,float v_speed,float v_min,float v_max,const char * display_format,float power)9194 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)
9195 {
9196     ImGuiWindow* window = GetCurrentWindow();
9197     if (window->SkipItems)
9198         return false;
9199 
9200     ImGuiContext& g = *GImGui;
9201     bool value_changed = false;
9202     BeginGroup();
9203     PushID(label);
9204     PushMultiItemsWidths(components);
9205     for (int i = 0; i < components; i++)
9206     {
9207         PushID(i);
9208         value_changed |= DragFloat("##v", &v[i], v_speed, v_min, v_max, display_format, power);
9209         SameLine(0, g.Style.ItemInnerSpacing.x);
9210         PopID();
9211         PopItemWidth();
9212     }
9213     PopID();
9214 
9215     TextUnformatted(label, FindRenderedTextEnd(label));
9216     EndGroup();
9217 
9218     return value_changed;
9219 }
9220 
DragFloat2(const char * label,float v[2],float v_speed,float v_min,float v_max,const char * display_format,float power)9221 bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power)
9222 {
9223     return DragFloatN(label, v, 2, v_speed, v_min, v_max, display_format, power);
9224 }
9225 
DragFloat3(const char * label,float v[3],float v_speed,float v_min,float v_max,const char * display_format,float power)9226 bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* display_format, float power)
9227 {
9228     return DragFloatN(label, v, 3, v_speed, v_min, v_max, display_format, power);
9229 }
9230 
DragFloat4(const char * label,float v[4],float v_speed,float v_min,float v_max,const char * display_format,float power)9231 bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* display_format, float power)
9232 {
9233     return DragFloatN(label, v, 4, v_speed, v_min, v_max, display_format, power);
9234 }
9235 
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)9236 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)
9237 {
9238     ImGuiWindow* window = GetCurrentWindow();
9239     if (window->SkipItems)
9240         return false;
9241 
9242     ImGuiContext& g = *GImGui;
9243     PushID(label);
9244     BeginGroup();
9245     PushMultiItemsWidths(2);
9246 
9247     bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format, power);
9248     PopItemWidth();
9249     SameLine(0, g.Style.ItemInnerSpacing.x);
9250     value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, display_format_max ? display_format_max : display_format, power);
9251     PopItemWidth();
9252     SameLine(0, g.Style.ItemInnerSpacing.x);
9253 
9254     TextUnformatted(label, FindRenderedTextEnd(label));
9255     EndGroup();
9256     PopID();
9257 
9258     return value_changed;
9259 }
9260 
9261 // 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)9262 bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* display_format)
9263 {
9264     if (!display_format)
9265         display_format = "%.0f";
9266     float v_f = (float)*v;
9267     bool value_changed = DragFloat(label, &v_f, v_speed, (float)v_min, (float)v_max, display_format);
9268     *v = (int)v_f;
9269     return value_changed;
9270 }
9271 
DragIntN(const char * label,int * v,int components,float v_speed,int v_min,int v_max,const char * display_format)9272 bool ImGui::DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format)
9273 {
9274     ImGuiWindow* window = GetCurrentWindow();
9275     if (window->SkipItems)
9276         return false;
9277 
9278     ImGuiContext& g = *GImGui;
9279     bool value_changed = false;
9280     BeginGroup();
9281     PushID(label);
9282     PushMultiItemsWidths(components);
9283     for (int i = 0; i < components; i++)
9284     {
9285         PushID(i);
9286         value_changed |= DragInt("##v", &v[i], v_speed, v_min, v_max, display_format);
9287         SameLine(0, g.Style.ItemInnerSpacing.x);
9288         PopID();
9289         PopItemWidth();
9290     }
9291     PopID();
9292 
9293     TextUnformatted(label, FindRenderedTextEnd(label));
9294     EndGroup();
9295 
9296     return value_changed;
9297 }
9298 
DragInt2(const char * label,int v[2],float v_speed,int v_min,int v_max,const char * display_format)9299 bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* display_format)
9300 {
9301     return DragIntN(label, v, 2, v_speed, v_min, v_max, display_format);
9302 }
9303 
DragInt3(const char * label,int v[3],float v_speed,int v_min,int v_max,const char * display_format)9304 bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* display_format)
9305 {
9306     return DragIntN(label, v, 3, v_speed, v_min, v_max, display_format);
9307 }
9308 
DragInt4(const char * label,int v[4],float v_speed,int v_min,int v_max,const char * display_format)9309 bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* display_format)
9310 {
9311     return DragIntN(label, v, 4, v_speed, v_min, v_max, display_format);
9312 }
9313 
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)9314 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)
9315 {
9316     ImGuiWindow* window = GetCurrentWindow();
9317     if (window->SkipItems)
9318         return false;
9319 
9320     ImGuiContext& g = *GImGui;
9321     PushID(label);
9322     BeginGroup();
9323     PushMultiItemsWidths(2);
9324 
9325     bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format);
9326     PopItemWidth();
9327     SameLine(0, g.Style.ItemInnerSpacing.x);
9328     value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, display_format_max ? display_format_max : display_format);
9329     PopItemWidth();
9330     SameLine(0, g.Style.ItemInnerSpacing.x);
9331 
9332     TextUnformatted(label, FindRenderedTextEnd(label));
9333     EndGroup();
9334     PopID();
9335 
9336     return value_changed;
9337 }
9338 
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)9339 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)
9340 {
9341     ImGuiWindow* window = GetCurrentWindow();
9342     if (window->SkipItems)
9343         return;
9344 
9345     ImGuiContext& g = *GImGui;
9346     const ImGuiStyle& style = g.Style;
9347 
9348     const ImVec2 label_size = CalcTextSize(label, NULL, true);
9349     if (graph_size.x == 0.0f)
9350         graph_size.x = CalcItemWidth();
9351     if (graph_size.y == 0.0f)
9352         graph_size.y = label_size.y + (style.FramePadding.y * 2);
9353 
9354     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y));
9355     const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
9356     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));
9357     ItemSize(total_bb, style.FramePadding.y);
9358     if (!ItemAdd(total_bb, 0, &frame_bb))
9359         return;
9360     const bool hovered = ItemHoverable(inner_bb, 0);
9361 
9362     // Determine scale from values if not specified
9363     if (scale_min == FLT_MAX || scale_max == FLT_MAX)
9364     {
9365         float v_min = FLT_MAX;
9366         float v_max = -FLT_MAX;
9367         for (int i = 0; i < values_count; i++)
9368         {
9369             const float v = values_getter(data, i);
9370             v_min = ImMin(v_min, v);
9371             v_max = ImMax(v_max, v);
9372         }
9373         if (scale_min == FLT_MAX)
9374             scale_min = v_min;
9375         if (scale_max == FLT_MAX)
9376             scale_max = v_max;
9377     }
9378 
9379     RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
9380 
9381     if (values_count > 0)
9382     {
9383         int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
9384         int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
9385 
9386         // Tooltip on hover
9387         int v_hovered = -1;
9388         if (hovered)
9389         {
9390             const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);
9391             const int v_idx = (int)(t * item_count);
9392             IM_ASSERT(v_idx >= 0 && v_idx < values_count);
9393 
9394             const float v0 = values_getter(data, (v_idx + values_offset) % values_count);
9395             const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count);
9396             if (plot_type == ImGuiPlotType_Lines)
9397                 SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1);
9398             else if (plot_type == ImGuiPlotType_Histogram)
9399                 SetTooltip("%d: %8.4g", v_idx, v0);
9400             v_hovered = v_idx;
9401         }
9402 
9403         const float t_step = 1.0f / (float)res_w;
9404         const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min));
9405 
9406         float v0 = values_getter(data, (0 + values_offset) % values_count);
9407         float t0 = 0.0f;
9408         ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) );                       // Point in the normalized space of our target rectangle
9409         float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (-scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f);   // Where does the zero line stands
9410 
9411         const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram);
9412         const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered);
9413 
9414         for (int n = 0; n < res_w; n++)
9415         {
9416             const float t1 = t0 + t_step;
9417             const int v1_idx = (int)(t0 * item_count + 0.5f);
9418             IM_ASSERT(v1_idx >= 0 && v1_idx < values_count);
9419             const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count);
9420             const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale) );
9421 
9422             // 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.
9423             ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0);
9424             ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t));
9425             if (plot_type == ImGuiPlotType_Lines)
9426             {
9427                 window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
9428             }
9429             else if (plot_type == ImGuiPlotType_Histogram)
9430             {
9431                 if (pos1.x >= pos0.x + 2.0f)
9432                     pos1.x -= 1.0f;
9433                 window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
9434             }
9435 
9436             t0 = t1;
9437             tp0 = tp1;
9438         }
9439     }
9440 
9441     // Text overlay
9442     if (overlay_text)
9443         RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f,0.0f));
9444 
9445     if (label_size.x > 0.0f)
9446         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
9447 }
9448 
9449 struct ImGuiPlotArrayGetterData
9450 {
9451     const float* Values;
9452     int Stride;
9453 
ImGuiPlotArrayGetterDataImGuiPlotArrayGetterData9454     ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; }
9455 };
9456 
Plot_ArrayGetter(void * data,int idx)9457 static float Plot_ArrayGetter(void* data, int idx)
9458 {
9459     ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data;
9460     const float v = *(const float*)(const void*)((const unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);
9461     return v;
9462 }
9463 
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)9464 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)
9465 {
9466     ImGuiPlotArrayGetterData data(values, stride);
9467     PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
9468 }
9469 
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)9470 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)
9471 {
9472     PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
9473 }
9474 
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)9475 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)
9476 {
9477     ImGuiPlotArrayGetterData data(values, stride);
9478     PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
9479 }
9480 
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)9481 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)
9482 {
9483     PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
9484 }
9485 
9486 // 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)9487 void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay)
9488 {
9489     ImGuiWindow* window = GetCurrentWindow();
9490     if (window->SkipItems)
9491         return;
9492 
9493     ImGuiContext& g = *GImGui;
9494     const ImGuiStyle& style = g.Style;
9495 
9496     ImVec2 pos = window->DC.CursorPos;
9497     ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f));
9498     ItemSize(bb, style.FramePadding.y);
9499     if (!ItemAdd(bb, 0))
9500         return;
9501 
9502     // Render
9503     fraction = ImSaturate(fraction);
9504     RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
9505     bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize));
9506     const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y);
9507     RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding);
9508 
9509     // Default displaying the fraction as percentage string, but user can override it
9510     char overlay_buf[32];
9511     if (!overlay)
9512     {
9513         ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f);
9514         overlay = overlay_buf;
9515     }
9516 
9517     ImVec2 overlay_size = CalcTextSize(overlay, NULL);
9518     if (overlay_size.x > 0.0f)
9519         RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f,0.5f), &bb);
9520 }
9521 
Checkbox(const char * label,bool * v)9522 bool ImGui::Checkbox(const char* label, bool* v)
9523 {
9524     ImGuiWindow* window = GetCurrentWindow();
9525     if (window->SkipItems)
9526         return false;
9527 
9528     ImGuiContext& g = *GImGui;
9529     const ImGuiStyle& style = g.Style;
9530     const ImGuiID id = window->GetID(label);
9531     const ImVec2 label_size = CalcTextSize(label, NULL, true);
9532 
9533     const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2, label_size.y + style.FramePadding.y*2)); // We want a square shape to we use Y twice
9534     ItemSize(check_bb, style.FramePadding.y);
9535 
9536     ImRect total_bb = check_bb;
9537     if (label_size.x > 0)
9538         SameLine(0, style.ItemInnerSpacing.x);
9539     const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size);
9540     if (label_size.x > 0)
9541     {
9542         ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
9543         total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max));
9544     }
9545 
9546     if (!ItemAdd(total_bb, id))
9547         return false;
9548 
9549     bool hovered, held;
9550     bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
9551     if (pressed)
9552         *v = !(*v);
9553 
9554     RenderNavHighlight(total_bb, id);
9555     RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
9556     if (*v)
9557     {
9558         const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
9559         const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
9560         RenderCheckMark(check_bb.Min + ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), check_bb.GetWidth() - pad*2.0f);
9561     }
9562 
9563     if (g.LogEnabled)
9564         LogRenderedText(&text_bb.Min, *v ? "[x]" : "[ ]");
9565     if (label_size.x > 0.0f)
9566         RenderText(text_bb.Min, label);
9567 
9568     return pressed;
9569 }
9570 
CheckboxFlags(const char * label,unsigned int * flags,unsigned int flags_value)9571 bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value)
9572 {
9573     bool v = ((*flags & flags_value) == flags_value);
9574     bool pressed = Checkbox(label, &v);
9575     if (pressed)
9576     {
9577         if (v)
9578             *flags |= flags_value;
9579         else
9580             *flags &= ~flags_value;
9581     }
9582 
9583     return pressed;
9584 }
9585 
RadioButton(const char * label,bool active)9586 bool ImGui::RadioButton(const char* label, bool active)
9587 {
9588     ImGuiWindow* window = GetCurrentWindow();
9589     if (window->SkipItems)
9590         return false;
9591 
9592     ImGuiContext& g = *GImGui;
9593     const ImGuiStyle& style = g.Style;
9594     const ImGuiID id = window->GetID(label);
9595     const ImVec2 label_size = CalcTextSize(label, NULL, true);
9596 
9597     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));
9598     ItemSize(check_bb, style.FramePadding.y);
9599 
9600     ImRect total_bb = check_bb;
9601     if (label_size.x > 0)
9602         SameLine(0, style.ItemInnerSpacing.x);
9603     const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size);
9604     if (label_size.x > 0)
9605     {
9606         ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
9607         total_bb.Add(text_bb);
9608     }
9609 
9610     if (!ItemAdd(total_bb, id))
9611         return false;
9612 
9613     ImVec2 center = check_bb.GetCenter();
9614     center.x = (float)(int)center.x + 0.5f;
9615     center.y = (float)(int)center.y + 0.5f;
9616     const float radius = check_bb.GetHeight() * 0.5f;
9617 
9618     bool hovered, held;
9619     bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
9620 
9621     RenderNavHighlight(total_bb, id);
9622     window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16);
9623     if (active)
9624     {
9625         const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
9626         const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
9627         window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16);
9628     }
9629 
9630     if (style.FrameBorderSize > 0.0f)
9631     {
9632         window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize);
9633         window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize);
9634     }
9635 
9636     if (g.LogEnabled)
9637         LogRenderedText(&text_bb.Min, active ? "(x)" : "( )");
9638     if (label_size.x > 0.0f)
9639         RenderText(text_bb.Min, label);
9640 
9641     return pressed;
9642 }
9643 
RadioButton(const char * label,int * v,int v_button)9644 bool ImGui::RadioButton(const char* label, int* v, int v_button)
9645 {
9646     const bool pressed = RadioButton(label, *v == v_button);
9647     if (pressed)
9648     {
9649         *v = v_button;
9650     }
9651     return pressed;
9652 }
9653 
InputTextCalcTextLenAndLineCount(const char * text_begin,const char ** out_text_end)9654 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
9655 {
9656     int line_count = 0;
9657     const char* s = text_begin;
9658     while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding
9659         if (c == '\n')
9660             line_count++;
9661     s--;
9662     if (s[0] != '\n' && s[0] != '\r')
9663         line_count++;
9664     *out_text_end = s;
9665     return line_count;
9666 }
9667 
InputTextCalcTextSizeW(const ImWchar * text_begin,const ImWchar * text_end,const ImWchar ** remaining,ImVec2 * out_offset,bool stop_on_new_line)9668 static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line)
9669 {
9670     ImFont* font = GImGui->Font;
9671     const float line_height = GImGui->FontSize;
9672     const float scale = line_height / font->FontSize;
9673 
9674     ImVec2 text_size = ImVec2(0,0);
9675     float line_width = 0.0f;
9676 
9677     const ImWchar* s = text_begin;
9678     while (s < text_end)
9679     {
9680         unsigned int c = (unsigned int)(*s++);
9681         if (c == '\n')
9682         {
9683             text_size.x = ImMax(text_size.x, line_width);
9684             text_size.y += line_height;
9685             line_width = 0.0f;
9686             if (stop_on_new_line)
9687                 break;
9688             continue;
9689         }
9690         if (c == '\r')
9691             continue;
9692 
9693         const float char_width = font->GetCharAdvance((unsigned short)c) * scale;
9694         line_width += char_width;
9695     }
9696 
9697     if (text_size.x < line_width)
9698         text_size.x = line_width;
9699 
9700     if (out_offset)
9701         *out_offset = ImVec2(line_width, text_size.y + line_height);  // offset allow for the possibility of sitting after a trailing \n
9702 
9703     if (line_width > 0 || text_size.y == 0.0f)                        // whereas size.y will ignore the trailing \n
9704         text_size.y += line_height;
9705 
9706     if (remaining)
9707         *remaining = s;
9708 
9709     return text_size;
9710 }
9711 
9712 // 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)
9713 namespace ImGuiStb
9714 {
9715 
STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING * obj)9716 static int     STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj)                             { return obj->CurLenW; }
STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING * obj,int idx)9717 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)9718 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)9719 static int     STB_TEXTEDIT_KEYTOTEXT(int key)                                                    { return key >= 0x10000 ? 0 : key; }
9720 static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
STB_TEXTEDIT_LAYOUTROW(StbTexteditRow * r,STB_TEXTEDIT_STRING * obj,int line_start_idx)9721 static void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
9722 {
9723     const ImWchar* text = obj->Text.Data;
9724     const ImWchar* text_remaining = NULL;
9725     const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true);
9726     r->x0 = 0.0f;
9727     r->x1 = size.x;
9728     r->baseline_y_delta = size.y;
9729     r->ymin = 0.0f;
9730     r->ymax = size.y;
9731     r->num_chars = (int)(text_remaining - (text + line_start_idx));
9732 }
9733 
is_separator(unsigned int c)9734 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)9735 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)9736 static int  STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)   { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
9737 #ifdef __APPLE__    // FIXME: Move setting to IO structure
is_word_boundary_from_left(STB_TEXTEDIT_STRING * obj,int idx)9738 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)9739 static int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)  { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
9740 #else
STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING * obj,int idx)9741 static int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)  { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
9742 #endif
9743 #define STB_TEXTEDIT_MOVEWORDLEFT   STB_TEXTEDIT_MOVEWORDLEFT_IMPL    // They need to be #define for stb_textedit.h
9744 #define STB_TEXTEDIT_MOVEWORDRIGHT  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
9745 
STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING * obj,int pos,int n)9746 static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
9747 {
9748     ImWchar* dst = obj->Text.Data + pos;
9749 
9750     // We maintain our buffer length in both UTF-8 and wchar formats
9751     obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);
9752     obj->CurLenW -= n;
9753 
9754     // Offset remaining text
9755     const ImWchar* src = obj->Text.Data + pos + n;
9756     while (ImWchar c = *src++)
9757         *dst++ = c;
9758     *dst = '\0';
9759 }
9760 
STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING * obj,int pos,const ImWchar * new_text,int new_text_len)9761 static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len)
9762 {
9763     const int text_len = obj->CurLenW;
9764     IM_ASSERT(pos <= text_len);
9765     if (new_text_len + text_len + 1 > obj->Text.Size)
9766         return false;
9767 
9768     const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
9769     if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA)
9770         return false;
9771 
9772     ImWchar* text = obj->Text.Data;
9773     if (pos != text_len)
9774         memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));
9775     memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
9776 
9777     obj->CurLenW += new_text_len;
9778     obj->CurLenA += new_text_len_utf8;
9779     obj->Text[obj->CurLenW] = '\0';
9780 
9781     return true;
9782 }
9783 
9784 // 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)
9785 #define STB_TEXTEDIT_K_LEFT         0x10000 // keyboard input to move cursor left
9786 #define STB_TEXTEDIT_K_RIGHT        0x10001 // keyboard input to move cursor right
9787 #define STB_TEXTEDIT_K_UP           0x10002 // keyboard input to move cursor up
9788 #define STB_TEXTEDIT_K_DOWN         0x10003 // keyboard input to move cursor down
9789 #define STB_TEXTEDIT_K_LINESTART    0x10004 // keyboard input to move cursor to start of line
9790 #define STB_TEXTEDIT_K_LINEEND      0x10005 // keyboard input to move cursor to end of line
9791 #define STB_TEXTEDIT_K_TEXTSTART    0x10006 // keyboard input to move cursor to start of text
9792 #define STB_TEXTEDIT_K_TEXTEND      0x10007 // keyboard input to move cursor to end of text
9793 #define STB_TEXTEDIT_K_DELETE       0x10008 // keyboard input to delete selection or character under cursor
9794 #define STB_TEXTEDIT_K_BACKSPACE    0x10009 // keyboard input to delete selection or character left of cursor
9795 #define STB_TEXTEDIT_K_UNDO         0x1000A // keyboard input to perform undo
9796 #define STB_TEXTEDIT_K_REDO         0x1000B // keyboard input to perform redo
9797 #define STB_TEXTEDIT_K_WORDLEFT     0x1000C // keyboard input to move cursor left one word
9798 #define STB_TEXTEDIT_K_WORDRIGHT    0x1000D // keyboard input to move cursor right one word
9799 #define STB_TEXTEDIT_K_SHIFT        0x20000
9800 
9801 #define STB_TEXTEDIT_IMPLEMENTATION
9802 #include "stb_textedit.h"
9803 
9804 }
9805 
OnKeyPressed(int key)9806 void ImGuiTextEditState::OnKeyPressed(int key)
9807 {
9808     stb_textedit_key(this, &StbState, key);
9809     CursorFollow = true;
9810     CursorAnimReset();
9811 }
9812 
9813 // Public API to manipulate UTF-8 text
9814 // We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar)
9815 // FIXME: The existence of this rarely exercised code path is a bit of a nuisance.
DeleteChars(int pos,int bytes_count)9816 void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count)
9817 {
9818     IM_ASSERT(pos + bytes_count <= BufTextLen);
9819     char* dst = Buf + pos;
9820     const char* src = Buf + pos + bytes_count;
9821     while (char c = *src++)
9822         *dst++ = c;
9823     *dst = '\0';
9824 
9825     if (CursorPos + bytes_count >= pos)
9826         CursorPos -= bytes_count;
9827     else if (CursorPos >= pos)
9828         CursorPos = pos;
9829     SelectionStart = SelectionEnd = CursorPos;
9830     BufDirty = true;
9831     BufTextLen -= bytes_count;
9832 }
9833 
InsertChars(int pos,const char * new_text,const char * new_text_end)9834 void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)
9835 {
9836     const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
9837     if (new_text_len + BufTextLen + 1 >= BufSize)
9838         return;
9839 
9840     if (BufTextLen != pos)
9841         memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos));
9842     memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char));
9843     Buf[BufTextLen + new_text_len] = '\0';
9844 
9845     if (CursorPos >= pos)
9846         CursorPos += new_text_len;
9847     SelectionStart = SelectionEnd = CursorPos;
9848     BufDirty = true;
9849     BufTextLen += new_text_len;
9850 }
9851 
9852 // Return false to discard a character.
InputTextFilterCharacter(unsigned int * p_char,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)9853 static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
9854 {
9855     unsigned int c = *p_char;
9856 
9857     if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF)))
9858     {
9859         bool pass = false;
9860         pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline));
9861         pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput));
9862         if (!pass)
9863             return false;
9864     }
9865 
9866     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.
9867         return false;
9868 
9869     if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific))
9870     {
9871         if (flags & ImGuiInputTextFlags_CharsDecimal)
9872             if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/'))
9873                 return false;
9874 
9875         if (flags & ImGuiInputTextFlags_CharsScientific)
9876             if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E'))
9877                 return false;
9878 
9879         if (flags & ImGuiInputTextFlags_CharsHexadecimal)
9880             if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F'))
9881                 return false;
9882 
9883         if (flags & ImGuiInputTextFlags_CharsUppercase)
9884             if (c >= 'a' && c <= 'z')
9885                 *p_char = (c += (unsigned int)('A'-'a'));
9886 
9887         if (flags & ImGuiInputTextFlags_CharsNoBlank)
9888             if (ImCharIsSpace(c))
9889                 return false;
9890     }
9891 
9892     if (flags & ImGuiInputTextFlags_CallbackCharFilter)
9893     {
9894         ImGuiTextEditCallbackData callback_data;
9895         memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
9896         callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;
9897         callback_data.EventChar = (ImWchar)c;
9898         callback_data.Flags = flags;
9899         callback_data.UserData = user_data;
9900         if (callback(&callback_data) != 0)
9901             return false;
9902         *p_char = callback_data.EventChar;
9903         if (!callback_data.EventChar)
9904             return false;
9905     }
9906 
9907     return true;
9908 }
9909 
9910 // Edit a string of text
9911 // 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.
9912 // 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)9913 bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
9914 {
9915     ImGuiWindow* window = GetCurrentWindow();
9916     if (window->SkipItems)
9917         return false;
9918 
9919     IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys)
9920     IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)
9921 
9922     ImGuiContext& g = *GImGui;
9923     const ImGuiIO& io = g.IO;
9924     const ImGuiStyle& style = g.Style;
9925 
9926     const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;
9927     const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0;
9928     const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
9929     const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0;
9930 
9931     if (is_multiline) // Open group before calling GetID() because groups tracks id created during their spawn
9932         BeginGroup();
9933     const ImGuiID id = window->GetID(label);
9934     const ImVec2 label_size = CalcTextSize(label, NULL, true);
9935     ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line
9936     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
9937     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));
9938 
9939     ImGuiWindow* draw_window = window;
9940     if (is_multiline)
9941     {
9942         ItemAdd(total_bb, id, &frame_bb);
9943         if (!BeginChildFrame(id, frame_bb.GetSize()))
9944         {
9945             EndChildFrame();
9946             EndGroup();
9947             return false;
9948         }
9949         draw_window = GetCurrentWindow();
9950         size.x -= draw_window->ScrollbarSizes.x;
9951     }
9952     else
9953     {
9954         ItemSize(total_bb, style.FramePadding.y);
9955         if (!ItemAdd(total_bb, id, &frame_bb))
9956             return false;
9957     }
9958     const bool hovered = ItemHoverable(frame_bb, id);
9959     if (hovered)
9960         g.MouseCursor = ImGuiMouseCursor_TextInput;
9961 
9962     // Password pushes a temporary font with only a fallback glyph
9963     if (is_password)
9964     {
9965         const ImFontGlyph* glyph = g.Font->FindGlyph('*');
9966         ImFont* password_font = &g.InputTextPasswordFont;
9967         password_font->FontSize = g.Font->FontSize;
9968         password_font->Scale = g.Font->Scale;
9969         password_font->DisplayOffset = g.Font->DisplayOffset;
9970         password_font->Ascent = g.Font->Ascent;
9971         password_font->Descent = g.Font->Descent;
9972         password_font->ContainerAtlas = g.Font->ContainerAtlas;
9973         password_font->FallbackGlyph = glyph;
9974         password_font->FallbackAdvanceX = glyph->AdvanceX;
9975         IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty());
9976         PushFont(password_font);
9977     }
9978 
9979     // NB: we are only allowed to access 'edit_state' if we are the active widget.
9980     ImGuiTextEditState& edit_state = g.InputTextState;
9981 
9982     const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0);    // Using completion callback disable keyboard tabbing
9983     const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent);
9984     const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code;
9985 
9986     const bool user_clicked = hovered && io.MouseClicked[0];
9987     const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY");
9988     const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_NavKeyboard));
9989 
9990     bool clear_active_id = false;
9991 
9992     bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline);
9993     if (focus_requested || user_clicked || user_scrolled || user_nav_input_start)
9994     {
9995         if (g.ActiveId != id)
9996         {
9997             // Start edition
9998             // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)
9999             // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode)
10000             const int prev_len_w = edit_state.CurLenW;
10001             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.
10002             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.
10003             ImStrncpy(edit_state.InitialText.Data, buf, edit_state.InitialText.Size);
10004             const char* buf_end = NULL;
10005             edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
10006             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.
10007             edit_state.CursorAnimReset();
10008 
10009             // Preserve cursor position and undo/redo stack if we come back to same widget
10010             // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar).
10011             const bool recycle_state = (edit_state.Id == id) && (prev_len_w == edit_state.CurLenW);
10012             if (recycle_state)
10013             {
10014                 // Recycle existing cursor/selection/undo stack but clamp position
10015                 // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
10016                 edit_state.CursorClamp();
10017             }
10018             else
10019             {
10020                 edit_state.Id = id;
10021                 edit_state.ScrollX = 0.0f;
10022                 stb_textedit_initialize_state(&edit_state.StbState, !is_multiline);
10023                 if (!is_multiline && focus_requested_by_code)
10024                     select_all = true;
10025             }
10026             if (flags & ImGuiInputTextFlags_AlwaysInsertMode)
10027                 edit_state.StbState.insert_mode = true;
10028             if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl)))
10029                 select_all = true;
10030         }
10031         SetActiveID(id, window);
10032         SetFocusID(id, window);
10033         FocusWindow(window);
10034         if (!is_multiline && !(flags & ImGuiInputTextFlags_CallbackHistory))
10035             g.ActiveIdAllowNavDirFlags |= ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down));
10036     }
10037     else if (io.MouseClicked[0])
10038     {
10039         // Release focus when we click outside
10040         clear_active_id = true;
10041     }
10042 
10043     bool value_changed = false;
10044     bool enter_pressed = false;
10045 
10046     if (g.ActiveId == id)
10047     {
10048         if (!is_editable && !g.ActiveIdIsJustActivated)
10049         {
10050             // When read-only we always use the live data passed to the function
10051             edit_state.Text.resize(buf_size+1);
10052             const char* buf_end = NULL;
10053             edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
10054             edit_state.CurLenA = (int)(buf_end - buf);
10055             edit_state.CursorClamp();
10056         }
10057 
10058         edit_state.BufSizeA = buf_size;
10059 
10060         // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
10061         // Down the line we should have a cleaner library-wide concept of Selected vs Active.
10062         g.ActiveIdAllowOverlap = !io.MouseDown[0];
10063         g.WantTextInputNextFrame = 1;
10064 
10065         // Edit in progress
10066         const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX;
10067         const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f));
10068 
10069         const bool osx_double_click_selects_words = io.OptMacOSXBehaviors;      // OS X style: Double click selects by word instead of selecting whole text
10070         if (select_all || (hovered && !osx_double_click_selects_words && io.MouseDoubleClicked[0]))
10071         {
10072             edit_state.SelectAll();
10073             edit_state.SelectedAllMouseLock = true;
10074         }
10075         else if (hovered && osx_double_click_selects_words && io.MouseDoubleClicked[0])
10076         {
10077             // Select a word only, OS X style (by simulating keystrokes)
10078             edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT);
10079             edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);
10080         }
10081         else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock)
10082         {
10083             if (hovered)
10084             {
10085                 stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
10086                 edit_state.CursorAnimReset();
10087             }
10088         }
10089         else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f))
10090         {
10091             stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
10092             edit_state.CursorAnimReset();
10093             edit_state.CursorFollow = true;
10094         }
10095         if (edit_state.SelectedAllMouseLock && !io.MouseDown[0])
10096             edit_state.SelectedAllMouseLock = false;
10097 
10098         if (io.InputCharacters[0])
10099         {
10100             // Process text input (before we check for Return because using some IME will effectively send a Return?)
10101             // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters.
10102             if (!(io.KeyCtrl && !io.KeyAlt) && is_editable && !user_nav_input_start)
10103                 for (int n = 0; n < IM_ARRAYSIZE(io.InputCharacters) && io.InputCharacters[n]; n++)
10104                 {
10105                     // Insert character if they pass filtering
10106                     unsigned int c = (unsigned int)io.InputCharacters[n];
10107                     if (InputTextFilterCharacter(&c, flags, callback, user_data))
10108                         edit_state.OnKeyPressed((int)c);
10109                 }
10110 
10111             // Consume characters
10112             memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
10113         }
10114     }
10115 
10116     bool cancel_edit = false;
10117     if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id)
10118     {
10119         // Handle key-presses
10120         const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
10121         const bool is_shortcut_key_only = (io.OptMacOSXBehaviors ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl
10122         const bool is_wordmove_key_down = io.OptMacOSXBehaviors ? io.KeyAlt : io.KeyCtrl;                     // OS X style: Text editing cursor movement using Alt instead of Ctrl
10123         const bool is_startend_key_down = io.OptMacOSXBehaviors && io.KeySuper && !io.KeyCtrl && !io.KeyAlt;  // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
10124         const bool is_ctrl_key_only = io.KeyCtrl && !io.KeyShift && !io.KeyAlt && !io.KeySuper;
10125         const bool is_shift_key_only = io.KeyShift && !io.KeyCtrl && !io.KeyAlt && !io.KeySuper;
10126 
10127         const bool is_cut   = ((is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && is_editable && !is_password && (!is_multiline || edit_state.HasSelection());
10128         const bool is_copy  = ((is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only  && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || edit_state.HasSelection());
10129         const bool is_paste = ((is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && is_editable;
10130 
10131         if (IsKeyPressedMap(ImGuiKey_LeftArrow))                        { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
10132         else if (IsKeyPressedMap(ImGuiKey_RightArrow))                  { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
10133         else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline)     { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
10134         else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline)   { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }
10135         else if (IsKeyPressedMap(ImGuiKey_Home))                        { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
10136         else if (IsKeyPressedMap(ImGuiKey_End))                         { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
10137         else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable)       { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
10138         else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable)
10139         {
10140             if (!edit_state.HasSelection())
10141             {
10142                 if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT);
10143                 else if (io.OptMacOSXBehaviors && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT);
10144             }
10145             edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);
10146         }
10147         else if (IsKeyPressedMap(ImGuiKey_Enter))
10148         {
10149             bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;
10150             if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl))
10151             {
10152                 enter_pressed = clear_active_id = true;
10153             }
10154             else if (is_editable)
10155             {
10156                 unsigned int c = '\n'; // Insert new line
10157                 if (InputTextFilterCharacter(&c, flags, callback, user_data))
10158                     edit_state.OnKeyPressed((int)c);
10159             }
10160         }
10161         else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable)
10162         {
10163             unsigned int c = '\t'; // Insert TAB
10164             if (InputTextFilterCharacter(&c, flags, callback, user_data))
10165                 edit_state.OnKeyPressed((int)c);
10166         }
10167         else if (IsKeyPressedMap(ImGuiKey_Escape))                                                       { clear_active_id = cancel_edit = true; }
10168         else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable && is_undoable)      { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); }
10169         else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable && is_undoable)      { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); }
10170         else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_A))                                    { edit_state.SelectAll(); edit_state.CursorFollow = true; }
10171         else if (is_cut || is_copy)
10172         {
10173             // Cut, Copy
10174             if (io.SetClipboardTextFn)
10175             {
10176                 const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0;
10177                 const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW;
10178                 edit_state.TempTextBuffer.resize((ie-ib) * 4 + 1);
10179                 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data+ib, edit_state.Text.Data+ie);
10180                 SetClipboardText(edit_state.TempTextBuffer.Data);
10181             }
10182 
10183             if (is_cut)
10184             {
10185                 if (!edit_state.HasSelection())
10186                     edit_state.SelectAll();
10187                 edit_state.CursorFollow = true;
10188                 stb_textedit_cut(&edit_state, &edit_state.StbState);
10189             }
10190         }
10191         else if (is_paste)
10192         {
10193             // Paste
10194             if (const char* clipboard = GetClipboardText())
10195             {
10196                 // Filter pasted buffer
10197                 const int clipboard_len = (int)strlen(clipboard);
10198                 ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar));
10199                 int clipboard_filtered_len = 0;
10200                 for (const char* s = clipboard; *s; )
10201                 {
10202                     unsigned int c;
10203                     s += ImTextCharFromUtf8(&c, s, NULL);
10204                     if (c == 0)
10205                         break;
10206                     if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, user_data))
10207                         continue;
10208                     clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;
10209                 }
10210                 clipboard_filtered[clipboard_filtered_len] = 0;
10211                 if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation
10212                 {
10213                     stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len);
10214                     edit_state.CursorFollow = true;
10215                 }
10216                 ImGui::MemFree(clipboard_filtered);
10217             }
10218         }
10219     }
10220 
10221     if (g.ActiveId == id)
10222     {
10223         if (cancel_edit)
10224         {
10225             // Restore initial value
10226             if (is_editable)
10227             {
10228                 ImStrncpy(buf, edit_state.InitialText.Data, buf_size);
10229                 value_changed = true;
10230             }
10231         }
10232 
10233         // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer before clearing ActiveId, even though strictly speaking it wasn't modified on this frame.
10234         // If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail. Also this allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage.
10235         bool apply_edit_back_to_user_buffer = !cancel_edit || (enter_pressed && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0);
10236         if (apply_edit_back_to_user_buffer)
10237         {
10238             // Apply new value immediately - copy modified buffer back
10239             // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer
10240             // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect.
10241             // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks.
10242             if (is_editable)
10243             {
10244                 edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4);
10245                 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL);
10246             }
10247 
10248             // User callback
10249             if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0)
10250             {
10251                 IM_ASSERT(callback != NULL);
10252 
10253                 // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
10254                 ImGuiInputTextFlags event_flag = 0;
10255                 ImGuiKey event_key = ImGuiKey_COUNT;
10256                 if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab))
10257                 {
10258                     event_flag = ImGuiInputTextFlags_CallbackCompletion;
10259                     event_key = ImGuiKey_Tab;
10260                 }
10261                 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow))
10262                 {
10263                     event_flag = ImGuiInputTextFlags_CallbackHistory;
10264                     event_key = ImGuiKey_UpArrow;
10265                 }
10266                 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow))
10267                 {
10268                     event_flag = ImGuiInputTextFlags_CallbackHistory;
10269                     event_key = ImGuiKey_DownArrow;
10270                 }
10271                 else if (flags & ImGuiInputTextFlags_CallbackAlways)
10272                     event_flag = ImGuiInputTextFlags_CallbackAlways;
10273 
10274                 if (event_flag)
10275                 {
10276                     ImGuiTextEditCallbackData callback_data;
10277                     memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
10278                     callback_data.EventFlag = event_flag;
10279                     callback_data.Flags = flags;
10280                     callback_data.UserData = user_data;
10281                     callback_data.ReadOnly = !is_editable;
10282 
10283                     callback_data.EventKey = event_key;
10284                     callback_data.Buf = edit_state.TempTextBuffer.Data;
10285                     callback_data.BufTextLen = edit_state.CurLenA;
10286                     callback_data.BufSize = edit_state.BufSizeA;
10287                     callback_data.BufDirty = false;
10288 
10289                     // 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)
10290                     ImWchar* text = edit_state.Text.Data;
10291                     const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor);
10292                     const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start);
10293                     const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end);
10294 
10295                     // Call user code
10296                     callback(&callback_data);
10297 
10298                     // Read back what user may have modified
10299                     IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data);  // Invalid to modify those fields
10300                     IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA);
10301                     IM_ASSERT(callback_data.Flags == flags);
10302                     if (callback_data.CursorPos != utf8_cursor_pos)            edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos);
10303                     if (callback_data.SelectionStart != utf8_selection_start)  edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart);
10304                     if (callback_data.SelectionEnd != utf8_selection_end)      edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd);
10305                     if (callback_data.BufDirty)
10306                     {
10307                         IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
10308                         edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, callback_data.Buf, NULL);
10309                         edit_state.CurLenA = callback_data.BufTextLen;  // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
10310                         edit_state.CursorAnimReset();
10311                     }
10312                 }
10313             }
10314 
10315             // Copy back to user buffer
10316             if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0)
10317             {
10318                 ImStrncpy(buf, edit_state.TempTextBuffer.Data, buf_size);
10319                 value_changed = true;
10320             }
10321         }
10322     }
10323 
10324     // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value)
10325     if (clear_active_id && g.ActiveId == id)
10326         ClearActiveID();
10327 
10328     // Render
10329     // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on.
10330     const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf; buf = NULL;
10331 
10332     RenderNavHighlight(frame_bb, id);
10333     if (!is_multiline)
10334         RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
10335 
10336     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
10337     ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;
10338     ImVec2 text_size(0.f, 0.f);
10339     const bool is_currently_scrolling = (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY"));
10340     if (g.ActiveId == id || is_currently_scrolling)
10341     {
10342         edit_state.CursorAnim += io.DeltaTime;
10343 
10344         // This is going to be messy. We need to:
10345         // - Display the text (this alone can be more easily clipped)
10346         // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)
10347         // - Measure text height (for scrollbar)
10348         // 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)
10349         // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
10350         const ImWchar* text_begin = edit_state.Text.Data;
10351         ImVec2 cursor_offset, select_start_offset;
10352 
10353         {
10354             // Count lines + find lines numbers straddling 'cursor' and 'select_start' position.
10355             const ImWchar* searches_input_ptr[2];
10356             searches_input_ptr[0] = text_begin + edit_state.StbState.cursor;
10357             searches_input_ptr[1] = NULL;
10358             int searches_remaining = 1;
10359             int searches_result_line_number[2] = { -1, -999 };
10360             if (edit_state.StbState.select_start != edit_state.StbState.select_end)
10361             {
10362                 searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
10363                 searches_result_line_number[1] = -1;
10364                 searches_remaining++;
10365             }
10366 
10367             // Iterate all lines to find our line numbers
10368             // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter.
10369             searches_remaining += is_multiline ? 1 : 0;
10370             int line_count = 0;
10371             for (const ImWchar* s = text_begin; *s != 0; s++)
10372                 if (*s == '\n')
10373                 {
10374                     line_count++;
10375                     if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; }
10376                     if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; }
10377                 }
10378             line_count++;
10379             if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count;
10380             if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count;
10381 
10382             // Calculate 2d position by finding the beginning of the line and measuring distance
10383             cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;
10384             cursor_offset.y = searches_result_line_number[0] * g.FontSize;
10385             if (searches_result_line_number[1] >= 0)
10386             {
10387                 select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;
10388                 select_start_offset.y = searches_result_line_number[1] * g.FontSize;
10389             }
10390 
10391             // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224)
10392             if (is_multiline)
10393                 text_size = ImVec2(size.x, line_count * g.FontSize);
10394         }
10395 
10396         // Scroll
10397         if (edit_state.CursorFollow)
10398         {
10399             // Horizontal scroll in chunks of quarter width
10400             if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll))
10401             {
10402                 const float scroll_increment_x = size.x * 0.25f;
10403                 if (cursor_offset.x < edit_state.ScrollX)
10404                     edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x);
10405                 else if (cursor_offset.x - size.x >= edit_state.ScrollX)
10406                     edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x);
10407             }
10408             else
10409             {
10410                 edit_state.ScrollX = 0.0f;
10411             }
10412 
10413             // Vertical scroll
10414             if (is_multiline)
10415             {
10416                 float scroll_y = draw_window->Scroll.y;
10417                 if (cursor_offset.y - g.FontSize < scroll_y)
10418                     scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
10419                 else if (cursor_offset.y - size.y >= scroll_y)
10420                     scroll_y = cursor_offset.y - size.y;
10421                 draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y);   // To avoid a frame of lag
10422                 draw_window->Scroll.y = scroll_y;
10423                 render_pos.y = draw_window->DC.CursorPos.y;
10424             }
10425         }
10426         edit_state.CursorFollow = false;
10427         const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f);
10428 
10429         // Draw selection
10430         if (edit_state.StbState.select_start != edit_state.StbState.select_end)
10431         {
10432             const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
10433             const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end);
10434 
10435             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.
10436             float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
10437             ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg);
10438             ImVec2 rect_pos = render_pos + select_start_offset - render_scroll;
10439             for (const ImWchar* p = text_selected_begin; p < text_selected_end; )
10440             {
10441                 if (rect_pos.y > clip_rect.w + g.FontSize)
10442                     break;
10443                 if (rect_pos.y < clip_rect.y)
10444                 {
10445                     while (p < text_selected_end)
10446                         if (*p++ == '\n')
10447                             break;
10448                 }
10449                 else
10450                 {
10451                     ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true);
10452                     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
10453                     ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn));
10454                     rect.ClipWith(clip_rect);
10455                     if (rect.Overlaps(clip_rect))
10456                         draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
10457                 }
10458                 rect_pos.x = render_pos.x - render_scroll.x;
10459                 rect_pos.y += g.FontSize;
10460             }
10461         }
10462 
10463         draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect);
10464 
10465         // Draw blinking cursor
10466         bool cursor_is_visible = (!g.IO.OptCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || fmodf(g.InputTextState.CursorAnim, 1.20f) <= 0.80f;
10467         ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll;
10468         ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y-g.FontSize+0.5f, cursor_screen_pos.x+1.0f, cursor_screen_pos.y-1.5f);
10469         if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
10470             draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
10471 
10472         // 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.)
10473         if (is_editable)
10474             g.OsImePosRequest = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize);
10475     }
10476     else
10477     {
10478         // Render text only
10479         const char* buf_end = NULL;
10480         if (is_multiline)
10481             text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width
10482         draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect);
10483     }
10484 
10485     if (is_multiline)
10486     {
10487         Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line
10488         EndChildFrame();
10489         EndGroup();
10490     }
10491 
10492     if (is_password)
10493         PopFont();
10494 
10495     // Log as text
10496     if (g.LogEnabled && !is_password)
10497         LogRenderedText(&render_pos, buf_display, NULL);
10498 
10499     if (label_size.x > 0)
10500         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
10501 
10502     if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)
10503         return enter_pressed;
10504     else
10505         return value_changed;
10506 }
10507 
InputText(const char * label,char * buf,size_t buf_size,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)10508 bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
10509 {
10510     IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline()
10511     return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data);
10512 }
10513 
InputTextMultiline(const char * label,char * buf,size_t buf_size,const ImVec2 & size,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)10514 bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
10515 {
10516     return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);
10517 }
10518 
10519 // 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)10520 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)
10521 {
10522     ImGuiWindow* window = GetCurrentWindow();
10523     if (window->SkipItems)
10524         return false;
10525 
10526     ImGuiContext& g = *GImGui;
10527     const ImGuiStyle& style = g.Style;
10528     const ImVec2 label_size = CalcTextSize(label, NULL, true);
10529 
10530     BeginGroup();
10531     PushID(label);
10532     const ImVec2 button_sz = ImVec2(GetFrameHeight(), GetFrameHeight());
10533     if (step_ptr)
10534         PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_sz.x + style.ItemInnerSpacing.x)*2));
10535 
10536     char buf[64];
10537     DataTypeFormatString(data_type, data_ptr, scalar_format, buf, IM_ARRAYSIZE(buf));
10538 
10539     bool value_changed = false;
10540     if ((extra_flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0)
10541         extra_flags |= ImGuiInputTextFlags_CharsDecimal;
10542     extra_flags |= ImGuiInputTextFlags_AutoSelectAll;
10543     if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags)) // PushId(label) + "" gives us the expected ID from outside point of view
10544         value_changed = DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, scalar_format);
10545 
10546     // Step buttons
10547     if (step_ptr)
10548     {
10549         PopItemWidth();
10550         SameLine(0, style.ItemInnerSpacing.x);
10551         if (ButtonEx("-", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
10552         {
10553             DataTypeApplyOp(data_type, '-', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
10554             value_changed = true;
10555         }
10556         SameLine(0, style.ItemInnerSpacing.x);
10557         if (ButtonEx("+", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
10558         {
10559             DataTypeApplyOp(data_type, '+', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
10560             value_changed = true;
10561         }
10562     }
10563     PopID();
10564 
10565     if (label_size.x > 0)
10566     {
10567         SameLine(0, style.ItemInnerSpacing.x);
10568         RenderText(ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + style.FramePadding.y), label);
10569         ItemSize(label_size, style.FramePadding.y);
10570     }
10571     EndGroup();
10572 
10573     return value_changed;
10574 }
10575 
InputFloat(const char * label,float * v,float step,float step_fast,int decimal_precision,ImGuiInputTextFlags extra_flags)10576 bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags)
10577 {
10578     extra_flags |= ImGuiInputTextFlags_CharsScientific;
10579     if (decimal_precision < 0)
10580     {
10581         // 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
10582         return InputScalarEx(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), "%f", extra_flags);
10583     }
10584     else
10585     {
10586         char display_format[16];
10587         ImFormatString(display_format, IM_ARRAYSIZE(display_format), "%%.%df", decimal_precision);
10588         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);
10589     }
10590 }
10591 
InputDouble(const char * label,double * v,double step,double step_fast,const char * display_format,ImGuiInputTextFlags extra_flags)10592 bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* display_format, ImGuiInputTextFlags extra_flags)
10593 {
10594     extra_flags |= ImGuiInputTextFlags_CharsScientific;
10595     return InputScalarEx(label, ImGuiDataType_Double, (void*)v, (void*)(step>0.0 ? &step : NULL), (void*)(step_fast>0.0 ? &step_fast : NULL), display_format, extra_flags);
10596 }
10597 
InputInt(const char * label,int * v,int step,int step_fast,ImGuiInputTextFlags extra_flags)10598 bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags)
10599 {
10600     // 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.
10601     const char* scalar_format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d";
10602     return InputScalarEx(label, ImGuiDataType_Int, (void*)v, (void*)(step>0 ? &step : NULL), (void*)(step_fast>0 ? &step_fast : NULL), scalar_format, extra_flags);
10603 }
10604 
InputFloatN(const char * label,float * v,int components,int decimal_precision,ImGuiInputTextFlags extra_flags)10605 bool ImGui::InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags)
10606 {
10607     ImGuiWindow* window = GetCurrentWindow();
10608     if (window->SkipItems)
10609         return false;
10610 
10611     ImGuiContext& g = *GImGui;
10612     bool value_changed = false;
10613     BeginGroup();
10614     PushID(label);
10615     PushMultiItemsWidths(components);
10616     for (int i = 0; i < components; i++)
10617     {
10618         PushID(i);
10619         value_changed |= InputFloat("##v", &v[i], 0, 0, decimal_precision, extra_flags);
10620         SameLine(0, g.Style.ItemInnerSpacing.x);
10621         PopID();
10622         PopItemWidth();
10623     }
10624     PopID();
10625 
10626     TextUnformatted(label, FindRenderedTextEnd(label));
10627     EndGroup();
10628 
10629     return value_changed;
10630 }
10631 
InputFloat2(const char * label,float v[2],int decimal_precision,ImGuiInputTextFlags extra_flags)10632 bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags)
10633 {
10634     return InputFloatN(label, v, 2, decimal_precision, extra_flags);
10635 }
10636 
InputFloat3(const char * label,float v[3],int decimal_precision,ImGuiInputTextFlags extra_flags)10637 bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags)
10638 {
10639     return InputFloatN(label, v, 3, decimal_precision, extra_flags);
10640 }
10641 
InputFloat4(const char * label,float v[4],int decimal_precision,ImGuiInputTextFlags extra_flags)10642 bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags)
10643 {
10644     return InputFloatN(label, v, 4, decimal_precision, extra_flags);
10645 }
10646 
InputIntN(const char * label,int * v,int components,ImGuiInputTextFlags extra_flags)10647 bool ImGui::InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags)
10648 {
10649     ImGuiWindow* window = GetCurrentWindow();
10650     if (window->SkipItems)
10651         return false;
10652 
10653     ImGuiContext& g = *GImGui;
10654     bool value_changed = false;
10655     BeginGroup();
10656     PushID(label);
10657     PushMultiItemsWidths(components);
10658     for (int i = 0; i < components; i++)
10659     {
10660         PushID(i);
10661         value_changed |= InputInt("##v", &v[i], 0, 0, extra_flags);
10662         SameLine(0, g.Style.ItemInnerSpacing.x);
10663         PopID();
10664         PopItemWidth();
10665     }
10666     PopID();
10667 
10668     TextUnformatted(label, FindRenderedTextEnd(label));
10669     EndGroup();
10670 
10671     return value_changed;
10672 }
10673 
InputInt2(const char * label,int v[2],ImGuiInputTextFlags extra_flags)10674 bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags)
10675 {
10676     return InputIntN(label, v, 2, extra_flags);
10677 }
10678 
InputInt3(const char * label,int v[3],ImGuiInputTextFlags extra_flags)10679 bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags)
10680 {
10681     return InputIntN(label, v, 3, extra_flags);
10682 }
10683 
InputInt4(const char * label,int v[4],ImGuiInputTextFlags extra_flags)10684 bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags)
10685 {
10686     return InputIntN(label, v, 4, extra_flags);
10687 }
10688 
CalcMaxPopupHeightFromItemCount(int items_count)10689 static float CalcMaxPopupHeightFromItemCount(int items_count)
10690 {
10691     ImGuiContext& g = *GImGui;
10692     if (items_count <= 0)
10693         return FLT_MAX;
10694     return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2);
10695 }
10696 
BeginCombo(const char * label,const char * preview_value,ImGuiComboFlags flags)10697 bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags)
10698 {
10699     // Always consume the SetNextWindowSizeConstraint() call in our early return paths
10700     ImGuiContext& g = *GImGui;
10701     ImGuiCond backup_next_window_size_constraint = g.NextWindowData.SizeConstraintCond;
10702     g.NextWindowData.SizeConstraintCond = 0;
10703 
10704     ImGuiWindow* window = GetCurrentWindow();
10705     if (window->SkipItems)
10706         return false;
10707 
10708     IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together
10709 
10710     const ImGuiStyle& style = g.Style;
10711     const ImGuiID id = window->GetID(label);
10712 
10713     const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight();
10714     const ImVec2 label_size = CalcTextSize(label, NULL, true);
10715     const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : CalcItemWidth();
10716     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
10717     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));
10718     ItemSize(total_bb, style.FramePadding.y);
10719     if (!ItemAdd(total_bb, id, &frame_bb))
10720         return false;
10721 
10722     bool hovered, held;
10723     bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
10724     bool popup_open = IsPopupOpen(id);
10725 
10726     const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
10727     const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
10728     RenderNavHighlight(frame_bb, id);
10729     if (!(flags & ImGuiComboFlags_NoPreview))
10730         window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Max.y), frame_col, style.FrameRounding, ImDrawCornerFlags_Left);
10731     if (!(flags & ImGuiComboFlags_NoArrowButton))
10732     {
10733         window->DrawList->AddRectFilled(ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button), style.FrameRounding, (w <= arrow_size) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right);
10734         RenderArrow(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down);
10735     }
10736     RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding);
10737     if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview))
10738         RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, preview_value, NULL, NULL, ImVec2(0.0f,0.0f));
10739     if (label_size.x > 0)
10740         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
10741 
10742     if ((pressed || g.NavActivateId == id) && !popup_open)
10743     {
10744         if (window->DC.NavLayerCurrent == 0)
10745             window->NavLastIds[0] = id;
10746         OpenPopupEx(id);
10747         popup_open = true;
10748     }
10749 
10750     if (!popup_open)
10751         return false;
10752 
10753     if (backup_next_window_size_constraint)
10754     {
10755         g.NextWindowData.SizeConstraintCond = backup_next_window_size_constraint;
10756         g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w);
10757     }
10758     else
10759     {
10760         if ((flags & ImGuiComboFlags_HeightMask_) == 0)
10761             flags |= ImGuiComboFlags_HeightRegular;
10762         IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_));    // Only one
10763         int popup_max_height_in_items = -1;
10764         if (flags & ImGuiComboFlags_HeightRegular)     popup_max_height_in_items = 8;
10765         else if (flags & ImGuiComboFlags_HeightSmall)  popup_max_height_in_items = 4;
10766         else if (flags & ImGuiComboFlags_HeightLarge)  popup_max_height_in_items = 20;
10767         SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
10768     }
10769 
10770     char name[16];
10771     ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth
10772 
10773     // Peak into expected window size so we can position it
10774     if (ImGuiWindow* popup_window = FindWindowByName(name))
10775         if (popup_window->WasActive)
10776         {
10777             ImVec2 size_contents = CalcSizeContents(popup_window);
10778             ImVec2 size_expected = CalcSizeAfterConstraint(popup_window, CalcSizeAutoFit(popup_window, size_contents));
10779             if (flags & ImGuiComboFlags_PopupAlignLeft)
10780                 popup_window->AutoPosLastDirection = ImGuiDir_Left;
10781             ImRect r_outer = FindScreenRectForWindow(popup_window);
10782             ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox);
10783             SetNextWindowPos(pos);
10784         }
10785 
10786     ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
10787     if (!Begin(name, NULL, window_flags))
10788     {
10789         EndPopup();
10790         IM_ASSERT(0);   // This should never happen as we tested for IsPopupOpen() above
10791         return false;
10792     }
10793 
10794     // Horizontally align ourselves with the framed text
10795     if (style.FramePadding.x != style.WindowPadding.x)
10796         Indent(style.FramePadding.x - style.WindowPadding.x);
10797 
10798     return true;
10799 }
10800 
EndCombo()10801 void ImGui::EndCombo()
10802 {
10803     const ImGuiStyle& style = GImGui->Style;
10804     if (style.FramePadding.x != style.WindowPadding.x)
10805         Unindent(style.FramePadding.x - style.WindowPadding.x);
10806     EndPopup();
10807 }
10808 
10809 // Old API, prefer using BeginCombo() nowadays if you can.
Combo(const char * label,int * current_item,bool (* items_getter)(void *,int,const char **),void * data,int items_count,int popup_max_height_in_items)10810 bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int popup_max_height_in_items)
10811 {
10812     ImGuiContext& g = *GImGui;
10813 
10814     const char* preview_text = NULL;
10815     if (*current_item >= 0 && *current_item < items_count)
10816         items_getter(data, *current_item, &preview_text);
10817 
10818     // The old Combo() API exposed "popup_max_height_in_items", however the new more general BeginCombo() API doesn't, so we emulate it here.
10819     if (popup_max_height_in_items != -1 && !g.NextWindowData.SizeConstraintCond)
10820     {
10821         float popup_max_height = CalcMaxPopupHeightFromItemCount(popup_max_height_in_items);
10822         SetNextWindowSizeConstraints(ImVec2(0,0), ImVec2(FLT_MAX, popup_max_height));
10823     }
10824 
10825     if (!BeginCombo(label, preview_text, 0))
10826         return false;
10827 
10828     // Display items
10829     // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed)
10830     bool value_changed = false;
10831     for (int i = 0; i < items_count; i++)
10832     {
10833         PushID((void*)(intptr_t)i);
10834         const bool item_selected = (i == *current_item);
10835         const char* item_text;
10836         if (!items_getter(data, i, &item_text))
10837             item_text = "*Unknown item*";
10838         if (Selectable(item_text, item_selected))
10839         {
10840             value_changed = true;
10841             *current_item = i;
10842         }
10843         if (item_selected)
10844             SetItemDefaultFocus();
10845         PopID();
10846     }
10847 
10848     EndCombo();
10849     return value_changed;
10850 }
10851 
Items_ArrayGetter(void * data,int idx,const char ** out_text)10852 static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
10853 {
10854     const char* const* items = (const char* const*)data;
10855     if (out_text)
10856         *out_text = items[idx];
10857     return true;
10858 }
10859 
Items_SingleStringGetter(void * data,int idx,const char ** out_text)10860 static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
10861 {
10862     // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
10863     const char* items_separated_by_zeros = (const char*)data;
10864     int items_count = 0;
10865     const char* p = items_separated_by_zeros;
10866     while (*p)
10867     {
10868         if (idx == items_count)
10869             break;
10870         p += strlen(p) + 1;
10871         items_count++;
10872     }
10873     if (!*p)
10874         return false;
10875     if (out_text)
10876         *out_text = p;
10877     return true;
10878 }
10879 
10880 // Combo box helper allowing to pass an array of strings.
Combo(const char * label,int * current_item,const char * const items[],int items_count,int height_in_items)10881 bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items)
10882 {
10883     const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
10884     return value_changed;
10885 }
10886 
10887 // 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)10888 bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
10889 {
10890     int items_count = 0;
10891     const char* p = items_separated_by_zeros;       // FIXME-OPT: Avoid computing this, or at least only when combo is open
10892     while (*p)
10893     {
10894         p += strlen(p) + 1;
10895         items_count++;
10896     }
10897     bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
10898     return value_changed;
10899 }
10900 
10901 // Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image.
10902 // 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)10903 bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
10904 {
10905     ImGuiWindow* window = GetCurrentWindow();
10906     if (window->SkipItems)
10907         return false;
10908 
10909     ImGuiContext& g = *GImGui;
10910     const ImGuiStyle& style = g.Style;
10911 
10912     if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) // FIXME-OPT: Avoid if vertically clipped.
10913         PopClipRect();
10914 
10915     ImGuiID id = window->GetID(label);
10916     ImVec2 label_size = CalcTextSize(label, NULL, true);
10917     ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
10918     ImVec2 pos = window->DC.CursorPos;
10919     pos.y += window->DC.CurrentLineTextBaseOffset;
10920     ImRect bb(pos, pos + size);
10921     ItemSize(bb);
10922 
10923     // Fill horizontal space.
10924     ImVec2 window_padding = window->WindowPadding;
10925     float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x;
10926     float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x);
10927     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);
10928     ImRect bb_with_spacing(pos, pos + size_draw);
10929     if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth))
10930         bb_with_spacing.Max.x += window_padding.x;
10931 
10932     // Selectables are tightly packed together, we extend the box to cover spacing between selectable.
10933     float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f);
10934     float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f);
10935     float spacing_R = style.ItemSpacing.x - spacing_L;
10936     float spacing_D = style.ItemSpacing.y - spacing_U;
10937     bb_with_spacing.Min.x -= spacing_L;
10938     bb_with_spacing.Min.y -= spacing_U;
10939     bb_with_spacing.Max.x += spacing_R;
10940     bb_with_spacing.Max.y += spacing_D;
10941     if (!ItemAdd(bb_with_spacing, (flags & ImGuiSelectableFlags_Disabled) ? 0 : id))
10942     {
10943         if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet)
10944             PushColumnClipRect();
10945         return false;
10946     }
10947 
10948     ImGuiButtonFlags button_flags = 0;
10949     if (flags & ImGuiSelectableFlags_Menu) button_flags |= ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_NoHoldingActiveID;
10950     if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnRelease;
10951     if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled;
10952     if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
10953     bool hovered, held;
10954     bool pressed = ButtonBehavior(bb_with_spacing, id, &hovered, &held, button_flags);
10955     if (flags & ImGuiSelectableFlags_Disabled)
10956         selected = false;
10957 
10958     // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets)
10959     if (pressed || hovered)// && (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f))
10960         if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerActiveMask)
10961         {
10962             g.NavDisableHighlight = true;
10963             SetNavID(id, window->DC.NavLayerCurrent);
10964         }
10965 
10966     // Render
10967     if (hovered || selected)
10968     {
10969         const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
10970         RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, 0.0f);
10971         RenderNavHighlight(bb_with_spacing, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
10972     }
10973 
10974     if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet)
10975     {
10976         PushColumnClipRect();
10977         bb_with_spacing.Max.x -= (GetContentRegionMax().x - max_x);
10978     }
10979 
10980     if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
10981     RenderTextClipped(bb.Min, bb_with_spacing.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f));
10982     if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor();
10983 
10984     // Automatically close popups
10985     if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup))
10986         CloseCurrentPopup();
10987     return pressed;
10988 }
10989 
Selectable(const char * label,bool * p_selected,ImGuiSelectableFlags flags,const ImVec2 & size_arg)10990 bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
10991 {
10992     if (Selectable(label, *p_selected, flags, size_arg))
10993     {
10994         *p_selected = !*p_selected;
10995         return true;
10996     }
10997     return false;
10998 }
10999 
11000 // Helper to calculate the size of a listbox and display a label on the right.
11001 // 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)11002 bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
11003 {
11004     ImGuiWindow* window = GetCurrentWindow();
11005     if (window->SkipItems)
11006         return false;
11007 
11008     const ImGuiStyle& style = GetStyle();
11009     const ImGuiID id = GetID(label);
11010     const ImVec2 label_size = CalcTextSize(label, NULL, true);
11011 
11012     // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
11013     ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y);
11014     ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y));
11015     ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
11016     ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
11017     window->DC.LastItemRect = bb; // Forward storage for ListBoxFooter.. dodgy.
11018 
11019     BeginGroup();
11020     if (label_size.x > 0)
11021         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
11022 
11023     BeginChildFrame(id, frame_bb.GetSize());
11024     return true;
11025 }
11026 
ListBoxHeader(const char * label,int items_count,int height_in_items)11027 bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items)
11028 {
11029     // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
11030     // 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.
11031     // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution.
11032     if (height_in_items < 0)
11033         height_in_items = ImMin(items_count, 7);
11034     float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f);
11035 
11036     // 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().
11037     ImVec2 size;
11038     size.x = 0.0f;
11039     size.y = GetTextLineHeightWithSpacing() * height_in_items_f + GetStyle().ItemSpacing.y;
11040     return ListBoxHeader(label, size);
11041 }
11042 
ListBoxFooter()11043 void ImGui::ListBoxFooter()
11044 {
11045     ImGuiWindow* parent_window = GetCurrentWindow()->ParentWindow;
11046     const ImRect bb = parent_window->DC.LastItemRect;
11047     const ImGuiStyle& style = GetStyle();
11048 
11049     EndChildFrame();
11050 
11051     // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect)
11052     // We call SameLine() to restore DC.CurrentLine* data
11053     SameLine();
11054     parent_window->DC.CursorPos = bb.Min;
11055     ItemSize(bb, style.FramePadding.y);
11056     EndGroup();
11057 }
11058 
ListBox(const char * label,int * current_item,const char * const items[],int items_count,int height_items)11059 bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items)
11060 {
11061     const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items);
11062     return value_changed;
11063 }
11064 
ListBox(const char * label,int * current_item,bool (* items_getter)(void *,int,const char **),void * data,int items_count,int height_in_items)11065 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)
11066 {
11067     if (!ListBoxHeader(label, items_count, height_in_items))
11068         return false;
11069 
11070     // 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.
11071     bool value_changed = false;
11072     ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to.
11073     while (clipper.Step())
11074         for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
11075         {
11076             const bool item_selected = (i == *current_item);
11077             const char* item_text;
11078             if (!items_getter(data, i, &item_text))
11079                 item_text = "*Unknown item*";
11080 
11081             PushID(i);
11082             if (Selectable(item_text, item_selected))
11083             {
11084                 *current_item = i;
11085                 value_changed = true;
11086             }
11087             if (item_selected)
11088                 SetItemDefaultFocus();
11089             PopID();
11090         }
11091     ListBoxFooter();
11092     return value_changed;
11093 }
11094 
MenuItem(const char * label,const char * shortcut,bool selected,bool enabled)11095 bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)
11096 {
11097     ImGuiWindow* window = GetCurrentWindow();
11098     if (window->SkipItems)
11099         return false;
11100 
11101     ImGuiContext& g = *GImGui;
11102     ImGuiStyle& style = g.Style;
11103     ImVec2 pos = window->DC.CursorPos;
11104     ImVec2 label_size = CalcTextSize(label, NULL, true);
11105 
11106     ImGuiSelectableFlags flags = ImGuiSelectableFlags_MenuItem | (enabled ? 0 : ImGuiSelectableFlags_Disabled);
11107     bool pressed;
11108     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
11109     {
11110         // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
11111         // Note that in this situation we render neither the shortcut neither the selected tick mark
11112         float w = label_size.x;
11113         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
11114         PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
11115         pressed = Selectable(label, false, flags, ImVec2(w, 0.0f));
11116         PopStyleVar();
11117         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
11118     }
11119     else
11120     {
11121         ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f);
11122         float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame
11123         float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
11124         pressed = Selectable(label, false, flags | ImGuiSelectableFlags_DrawFillAvailWidth, ImVec2(w, 0.0f));
11125         if (shortcut_size.x > 0.0f)
11126         {
11127             PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
11128             if (shortcut)
11129               RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
11130             PopStyleColor();
11131         }
11132         if (selected)
11133             RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize  * 0.866f);
11134     }
11135     return pressed;
11136 }
11137 
MenuItem(const char * label,const char * shortcut,bool * p_selected,bool enabled)11138 bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled)
11139 {
11140     if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled))
11141     {
11142         if (p_selected)
11143             *p_selected = !*p_selected;
11144         return true;
11145     }
11146     return false;
11147 }
11148 
BeginMainMenuBar()11149 bool ImGui::BeginMainMenuBar()
11150 {
11151     ImGuiContext& g = *GImGui;
11152     SetNextWindowPos(ImVec2(0.0f, 0.0f));
11153     SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.FontBaseSize + g.Style.FramePadding.y * 2.0f));
11154     PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
11155     PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0));
11156     if (!Begin("##MainMenuBar", NULL, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_MenuBar)
11157         || !BeginMenuBar())
11158     {
11159         End();
11160         PopStyleVar(2);
11161         return false;
11162     }
11163     g.CurrentWindow->DC.MenuBarOffsetX += g.Style.DisplaySafeAreaPadding.x;
11164     return true;
11165 }
11166 
EndMainMenuBar()11167 void ImGui::EndMainMenuBar()
11168 {
11169     EndMenuBar();
11170 
11171     // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window
11172     ImGuiContext& g = *GImGui;
11173     if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0)
11174         FocusFrontMostActiveWindow(g.NavWindow);
11175 
11176     End();
11177     PopStyleVar(2);
11178 }
11179 
BeginMenuBar()11180 bool ImGui::BeginMenuBar()
11181 {
11182     ImGuiWindow* window = GetCurrentWindow();
11183     if (window->SkipItems)
11184         return false;
11185     if (!(window->Flags & ImGuiWindowFlags_MenuBar))
11186         return false;
11187 
11188     IM_ASSERT(!window->DC.MenuBarAppending);
11189     BeginGroup(); // Save position
11190     PushID("##menubar");
11191 
11192     // We don't clip with regular window clipping rectangle as it is already set to the area below. However we clip with window full rect.
11193     // We remove 1 worth of rounding to Max.x to that text in long menus don't tend to display over the lower-right rounded area, which looks particularly glitchy.
11194     ImRect bar_rect = window->MenuBarRect();
11195     ImRect clip_rect(ImFloor(bar_rect.Min.x + 0.5f), ImFloor(bar_rect.Min.y + window->WindowBorderSize + 0.5f), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - window->WindowRounding) + 0.5f), ImFloor(bar_rect.Max.y + 0.5f));
11196     clip_rect.ClipWith(window->WindowRectClipped);
11197     PushClipRect(clip_rect.Min, clip_rect.Max, false);
11198 
11199     window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffsetX, bar_rect.Min.y);// + g.Style.FramePadding.y);
11200     window->DC.LayoutType = ImGuiLayoutType_Horizontal;
11201     window->DC.NavLayerCurrent++;
11202     window->DC.NavLayerCurrentMask <<= 1;
11203     window->DC.MenuBarAppending = true;
11204     AlignTextToFramePadding();
11205     return true;
11206 }
11207 
EndMenuBar()11208 void ImGui::EndMenuBar()
11209 {
11210     ImGuiWindow* window = GetCurrentWindow();
11211     if (window->SkipItems)
11212         return;
11213     ImGuiContext& g = *GImGui;
11214 
11215     // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings.
11216     if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
11217     {
11218         ImGuiWindow* nav_earliest_child = g.NavWindow;
11219         while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu))
11220             nav_earliest_child = nav_earliest_child->ParentWindow;
11221         if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None)
11222         {
11223             // To do so we claim focus back, restore NavId and then process the movement request for yet another frame.
11224             // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost)
11225             IM_ASSERT(window->DC.NavLayerActiveMaskNext & 0x02); // Sanity check
11226             FocusWindow(window);
11227             SetNavIDWithRectRel(window->NavLastIds[1], 1, window->NavRectRel[1]);
11228             g.NavLayer = 1;
11229             g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection.
11230             g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
11231             NavMoveRequestCancel();
11232         }
11233     }
11234 
11235     IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);
11236     IM_ASSERT(window->DC.MenuBarAppending);
11237     PopClipRect();
11238     PopID();
11239     window->DC.MenuBarOffsetX = window->DC.CursorPos.x - window->MenuBarRect().Min.x;
11240     window->DC.GroupStack.back().AdvanceCursor = false;
11241     EndGroup();
11242     window->DC.LayoutType = ImGuiLayoutType_Vertical;
11243     window->DC.NavLayerCurrent--;
11244     window->DC.NavLayerCurrentMask >>= 1;
11245     window->DC.MenuBarAppending = false;
11246 }
11247 
BeginMenu(const char * label,bool enabled)11248 bool ImGui::BeginMenu(const char* label, bool enabled)
11249 {
11250     ImGuiWindow* window = GetCurrentWindow();
11251     if (window->SkipItems)
11252         return false;
11253 
11254     ImGuiContext& g = *GImGui;
11255     const ImGuiStyle& style = g.Style;
11256     const ImGuiID id = window->GetID(label);
11257 
11258     ImVec2 label_size = CalcTextSize(label, NULL, true);
11259 
11260     bool pressed;
11261     bool menu_is_open = IsPopupOpen(id);
11262     bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].OpenParentId == window->IDStack.back());
11263     ImGuiWindow* backed_nav_window = g.NavWindow;
11264     if (menuset_is_open)
11265         g.NavWindow = window;  // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent)
11266 
11267     // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestPopupWindowPos).
11268     ImVec2 popup_pos, pos = window->DC.CursorPos;
11269     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
11270     {
11271         // Menu inside an horizontal menu bar
11272         // Selectable extend their highlight by half ItemSpacing in each direction.
11273         // For ChildMenu, the popup position will be overwritten by the call to FindBestPopupWindowPos() in Begin()
11274         popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight());
11275         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
11276         PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
11277         float w = label_size.x;
11278         pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
11279         PopStyleVar();
11280         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
11281     }
11282     else
11283     {
11284         // Menu inside a menu
11285         popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
11286         float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame
11287         float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
11288         pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
11289         if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
11290         RenderArrow(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), ImGuiDir_Right);
11291         if (!enabled) PopStyleColor();
11292     }
11293 
11294     const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id);
11295     if (menuset_is_open)
11296         g.NavWindow = backed_nav_window;
11297 
11298     bool want_open = false, want_close = false;
11299     if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
11300     {
11301         // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
11302         bool moving_within_opened_triangle = false;
11303         if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar))
11304         {
11305             if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window)
11306             {
11307                 ImRect next_window_rect = next_window->Rect();
11308                 ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
11309                 ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
11310                 ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
11311                 float extra = ImClamp(fabsf(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
11312                 ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f;   // to avoid numerical issues
11313                 tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f);            // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
11314                 tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
11315                 moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
11316                 //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug
11317             }
11318         }
11319 
11320         want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle);
11321         want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed);
11322 
11323         if (g.NavActivateId == id)
11324         {
11325             want_close = menu_is_open;
11326             want_open = !menu_is_open;
11327         }
11328         if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open
11329         {
11330             want_open = true;
11331             NavMoveRequestCancel();
11332         }
11333     }
11334     else
11335     {
11336         // Menu bar
11337         if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it
11338         {
11339             want_close = true;
11340             want_open = menu_is_open = false;
11341         }
11342         else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others
11343         {
11344             want_open = true;
11345         }
11346         else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open
11347         {
11348             want_open = true;
11349             NavMoveRequestCancel();
11350         }
11351     }
11352 
11353     if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }'
11354         want_close = true;
11355     if (want_close && IsPopupOpen(id))
11356         ClosePopupToLevel(g.CurrentPopupStack.Size);
11357 
11358     if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size)
11359     {
11360         // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
11361         OpenPopup(label);
11362         return false;
11363     }
11364 
11365     menu_is_open |= want_open;
11366     if (want_open)
11367         OpenPopup(label);
11368 
11369     if (menu_is_open)
11370     {
11371         SetNextWindowPos(popup_pos, ImGuiCond_Always);
11372         ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu);
11373         menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
11374     }
11375 
11376     return menu_is_open;
11377 }
11378 
EndMenu()11379 void ImGui::EndMenu()
11380 {
11381     // Nav: When a left move request _within our child menu_ failed, close the menu.
11382     // A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs.
11383     // However it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction.
11384     ImGuiContext& g = *GImGui;
11385     ImGuiWindow* window = g.CurrentWindow;
11386     if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical)
11387     {
11388         ClosePopupToLevel(g.OpenPopupStack.Size - 1);
11389         NavMoveRequestCancel();
11390     }
11391 
11392     EndPopup();
11393 }
11394 
11395 // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
ColorTooltip(const char * text,const float * col,ImGuiColorEditFlags flags)11396 void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags)
11397 {
11398     ImGuiContext& g = *GImGui;
11399 
11400     int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]);
11401     BeginTooltipEx(0, true);
11402 
11403     const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text;
11404     if (text_end > text)
11405     {
11406         TextUnformatted(text, text_end);
11407         Separator();
11408     }
11409 
11410     ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2);
11411     ColorButton("##preview", ImVec4(col[0], col[1], col[2], col[3]), (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz);
11412     SameLine();
11413     if (flags & ImGuiColorEditFlags_NoAlpha)
11414         Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]);
11415     else
11416         Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]);
11417     EndTooltip();
11418 }
11419 
ImAlphaBlendColor(ImU32 col_a,ImU32 col_b)11420 static inline ImU32 ImAlphaBlendColor(ImU32 col_a, ImU32 col_b)
11421 {
11422     float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
11423     int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
11424     int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
11425     int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
11426     return IM_COL32(r, g, b, 0xFF);
11427 }
11428 
11429 // NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that.
11430 // I spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding alltogether.
RenderColorRectWithAlphaCheckerboard(ImVec2 p_min,ImVec2 p_max,ImU32 col,float grid_step,ImVec2 grid_off,float rounding,int rounding_corners_flags)11431 void ImGui::RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, int rounding_corners_flags)
11432 {
11433     ImGuiWindow* window = GetCurrentWindow();
11434     if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF)
11435     {
11436         ImU32 col_bg1 = GetColorU32(ImAlphaBlendColor(IM_COL32(204,204,204,255), col));
11437         ImU32 col_bg2 = GetColorU32(ImAlphaBlendColor(IM_COL32(128,128,128,255), col));
11438         window->DrawList->AddRectFilled(p_min, p_max, col_bg1, rounding, rounding_corners_flags);
11439 
11440         int yi = 0;
11441         for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++)
11442         {
11443             float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y);
11444             if (y2 <= y1)
11445                 continue;
11446             for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f)
11447             {
11448                 float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x);
11449                 if (x2 <= x1)
11450                     continue;
11451                 int rounding_corners_flags_cell = 0;
11452                 if (y1 <= p_min.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopRight; }
11453                 if (y2 >= p_max.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotRight; }
11454                 rounding_corners_flags_cell &= rounding_corners_flags;
11455                 window->DrawList->AddRectFilled(ImVec2(x1,y1), ImVec2(x2,y2), col_bg2, rounding_corners_flags_cell ? rounding : 0.0f, rounding_corners_flags_cell);
11456             }
11457         }
11458     }
11459     else
11460     {
11461         window->DrawList->AddRectFilled(p_min, p_max, col, rounding, rounding_corners_flags);
11462     }
11463 }
11464 
SetColorEditOptions(ImGuiColorEditFlags flags)11465 void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags)
11466 {
11467     ImGuiContext& g = *GImGui;
11468     if ((flags & ImGuiColorEditFlags__InputsMask) == 0)
11469         flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputsMask;
11470     if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0)
11471         flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask;
11472     if ((flags & ImGuiColorEditFlags__PickerMask) == 0)
11473         flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask;
11474     IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__InputsMask)));   // Check only 1 option is selected
11475     IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__DataTypeMask))); // Check only 1 option is selected
11476     IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask)));   // Check only 1 option is selected
11477     g.ColorEditOptions = flags;
11478 }
11479 
11480 // A little colored square. Return true when clicked.
11481 // FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip.
11482 // 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip.
ColorButton(const char * desc_id,const ImVec4 & col,ImGuiColorEditFlags flags,ImVec2 size)11483 bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size)
11484 {
11485     ImGuiWindow* window = GetCurrentWindow();
11486     if (window->SkipItems)
11487         return false;
11488 
11489     ImGuiContext& g = *GImGui;
11490     const ImGuiID id = window->GetID(desc_id);
11491     float default_size = GetFrameHeight();
11492     if (size.x == 0.0f)
11493         size.x = default_size;
11494     if (size.y == 0.0f)
11495         size.y = default_size;
11496     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
11497     ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f);
11498     if (!ItemAdd(bb, id))
11499         return false;
11500 
11501     bool hovered, held;
11502     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
11503 
11504     if (flags & ImGuiColorEditFlags_NoAlpha)
11505         flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf);
11506 
11507     ImVec4 col_without_alpha(col.x, col.y, col.z, 1.0f);
11508     float grid_step = ImMin(size.x, size.y) / 2.99f;
11509     float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f);
11510     ImRect bb_inner = bb;
11511     float off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts.
11512     bb_inner.Expand(off);
11513     if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col.w < 1.0f)
11514     {
11515         float mid_x = (float)(int)((bb_inner.Min.x + bb_inner.Max.x) * 0.5f + 0.5f);
11516         RenderColorRectWithAlphaCheckerboard(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight| ImDrawCornerFlags_BotRight);
11517         window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_without_alpha), rounding, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotLeft);
11518     }
11519     else
11520     {
11521         // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha
11522         ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col : col_without_alpha;
11523         if (col_source.w < 1.0f)
11524             RenderColorRectWithAlphaCheckerboard(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding);
11525         else
11526             window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding, ImDrawCornerFlags_All);
11527     }
11528     RenderNavHighlight(bb, id);
11529     if (g.Style.FrameBorderSize > 0.0f)
11530         RenderFrameBorder(bb.Min, bb.Max, rounding);
11531     else
11532         window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border
11533 
11534     // Drag and Drop Source
11535     if (g.ActiveId == id && BeginDragDropSource()) // NB: The ActiveId test is merely an optional micro-optimization
11536     {
11537         if (flags & ImGuiColorEditFlags_NoAlpha)
11538             SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col, sizeof(float) * 3, ImGuiCond_Once);
11539         else
11540             SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col, sizeof(float) * 4, ImGuiCond_Once);
11541         ColorButton(desc_id, col, flags);
11542         SameLine();
11543         TextUnformatted("Color");
11544         EndDragDropSource();
11545         hovered = false;
11546     }
11547 
11548     // Tooltip
11549     if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered)
11550       ColorTooltip(desc_id, (const float*)&col, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf));
11551 
11552     return pressed;
11553 }
11554 
ColorEdit3(const char * label,float col[3],ImGuiColorEditFlags flags)11555 bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags)
11556 {
11557     return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha);
11558 }
11559 
ColorEditOptionsPopup(const float * col,ImGuiColorEditFlags flags)11560 void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
11561 {
11562     bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__InputsMask);
11563     bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask);
11564     if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context"))
11565         return;
11566     ImGuiContext& g = *GImGui;
11567     ImGuiColorEditFlags opts = g.ColorEditOptions;
11568     if (allow_opt_inputs)
11569     {
11570         if (RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB;
11571         if (RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV;
11572         if (RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX;
11573     }
11574     if (allow_opt_datatype)
11575     {
11576         if (allow_opt_inputs) Separator();
11577         if (RadioButton("0..255",     (opts & ImGuiColorEditFlags_Uint8) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8;
11578         if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float;
11579     }
11580 
11581     if (allow_opt_inputs || allow_opt_datatype)
11582         Separator();
11583     if (Button("Copy as..", ImVec2(-1,0)))
11584         OpenPopup("Copy");
11585     if (BeginPopup("Copy"))
11586     {
11587         int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]);
11588         char buf[64];
11589         ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
11590         if (Selectable(buf))
11591             SetClipboardText(buf);
11592         ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca);
11593         if (Selectable(buf))
11594             SetClipboardText(buf);
11595         if (flags & ImGuiColorEditFlags_NoAlpha)
11596             ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X", cr, cg, cb);
11597         else
11598             ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X%02X", cr, cg, cb, ca);
11599         if (Selectable(buf))
11600             SetClipboardText(buf);
11601         EndPopup();
11602     }
11603 
11604     g.ColorEditOptions = opts;
11605     EndPopup();
11606 }
11607 
ColorPickerOptionsPopup(ImGuiColorEditFlags flags,const float * ref_col)11608 static void ColorPickerOptionsPopup(ImGuiColorEditFlags flags, const float* ref_col)
11609 {
11610     bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask);
11611     bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar);
11612     if ((!allow_opt_picker && !allow_opt_alpha_bar) || !ImGui::BeginPopup("context"))
11613         return;
11614     ImGuiContext& g = *GImGui;
11615     if (allow_opt_picker)
11616     {
11617         ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function
11618         ImGui::PushItemWidth(picker_size.x);
11619         for (int picker_type = 0; picker_type < 2; picker_type++)
11620         {
11621             // Draw small/thumbnail version of each picker type (over an invisible button for selection)
11622             if (picker_type > 0) ImGui::Separator();
11623             ImGui::PushID(picker_type);
11624             ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs|ImGuiColorEditFlags_NoOptions|ImGuiColorEditFlags_NoLabel|ImGuiColorEditFlags_NoSidePreview|(flags & ImGuiColorEditFlags_NoAlpha);
11625             if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar;
11626             if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel;
11627             ImVec2 backup_pos = ImGui::GetCursorScreenPos();
11628             if (ImGui::Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup
11629                 g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask);
11630             ImGui::SetCursorScreenPos(backup_pos);
11631             ImVec4 dummy_ref_col;
11632             memcpy(&dummy_ref_col, ref_col, sizeof(float) * (picker_flags & ImGuiColorEditFlags_NoAlpha ? 3 : 4));
11633             ImGui::ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags);
11634             ImGui::PopID();
11635         }
11636         ImGui::PopItemWidth();
11637     }
11638     if (allow_opt_alpha_bar)
11639     {
11640         if (allow_opt_picker) ImGui::Separator();
11641         ImGui::CheckboxFlags("Alpha Bar", (unsigned int*)&g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar);
11642     }
11643     ImGui::EndPopup();
11644 }
11645 
11646 // Edit colors components (each component in 0.0f..1.0f range).
11647 // See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
11648 // With typical options: Left-click on colored square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item.
ColorEdit4(const char * label,float col[4],ImGuiColorEditFlags flags)11649 bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags)
11650 {
11651     ImGuiWindow* window = GetCurrentWindow();
11652     if (window->SkipItems)
11653         return false;
11654 
11655     ImGuiContext& g = *GImGui;
11656     const ImGuiStyle& style = g.Style;
11657     const float square_sz = GetFrameHeight();
11658     const float w_extra = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x);
11659     const float w_items_all = CalcItemWidth() - w_extra;
11660     const char* label_display_end = FindRenderedTextEnd(label);
11661 
11662     const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0;
11663     const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0;
11664     const int components = alpha ? 4 : 3;
11665     const ImGuiColorEditFlags flags_untouched = flags;
11666 
11667     BeginGroup();
11668     PushID(label);
11669 
11670     // If we're not showing any slider there's no point in doing any HSV conversions
11671     if (flags & ImGuiColorEditFlags_NoInputs)
11672         flags = (flags & (~ImGuiColorEditFlags__InputsMask)) | ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_NoOptions;
11673 
11674     // Context menu: display and modify options (before defaults are applied)
11675     if (!(flags & ImGuiColorEditFlags_NoOptions))
11676         ColorEditOptionsPopup(col, flags);
11677 
11678     // Read stored options
11679     if (!(flags & ImGuiColorEditFlags__InputsMask))
11680         flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputsMask);
11681     if (!(flags & ImGuiColorEditFlags__DataTypeMask))
11682         flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask);
11683     if (!(flags & ImGuiColorEditFlags__PickerMask))
11684         flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask);
11685     flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask));
11686 
11687     // Convert to the formats we need
11688     float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f };
11689     if (flags & ImGuiColorEditFlags_HSV)
11690         ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
11691     int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) };
11692 
11693     bool value_changed = false;
11694     bool value_changed_as_float = false;
11695 
11696     if ((flags & (ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_HSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
11697     {
11698         // RGB/HSV 0..255 Sliders
11699         const float w_item_one  = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
11700         const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
11701 
11702         const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x);
11703         const char* ids[4] = { "##X", "##Y", "##Z", "##W" };
11704         const char* fmt_table_int[3][4] =
11705         {
11706             {   "%3.0f",   "%3.0f",   "%3.0f",   "%3.0f" }, // Short display
11707             { "R:%3.0f", "G:%3.0f", "B:%3.0f", "A:%3.0f" }, // Long display for RGBA
11708             { "H:%3.0f", "S:%3.0f", "V:%3.0f", "A:%3.0f" }  // Long display for HSVA
11709         };
11710         const char* fmt_table_float[3][4] =
11711         {
11712             {   "%0.3f",   "%0.3f",   "%0.3f",   "%0.3f" }, // Short display
11713             { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA
11714             { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" }  // Long display for HSVA
11715         };
11716         const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_HSV) ? 2 : 1;
11717 
11718         PushItemWidth(w_item_one);
11719         for (int n = 0; n < components; n++)
11720         {
11721             if (n > 0)
11722                 SameLine(0, style.ItemInnerSpacing.x);
11723             if (n + 1 == components)
11724                 PushItemWidth(w_item_last);
11725             if (flags & ImGuiColorEditFlags_Float)
11726                 value_changed = value_changed_as_float = value_changed | DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]);
11727             else
11728                 value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]);
11729             if (!(flags & ImGuiColorEditFlags_NoOptions))
11730                 OpenPopupOnItemClick("context");
11731         }
11732         PopItemWidth();
11733         PopItemWidth();
11734     }
11735     else if ((flags & ImGuiColorEditFlags_HEX) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
11736     {
11737         // RGB Hexadecimal Input
11738         char buf[64];
11739         if (alpha)
11740             ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255), ImClamp(i[3],0,255));
11741         else
11742             ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255));
11743         PushItemWidth(w_items_all);
11744         if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase))
11745         {
11746             value_changed = true;
11747             char* p = buf;
11748             while (*p == '#' || ImCharIsSpace((unsigned int)*p))
11749                 p++;
11750             i[0] = i[1] = i[2] = i[3] = 0;
11751             if (alpha)
11752                 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)
11753             else
11754                 sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
11755         }
11756         if (!(flags & ImGuiColorEditFlags_NoOptions))
11757             OpenPopupOnItemClick("context");
11758         PopItemWidth();
11759     }
11760 
11761     ImGuiWindow* picker_active_window = NULL;
11762     if (!(flags & ImGuiColorEditFlags_NoSmallPreview))
11763     {
11764         if (!(flags & ImGuiColorEditFlags_NoInputs))
11765             SameLine(0, style.ItemInnerSpacing.x);
11766 
11767         const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f);
11768         if (ColorButton("##ColorButton", col_v4, flags))
11769         {
11770             if (!(flags & ImGuiColorEditFlags_NoPicker))
11771             {
11772                 // Store current color and open a picker
11773                 g.ColorPickerRef = col_v4;
11774                 OpenPopup("picker");
11775                 SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1,style.ItemSpacing.y));
11776             }
11777         }
11778         if (!(flags & ImGuiColorEditFlags_NoOptions))
11779             OpenPopupOnItemClick("context");
11780 
11781         if (BeginPopup("picker"))
11782         {
11783             picker_active_window = g.CurrentWindow;
11784             if (label != label_display_end)
11785             {
11786                 TextUnformatted(label, label_display_end);
11787                 Separator();
11788             }
11789             ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar;
11790             ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf;
11791             PushItemWidth(square_sz * 12.0f); // Use 256 + bar sizes?
11792             value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x);
11793             PopItemWidth();
11794             EndPopup();
11795         }
11796     }
11797 
11798     if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel))
11799     {
11800         SameLine(0, style.ItemInnerSpacing.x);
11801         TextUnformatted(label, label_display_end);
11802     }
11803 
11804     // Convert back
11805     if (picker_active_window == NULL)
11806     {
11807         if (!value_changed_as_float)
11808             for (int n = 0; n < 4; n++)
11809                 f[n] = i[n] / 255.0f;
11810         if (flags & ImGuiColorEditFlags_HSV)
11811             ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
11812         if (value_changed)
11813         {
11814             col[0] = f[0];
11815             col[1] = f[1];
11816             col[2] = f[2];
11817             if (alpha)
11818                 col[3] = f[3];
11819         }
11820     }
11821 
11822     PopID();
11823     EndGroup();
11824 
11825     // Drag and Drop Target
11826     if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) && BeginDragDropTarget()) // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test.
11827     {
11828         if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
11829         {
11830             memcpy((float*)col, payload->Data, sizeof(float) * 3);
11831             value_changed = true;
11832         }
11833         if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
11834         {
11835             memcpy((float*)col, payload->Data, sizeof(float) * components);
11836             value_changed = true;
11837         }
11838         EndDragDropTarget();
11839     }
11840 
11841     // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4().
11842     if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window)
11843         window->DC.LastItemId = g.ActiveId;
11844 
11845     return value_changed;
11846 }
11847 
ColorPicker3(const char * label,float col[3],ImGuiColorEditFlags flags)11848 bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags)
11849 {
11850     float col4[4] = { col[0], col[1], col[2], 1.0f };
11851     if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha))
11852         return false;
11853     col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2];
11854     return true;
11855 }
11856 
11857 // 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side.
RenderArrow(ImDrawList * draw_list,ImVec2 pos,ImVec2 half_sz,ImGuiDir direction,ImU32 col)11858 static void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col)
11859 {
11860     switch (direction)
11861     {
11862     case ImGuiDir_Left:  draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), pos, col); return;
11863     case ImGuiDir_Right: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), pos, col); return;
11864     case ImGuiDir_Up:    draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), pos, col); return;
11865     case ImGuiDir_Down:  draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), pos, col); return;
11866     case ImGuiDir_None: case ImGuiDir_COUNT: break; // Fix warnings
11867     }
11868 }
11869 
RenderArrowsForVerticalBar(ImDrawList * draw_list,ImVec2 pos,ImVec2 half_sz,float bar_w)11870 static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w)
11871 {
11872     RenderArrow(draw_list, ImVec2(pos.x + half_sz.x + 1,         pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32_BLACK);
11873     RenderArrow(draw_list, ImVec2(pos.x + half_sz.x,             pos.y), half_sz,                              ImGuiDir_Right, IM_COL32_WHITE);
11874     RenderArrow(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left,  IM_COL32_BLACK);
11875     RenderArrow(draw_list, ImVec2(pos.x + bar_w - half_sz.x,     pos.y), half_sz,                              ImGuiDir_Left,  IM_COL32_WHITE);
11876 }
11877 
11878 // ColorPicker
11879 // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
11880 // FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..)
ColorPicker4(const char * label,float col[4],ImGuiColorEditFlags flags,const float * ref_col)11881 bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col)
11882 {
11883     ImGuiContext& g = *GImGui;
11884     ImGuiWindow* window = GetCurrentWindow();
11885     ImDrawList* draw_list = window->DrawList;
11886 
11887     ImGuiStyle& style = g.Style;
11888     ImGuiIO& io = g.IO;
11889 
11890     PushID(label);
11891     BeginGroup();
11892 
11893     if (!(flags & ImGuiColorEditFlags_NoSidePreview))
11894         flags |= ImGuiColorEditFlags_NoSmallPreview;
11895 
11896     // Context menu: display and store options.
11897     if (!(flags & ImGuiColorEditFlags_NoOptions))
11898         ColorPickerOptionsPopup(flags, col);
11899 
11900     // Read stored options
11901     if (!(flags & ImGuiColorEditFlags__PickerMask))
11902         flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask;
11903     IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check that only 1 is selected
11904     if (!(flags & ImGuiColorEditFlags_NoOptions))
11905         flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar);
11906 
11907     // Setup
11908     int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4;
11909     bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha);
11910     ImVec2 picker_pos = window->DC.CursorPos;
11911     float square_sz = GetFrameHeight();
11912     float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars
11913     float sv_picker_size = ImMax(bars_width * 1, CalcItemWidth() - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box
11914     float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x;
11915     float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x;
11916     float bars_triangles_half_sz = (float)(int)(bars_width * 0.20f);
11917 
11918     float backup_initial_col[4];
11919     memcpy(backup_initial_col, col, components * sizeof(float));
11920 
11921     float wheel_thickness = sv_picker_size * 0.08f;
11922     float wheel_r_outer = sv_picker_size * 0.50f;
11923     float wheel_r_inner = wheel_r_outer - wheel_thickness;
11924     ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size*0.5f);
11925 
11926     // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic.
11927     float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f);
11928     ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point.
11929     ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point.
11930     ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point.
11931 
11932     float H,S,V;
11933     ColorConvertRGBtoHSV(col[0], col[1], col[2], H, S, V);
11934 
11935     bool value_changed = false, value_changed_h = false, value_changed_sv = false;
11936 
11937     PushItemFlag(ImGuiItemFlags_NoNav, true);
11938     if (flags & ImGuiColorEditFlags_PickerHueWheel)
11939     {
11940         // Hue wheel + SV triangle logic
11941         InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size));
11942         if (IsItemActive())
11943         {
11944             ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center;
11945             ImVec2 current_off = g.IO.MousePos - wheel_center;
11946             float initial_dist2 = ImLengthSqr(initial_off);
11947             if (initial_dist2 >= (wheel_r_inner-1)*(wheel_r_inner-1) && initial_dist2 <= (wheel_r_outer+1)*(wheel_r_outer+1))
11948             {
11949                 // Interactive with Hue wheel
11950                 H = atan2f(current_off.y, current_off.x) / IM_PI*0.5f;
11951                 if (H < 0.0f)
11952                     H += 1.0f;
11953                 value_changed = value_changed_h = true;
11954             }
11955             float cos_hue_angle = cosf(-H * 2.0f * IM_PI);
11956             float sin_hue_angle = sinf(-H * 2.0f * IM_PI);
11957             if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle)))
11958             {
11959                 // Interacting with SV triangle
11960                 ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle);
11961                 if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated))
11962                     current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated);
11963                 float uu, vv, ww;
11964                 ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww);
11965                 V = ImClamp(1.0f - vv, 0.0001f, 1.0f);
11966                 S = ImClamp(uu / V, 0.0001f, 1.0f);
11967                 value_changed = value_changed_sv = true;
11968             }
11969         }
11970         if (!(flags & ImGuiColorEditFlags_NoOptions))
11971             OpenPopupOnItemClick("context");
11972     }
11973     else if (flags & ImGuiColorEditFlags_PickerHueBar)
11974     {
11975         // SV rectangle logic
11976         InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size));
11977         if (IsItemActive())
11978         {
11979             S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size-1));
11980             V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
11981             value_changed = value_changed_sv = true;
11982         }
11983         if (!(flags & ImGuiColorEditFlags_NoOptions))
11984             OpenPopupOnItemClick("context");
11985 
11986         // Hue bar logic
11987         SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y));
11988         InvisibleButton("hue", ImVec2(bars_width, sv_picker_size));
11989         if (IsItemActive())
11990         {
11991             H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
11992             value_changed = value_changed_h = true;
11993         }
11994     }
11995 
11996     // Alpha bar logic
11997     if (alpha_bar)
11998     {
11999         SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y));
12000         InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size));
12001         if (IsItemActive())
12002         {
12003             col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
12004             value_changed = true;
12005         }
12006     }
12007     PopItemFlag(); // ImGuiItemFlags_NoNav
12008 
12009     if (!(flags & ImGuiColorEditFlags_NoSidePreview))
12010     {
12011         SameLine(0, style.ItemInnerSpacing.x);
12012         BeginGroup();
12013     }
12014 
12015     if (!(flags & ImGuiColorEditFlags_NoLabel))
12016     {
12017         const char* label_display_end = FindRenderedTextEnd(label);
12018         if (label != label_display_end)
12019         {
12020             if ((flags & ImGuiColorEditFlags_NoSidePreview))
12021                 SameLine(0, style.ItemInnerSpacing.x);
12022             TextUnformatted(label, label_display_end);
12023         }
12024     }
12025 
12026     if (!(flags & ImGuiColorEditFlags_NoSidePreview))
12027     {
12028         PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true);
12029         ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
12030         if ((flags & ImGuiColorEditFlags_NoLabel))
12031             Text("Current");
12032         ColorButton("##current", col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2));
12033         if (ref_col != NULL)
12034         {
12035             Text("Original");
12036             ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]);
12037             if (ColorButton("##original", ref_col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2)))
12038             {
12039                 memcpy(col, ref_col, components * sizeof(float));
12040                 value_changed = true;
12041             }
12042         }
12043         PopItemFlag();
12044         EndGroup();
12045     }
12046 
12047     // Convert back color to RGB
12048     if (value_changed_h || value_changed_sv)
12049         ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10*1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]);
12050 
12051     // R,G,B and H,S,V slider color editor
12052     if ((flags & ImGuiColorEditFlags_NoInputs) == 0)
12053     {
12054         PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x);
12055         ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf;
12056         ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker;
12057         if (flags & ImGuiColorEditFlags_RGB || (flags & ImGuiColorEditFlags__InputsMask) == 0)
12058             value_changed |= ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_RGB);
12059         if (flags & ImGuiColorEditFlags_HSV || (flags & ImGuiColorEditFlags__InputsMask) == 0)
12060             value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_HSV);
12061         if (flags & ImGuiColorEditFlags_HEX || (flags & ImGuiColorEditFlags__InputsMask) == 0)
12062             value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_HEX);
12063         PopItemWidth();
12064     }
12065 
12066     // Try to cancel hue wrap (after ColorEdit), if any
12067     if (value_changed)
12068     {
12069         float new_H, new_S, new_V;
12070         ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V);
12071         if (new_H <= 0 && H > 0)
12072         {
12073             if (new_V <= 0 && V != new_V)
12074                 ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]);
12075             else if (new_S <= 0)
12076                 ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]);
12077         }
12078     }
12079 
12080     ImVec4 hue_color_f(1, 1, 1, 1); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z);
12081     ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f);
12082     ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 1.0f));
12083 
12084     const ImU32 hue_colors[6+1] = { IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255), IM_COL32(0,255,255,255), IM_COL32(0,0,255,255), IM_COL32(255,0,255,255), IM_COL32(255,0,0,255) };
12085     ImVec2 sv_cursor_pos;
12086 
12087     if (flags & ImGuiColorEditFlags_PickerHueWheel)
12088     {
12089         // Render Hue Wheel
12090         const float aeps = 1.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out).
12091         const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12);
12092         for (int n = 0; n < 6; n++)
12093         {
12094             const float a0 = (n)     /6.0f * 2.0f * IM_PI - aeps;
12095             const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps;
12096             const int vert_start_idx = draw_list->VtxBuffer.Size;
12097             draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc);
12098             draw_list->PathStroke(IM_COL32_WHITE, false, wheel_thickness);
12099             const int vert_end_idx = draw_list->VtxBuffer.Size;
12100 
12101             // Paint colors over existing vertices
12102             ImVec2 gradient_p0(wheel_center.x + cosf(a0) * wheel_r_inner, wheel_center.y + sinf(a0) * wheel_r_inner);
12103             ImVec2 gradient_p1(wheel_center.x + cosf(a1) * wheel_r_inner, wheel_center.y + sinf(a1) * wheel_r_inner);
12104             ShadeVertsLinearColorGradientKeepAlpha(draw_list->VtxBuffer.Data + vert_start_idx, draw_list->VtxBuffer.Data + vert_end_idx, gradient_p0, gradient_p1, hue_colors[n], hue_colors[n+1]);
12105         }
12106 
12107         // Render Cursor + preview on Hue Wheel
12108         float cos_hue_angle = cosf(H * 2.0f * IM_PI);
12109         float sin_hue_angle = sinf(H * 2.0f * IM_PI);
12110         ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f);
12111         float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f;
12112         int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32);
12113         draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments);
12114         draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad+1, IM_COL32(128,128,128,255), hue_cursor_segments);
12115         draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, IM_COL32_WHITE, hue_cursor_segments);
12116 
12117         // Render SV triangle (rotated according to hue)
12118         ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle);
12119         ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle);
12120         ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle);
12121         ImVec2 uv_white = GetFontTexUvWhitePixel();
12122         draw_list->PrimReserve(6, 6);
12123         draw_list->PrimVtx(tra, uv_white, hue_color32);
12124         draw_list->PrimVtx(trb, uv_white, hue_color32);
12125         draw_list->PrimVtx(trc, uv_white, IM_COL32_WHITE);
12126         draw_list->PrimVtx(tra, uv_white, IM_COL32_BLACK_TRANS);
12127         draw_list->PrimVtx(trb, uv_white, IM_COL32_BLACK);
12128         draw_list->PrimVtx(trc, uv_white, IM_COL32_BLACK_TRANS);
12129         draw_list->AddTriangle(tra, trb, trc, IM_COL32(128,128,128,255), 1.5f);
12130         sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V));
12131     }
12132     else if (flags & ImGuiColorEditFlags_PickerHueBar)
12133     {
12134         // Render SV Square
12135         draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_WHITE, hue_color32, hue_color32, IM_COL32_WHITE);
12136         draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_BLACK_TRANS, IM_COL32_BLACK_TRANS, IM_COL32_BLACK, IM_COL32_BLACK);
12137         RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), 0.0f);
12138         sv_cursor_pos.x = ImClamp((float)(int)(picker_pos.x + ImSaturate(S)     * sv_picker_size + 0.5f), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much
12139         sv_cursor_pos.y = ImClamp((float)(int)(picker_pos.y + ImSaturate(1 - V) * sv_picker_size + 0.5f), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2);
12140 
12141         // Render Hue Bar
12142         for (int i = 0; i < 6; ++i)
12143             draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), hue_colors[i], hue_colors[i], hue_colors[i + 1], hue_colors[i + 1]);
12144         float bar0_line_y = (float)(int)(picker_pos.y + H * sv_picker_size + 0.5f);
12145         RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f);
12146         RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f);
12147     }
12148 
12149     // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range)
12150     float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f;
12151     draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, col32_no_alpha, 12);
12152     draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad+1, IM_COL32(128,128,128,255), 12);
12153     draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, IM_COL32_WHITE, 12);
12154 
12155     // Render alpha bar
12156     if (alpha_bar)
12157     {
12158         float alpha = ImSaturate(col[3]);
12159         ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size);
12160         RenderColorRectWithAlphaCheckerboard(bar1_bb.Min, bar1_bb.Max, IM_COL32(0,0,0,0), bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f));
12161         draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, col32_no_alpha, col32_no_alpha, col32_no_alpha & ~IM_COL32_A_MASK, col32_no_alpha & ~IM_COL32_A_MASK);
12162         float bar1_line_y = (float)(int)(picker_pos.y + (1.0f - alpha) * sv_picker_size + 0.5f);
12163         RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f);
12164         RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f);
12165     }
12166 
12167     EndGroup();
12168     PopID();
12169 
12170     return value_changed && memcmp(backup_initial_col, col, components * sizeof(float));
12171 }
12172 
12173 // Horizontal separating line.
Separator()12174 void ImGui::Separator()
12175 {
12176     ImGuiWindow* window = GetCurrentWindow();
12177     if (window->SkipItems)
12178         return;
12179     ImGuiContext& g = *GImGui;
12180 
12181     ImGuiSeparatorFlags flags = 0;
12182     if ((flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)) == 0)
12183         flags |= (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal;
12184     IM_ASSERT(ImIsPowerOfTwo((int)(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical))));   // Check that only 1 option is selected
12185     if (flags & ImGuiSeparatorFlags_Vertical)
12186     {
12187         VerticalSeparator();
12188         return;
12189     }
12190 
12191     // Horizontal Separator
12192     if (window->DC.ColumnsSet)
12193         PopClipRect();
12194 
12195     float x1 = window->Pos.x;
12196     float x2 = window->Pos.x + window->Size.x;
12197     if (!window->DC.GroupStack.empty())
12198         x1 += window->DC.IndentX;
12199 
12200     const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f));
12201     ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit, we don't provide height to not alter layout.
12202     if (!ItemAdd(bb, 0))
12203     {
12204         if (window->DC.ColumnsSet)
12205             PushColumnClipRect();
12206         return;
12207     }
12208 
12209     window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Separator));
12210 
12211     if (g.LogEnabled)
12212             LogRenderedText(NULL, IM_NEWLINE "--------------------------------");
12213 
12214     if (window->DC.ColumnsSet)
12215     {
12216         PushColumnClipRect();
12217         window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y;
12218     }
12219 }
12220 
VerticalSeparator()12221 void ImGui::VerticalSeparator()
12222 {
12223     ImGuiWindow* window = GetCurrentWindow();
12224     if (window->SkipItems)
12225         return;
12226     ImGuiContext& g = *GImGui;
12227 
12228     float y1 = window->DC.CursorPos.y;
12229     float y2 = window->DC.CursorPos.y + window->DC.CurrentLineHeight;
12230     const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + 1.0f, y2));
12231     ItemSize(ImVec2(bb.GetWidth(), 0.0f));
12232     if (!ItemAdd(bb, 0))
12233         return;
12234 
12235     window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x, bb.Max.y), GetColorU32(ImGuiCol_Separator));
12236     if (g.LogEnabled)
12237         LogText(" |");
12238 }
12239 
SplitterBehavior(ImGuiID id,const ImRect & bb,ImGuiAxis axis,float * size1,float * size2,float min_size1,float min_size2,float hover_extend)12240 bool ImGui::SplitterBehavior(ImGuiID id, const ImRect& bb, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend)
12241 {
12242     ImGuiContext& g = *GImGui;
12243     ImGuiWindow* window = g.CurrentWindow;
12244 
12245     const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
12246     window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus;
12247     bool item_add = ItemAdd(bb, id);
12248     window->DC.ItemFlags = item_flags_backup;
12249     if (!item_add)
12250         return false;
12251 
12252     bool hovered, held;
12253     ImRect bb_interact = bb;
12254     bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f));
12255     ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap);
12256     if (g.ActiveId != id)
12257         SetItemAllowOverlap();
12258 
12259     if (held || (g.HoveredId == id && g.HoveredIdPreviousFrame == id))
12260         SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW);
12261 
12262     ImRect bb_render = bb;
12263     if (held)
12264     {
12265         ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min;
12266         float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x;
12267 
12268         // Minimum pane size
12269         if (mouse_delta < min_size1 - *size1)
12270             mouse_delta = min_size1 - *size1;
12271         if (mouse_delta > *size2 - min_size2)
12272             mouse_delta = *size2 - min_size2;
12273 
12274         // Apply resize
12275         *size1 += mouse_delta;
12276         *size2 -= mouse_delta;
12277         bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta));
12278     }
12279 
12280     // Render
12281     const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
12282     window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, g.Style.FrameRounding);
12283 
12284     return held;
12285 }
12286 
Spacing()12287 void ImGui::Spacing()
12288 {
12289     ImGuiWindow* window = GetCurrentWindow();
12290     if (window->SkipItems)
12291         return;
12292     ItemSize(ImVec2(0,0));
12293 }
12294 
Dummy(const ImVec2 & size)12295 void ImGui::Dummy(const ImVec2& size)
12296 {
12297     ImGuiWindow* window = GetCurrentWindow();
12298     if (window->SkipItems)
12299         return;
12300 
12301     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
12302     ItemSize(bb);
12303     ItemAdd(bb, 0);
12304 }
12305 
IsRectVisible(const ImVec2 & size)12306 bool ImGui::IsRectVisible(const ImVec2& size)
12307 {
12308     ImGuiWindow* window = GetCurrentWindowRead();
12309     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
12310 }
12311 
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)12312 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
12313 {
12314     ImGuiWindow* window = GetCurrentWindowRead();
12315     return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
12316 }
12317 
12318 // Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
BeginGroup()12319 void ImGui::BeginGroup()
12320 {
12321     ImGuiWindow* window = GetCurrentWindow();
12322 
12323     window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
12324     ImGuiGroupData& group_data = window->DC.GroupStack.back();
12325     group_data.BackupCursorPos = window->DC.CursorPos;
12326     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
12327     group_data.BackupIndentX = window->DC.IndentX;
12328     group_data.BackupGroupOffsetX = window->DC.GroupOffsetX;
12329     group_data.BackupCurrentLineHeight = window->DC.CurrentLineHeight;
12330     group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
12331     group_data.BackupLogLinePosY = window->DC.LogLinePosY;
12332     group_data.BackupActiveIdIsAlive = GImGui->ActiveIdIsAlive;
12333     group_data.AdvanceCursor = true;
12334 
12335     window->DC.GroupOffsetX = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffsetX;
12336     window->DC.IndentX = window->DC.GroupOffsetX;
12337     window->DC.CursorMaxPos = window->DC.CursorPos;
12338     window->DC.CurrentLineHeight = 0.0f;
12339     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
12340 }
12341 
EndGroup()12342 void ImGui::EndGroup()
12343 {
12344     ImGuiContext& g = *GImGui;
12345     ImGuiWindow* window = GetCurrentWindow();
12346 
12347     IM_ASSERT(!window->DC.GroupStack.empty());    // Mismatched BeginGroup()/EndGroup() calls
12348 
12349     ImGuiGroupData& group_data = window->DC.GroupStack.back();
12350 
12351     ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
12352     group_bb.Max = ImMax(group_bb.Min, group_bb.Max);
12353 
12354     window->DC.CursorPos = group_data.BackupCursorPos;
12355     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
12356     window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight;
12357     window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
12358     window->DC.IndentX = group_data.BackupIndentX;
12359     window->DC.GroupOffsetX = group_data.BackupGroupOffsetX;
12360     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
12361 
12362     if (group_data.AdvanceCursor)
12363     {
12364         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.
12365         ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset);
12366         ItemAdd(group_bb, 0);
12367     }
12368 
12369     // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive() will be functional on the entire group.
12370     // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but if you search for LastItemId you'll notice it is only used in that context.
12371     const bool active_id_within_group = (!group_data.BackupActiveIdIsAlive && g.ActiveIdIsAlive && g.ActiveId && g.ActiveIdWindow->RootWindow == window->RootWindow);
12372     if (active_id_within_group)
12373         window->DC.LastItemId = g.ActiveId;
12374     window->DC.LastItemRect = group_bb;
12375 
12376     window->DC.GroupStack.pop_back();
12377 
12378     //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
12379 }
12380 
12381 // Gets back to previous line and continue with horizontal layout
12382 //      pos_x == 0      : follow right after previous item
12383 //      pos_x != 0      : align to specified x position (relative to window/group left)
12384 //      spacing_w < 0   : use default spacing if pos_x == 0, no spacing if pos_x != 0
12385 //      spacing_w >= 0  : enforce spacing amount
SameLine(float pos_x,float spacing_w)12386 void ImGui::SameLine(float pos_x, float spacing_w)
12387 {
12388     ImGuiWindow* window = GetCurrentWindow();
12389     if (window->SkipItems)
12390         return;
12391 
12392     ImGuiContext& g = *GImGui;
12393     if (pos_x != 0.0f)
12394     {
12395         if (spacing_w < 0.0f) spacing_w = 0.0f;
12396         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffsetX + window->DC.ColumnsOffsetX;
12397         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
12398     }
12399     else
12400     {
12401         if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
12402         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
12403         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
12404     }
12405     window->DC.CurrentLineHeight = window->DC.PrevLineHeight;
12406     window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
12407 }
12408 
NewLine()12409 void ImGui::NewLine()
12410 {
12411     ImGuiWindow* window = GetCurrentWindow();
12412     if (window->SkipItems)
12413         return;
12414 
12415     ImGuiContext& g = *GImGui;
12416     const ImGuiLayoutType backup_layout_type = window->DC.LayoutType;
12417     window->DC.LayoutType = ImGuiLayoutType_Vertical;
12418     if (window->DC.CurrentLineHeight > 0.0f)     // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height.
12419         ItemSize(ImVec2(0,0));
12420     else
12421         ItemSize(ImVec2(0.0f, g.FontSize));
12422     window->DC.LayoutType = backup_layout_type;
12423 }
12424 
NextColumn()12425 void ImGui::NextColumn()
12426 {
12427     ImGuiWindow* window = GetCurrentWindow();
12428     if (window->SkipItems || window->DC.ColumnsSet == NULL)
12429         return;
12430 
12431     ImGuiContext& g = *GImGui;
12432     PopItemWidth();
12433     PopClipRect();
12434 
12435     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
12436     columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
12437     if (++columns->Current < columns->Count)
12438     {
12439         // Columns 1+ cancel out IndentX
12440         window->DC.ColumnsOffsetX = GetColumnOffset(columns->Current) - window->DC.IndentX + g.Style.ItemSpacing.x;
12441         window->DrawList->ChannelsSetCurrent(columns->Current);
12442     }
12443     else
12444     {
12445         window->DC.ColumnsOffsetX = 0.0f;
12446         window->DrawList->ChannelsSetCurrent(0);
12447         columns->Current = 0;
12448         columns->LineMinY = columns->LineMaxY;
12449     }
12450     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
12451     window->DC.CursorPos.y = columns->LineMinY;
12452     window->DC.CurrentLineHeight = 0.0f;
12453     window->DC.CurrentLineTextBaseOffset = 0.0f;
12454 
12455     PushColumnClipRect();
12456     PushItemWidth(GetColumnWidth() * 0.65f);  // FIXME: Move on columns setup
12457 }
12458 
GetColumnIndex()12459 int ImGui::GetColumnIndex()
12460 {
12461     ImGuiWindow* window = GetCurrentWindowRead();
12462     return window->DC.ColumnsSet ? window->DC.ColumnsSet->Current : 0;
12463 }
12464 
GetColumnsCount()12465 int ImGui::GetColumnsCount()
12466 {
12467     ImGuiWindow* window = GetCurrentWindowRead();
12468     return window->DC.ColumnsSet ? window->DC.ColumnsSet->Count : 1;
12469 }
12470 
OffsetNormToPixels(const ImGuiColumnsSet * columns,float offset_norm)12471 static float OffsetNormToPixels(const ImGuiColumnsSet* columns, float offset_norm)
12472 {
12473     return offset_norm * (columns->MaxX - columns->MinX);
12474 }
12475 
PixelsToOffsetNorm(const ImGuiColumnsSet * columns,float offset)12476 static float PixelsToOffsetNorm(const ImGuiColumnsSet* columns, float offset)
12477 {
12478     return offset / (columns->MaxX - columns->MinX);
12479 }
12480 
GetColumnsRectHalfWidth()12481 static inline float GetColumnsRectHalfWidth() { return 4.0f; }
12482 
GetDraggedColumnOffset(ImGuiColumnsSet * columns,int column_index)12483 static float GetDraggedColumnOffset(ImGuiColumnsSet* columns, int column_index)
12484 {
12485     // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
12486     // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
12487     ImGuiContext& g = *GImGui;
12488     ImGuiWindow* window = g.CurrentWindow;
12489     IM_ASSERT(column_index > 0); // We are not supposed to drag column 0.
12490     IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));
12491 
12492     float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + GetColumnsRectHalfWidth() - window->Pos.x;
12493     x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing);
12494     if ((columns->Flags & ImGuiColumnsFlags_NoPreserveWidths))
12495         x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);
12496 
12497     return x;
12498 }
12499 
GetColumnOffset(int column_index)12500 float ImGui::GetColumnOffset(int column_index)
12501 {
12502     ImGuiWindow* window = GetCurrentWindowRead();
12503     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
12504     IM_ASSERT(columns != NULL);
12505 
12506     if (column_index < 0)
12507         column_index = columns->Current;
12508     IM_ASSERT(column_index < columns->Columns.Size);
12509 
12510     const float t = columns->Columns[column_index].OffsetNorm;
12511     const float x_offset = ImLerp(columns->MinX, columns->MaxX, t);
12512     return x_offset;
12513 }
12514 
GetColumnWidthEx(ImGuiColumnsSet * columns,int column_index,bool before_resize=false)12515 static float GetColumnWidthEx(ImGuiColumnsSet* columns, int column_index, bool before_resize = false)
12516 {
12517     if (column_index < 0)
12518         column_index = columns->Current;
12519 
12520     float offset_norm;
12521     if (before_resize)
12522         offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize;
12523     else
12524         offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm;
12525     return OffsetNormToPixels(columns, offset_norm);
12526 }
12527 
GetColumnWidth(int column_index)12528 float ImGui::GetColumnWidth(int column_index)
12529 {
12530     ImGuiWindow* window = GetCurrentWindowRead();
12531     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
12532     IM_ASSERT(columns != NULL);
12533 
12534     if (column_index < 0)
12535         column_index = columns->Current;
12536     return OffsetNormToPixels(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm);
12537 }
12538 
SetColumnOffset(int column_index,float offset)12539 void ImGui::SetColumnOffset(int column_index, float offset)
12540 {
12541     ImGuiContext& g = *GImGui;
12542     ImGuiWindow* window = g.CurrentWindow;
12543     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
12544     IM_ASSERT(columns != NULL);
12545 
12546     if (column_index < 0)
12547         column_index = columns->Current;
12548     IM_ASSERT(column_index < columns->Columns.Size);
12549 
12550     const bool preserve_width = !(columns->Flags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < columns->Count-1);
12551     const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f;
12552 
12553     if (!(columns->Flags & ImGuiColumnsFlags_NoForceWithinWindow))
12554         offset = ImMin(offset, columns->MaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index));
12555     columns->Columns[column_index].OffsetNorm = PixelsToOffsetNorm(columns, offset - columns->MinX);
12556 
12557     if (preserve_width)
12558         SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width));
12559 }
12560 
SetColumnWidth(int column_index,float width)12561 void ImGui::SetColumnWidth(int column_index, float width)
12562 {
12563     ImGuiWindow* window = GetCurrentWindowRead();
12564     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
12565     IM_ASSERT(columns != NULL);
12566 
12567     if (column_index < 0)
12568         column_index = columns->Current;
12569     SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width);
12570 }
12571 
PushColumnClipRect(int column_index)12572 void ImGui::PushColumnClipRect(int column_index)
12573 {
12574     ImGuiWindow* window = GetCurrentWindowRead();
12575     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
12576     if (column_index < 0)
12577         column_index = columns->Current;
12578 
12579     PushClipRect(columns->Columns[column_index].ClipRect.Min, columns->Columns[column_index].ClipRect.Max, false);
12580 }
12581 
FindOrAddColumnsSet(ImGuiWindow * window,ImGuiID id)12582 static ImGuiColumnsSet* FindOrAddColumnsSet(ImGuiWindow* window, ImGuiID id)
12583 {
12584     for (int n = 0; n < window->ColumnsStorage.Size; n++)
12585         if (window->ColumnsStorage[n].ID == id)
12586             return &window->ColumnsStorage[n];
12587 
12588     window->ColumnsStorage.push_back(ImGuiColumnsSet());
12589     ImGuiColumnsSet* columns = &window->ColumnsStorage.back();
12590     columns->ID = id;
12591     return columns;
12592 }
12593 
BeginColumns(const char * str_id,int columns_count,ImGuiColumnsFlags flags)12594 void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlags flags)
12595 {
12596     ImGuiContext& g = *GImGui;
12597     ImGuiWindow* window = GetCurrentWindow();
12598 
12599     IM_ASSERT(columns_count > 1);
12600     IM_ASSERT(window->DC.ColumnsSet == NULL); // Nested columns are currently not supported
12601 
12602     // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
12603     // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
12604     PushID(0x11223347 + (str_id ? 0 : columns_count));
12605     ImGuiID id = window->GetID(str_id ? str_id : "columns");
12606     PopID();
12607 
12608     // Acquire storage for the columns set
12609     ImGuiColumnsSet* columns = FindOrAddColumnsSet(window, id);
12610     IM_ASSERT(columns->ID == id);
12611     columns->Current = 0;
12612     columns->Count = columns_count;
12613     columns->Flags = flags;
12614     window->DC.ColumnsSet = columns;
12615 
12616     // Set state for first column
12617     const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? (window->SizeContentsExplicit.x) : (window->InnerClipRect.Max.x - window->Pos.x);
12618     columns->MinX = window->DC.IndentX - g.Style.ItemSpacing.x; // Lock our horizontal range
12619     columns->MaxX = ImMax(content_region_width - window->Scroll.x, columns->MinX + 1.0f);
12620     columns->StartPosY = window->DC.CursorPos.y;
12621     columns->StartMaxPosX = window->DC.CursorMaxPos.x;
12622     columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y;
12623     window->DC.ColumnsOffsetX = 0.0f;
12624     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
12625 
12626     // Clear data if columns count changed
12627     if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1)
12628         columns->Columns.resize(0);
12629 
12630     // Initialize defaults
12631     columns->IsFirstFrame = (columns->Columns.Size == 0);
12632     if (columns->Columns.Size == 0)
12633     {
12634         columns->Columns.reserve(columns_count + 1);
12635         for (int n = 0; n < columns_count + 1; n++)
12636         {
12637             ImGuiColumnData column;
12638             column.OffsetNorm = n / (float)columns_count;
12639             columns->Columns.push_back(column);
12640         }
12641     }
12642 
12643     for (int n = 0; n < columns_count; n++)
12644     {
12645         // Compute clipping rectangle
12646         ImGuiColumnData* column = &columns->Columns[n];
12647         float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n) - 1.0f);
12648         float clip_x2 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n + 1) - 1.0f);
12649         column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);
12650         column->ClipRect.ClipWith(window->ClipRect);
12651     }
12652 
12653     window->DrawList->ChannelsSplit(columns->Count);
12654     PushColumnClipRect();
12655     PushItemWidth(GetColumnWidth() * 0.65f);
12656 }
12657 
EndColumns()12658 void ImGui::EndColumns()
12659 {
12660     ImGuiContext& g = *GImGui;
12661     ImGuiWindow* window = GetCurrentWindow();
12662     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
12663     IM_ASSERT(columns != NULL);
12664 
12665     PopItemWidth();
12666     PopClipRect();
12667     window->DrawList->ChannelsMerge();
12668 
12669     columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
12670     window->DC.CursorPos.y = columns->LineMaxY;
12671     if (!(columns->Flags & ImGuiColumnsFlags_GrowParentContentsSize))
12672         window->DC.CursorMaxPos.x = ImMax(columns->StartMaxPosX, columns->MaxX);  // Restore cursor max pos, as columns don't grow parent
12673 
12674     // Draw columns borders and handle resize
12675     bool is_being_resized = false;
12676     if (!(columns->Flags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems)
12677     {
12678         const float y1 = columns->StartPosY;
12679         const float y2 = window->DC.CursorPos.y;
12680         int dragging_column = -1;
12681         for (int n = 1; n < columns->Count; n++)
12682         {
12683             float x = window->Pos.x + GetColumnOffset(n);
12684             const ImGuiID column_id = columns->ID + ImGuiID(n);
12685             const float column_hw = GetColumnsRectHalfWidth(); // Half-width for interaction
12686             const ImRect column_rect(ImVec2(x - column_hw, y1), ImVec2(x + column_hw, y2));
12687             KeepAliveID(column_id);
12688             if (IsClippedEx(column_rect, column_id, false))
12689                 continue;
12690 
12691             bool hovered = false, held = false;
12692             if (!(columns->Flags & ImGuiColumnsFlags_NoResize))
12693             {
12694                 ButtonBehavior(column_rect, column_id, &hovered, &held);
12695                 if (hovered || held)
12696                     g.MouseCursor = ImGuiMouseCursor_ResizeEW;
12697                 if (held && !(columns->Columns[n].Flags & ImGuiColumnsFlags_NoResize))
12698                     dragging_column = n;
12699             }
12700 
12701             // Draw column (we clip the Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.)
12702             const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
12703             const float xi = (float)(int)x;
12704             window->DrawList->AddLine(ImVec2(xi, ImMax(y1 + 1.0f, window->ClipRect.Min.y)), ImVec2(xi, ImMin(y2, window->ClipRect.Max.y)), col);
12705         }
12706 
12707         // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
12708         if (dragging_column != -1)
12709         {
12710             if (!columns->IsBeingResized)
12711                 for (int n = 0; n < columns->Count + 1; n++)
12712                     columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm;
12713             columns->IsBeingResized = is_being_resized = true;
12714             float x = GetDraggedColumnOffset(columns, dragging_column);
12715             SetColumnOffset(dragging_column, x);
12716         }
12717     }
12718     columns->IsBeingResized = is_being_resized;
12719 
12720     window->DC.ColumnsSet = NULL;
12721     window->DC.ColumnsOffsetX = 0.0f;
12722     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
12723 }
12724 
12725 // [2018-03: This is currently the only public API, while we are working on making BeginColumns/EndColumns user-facing]
Columns(int columns_count,const char * id,bool border)12726 void ImGui::Columns(int columns_count, const char* id, bool border)
12727 {
12728     ImGuiWindow* window = GetCurrentWindow();
12729     IM_ASSERT(columns_count >= 1);
12730 
12731     ImGuiColumnsFlags flags = (border ? 0 : ImGuiColumnsFlags_NoBorder);
12732     //flags |= ImGuiColumnsFlags_NoPreserveWidths; // NB: Legacy behavior
12733     if (window->DC.ColumnsSet != NULL && window->DC.ColumnsSet->Count == columns_count && window->DC.ColumnsSet->Flags == flags)
12734         return;
12735 
12736     if (window->DC.ColumnsSet != NULL)
12737         EndColumns();
12738 
12739     if (columns_count != 1)
12740         BeginColumns(id, columns_count, flags);
12741 }
12742 
Indent(float indent_w)12743 void ImGui::Indent(float indent_w)
12744 {
12745     ImGuiContext& g = *GImGui;
12746     ImGuiWindow* window = GetCurrentWindow();
12747     window->DC.IndentX += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
12748     window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
12749 }
12750 
Unindent(float indent_w)12751 void ImGui::Unindent(float indent_w)
12752 {
12753     ImGuiContext& g = *GImGui;
12754     ImGuiWindow* window = GetCurrentWindow();
12755     window->DC.IndentX -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
12756     window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
12757 }
12758 
TreePush(const char * str_id)12759 void ImGui::TreePush(const char* str_id)
12760 {
12761     ImGuiWindow* window = GetCurrentWindow();
12762     Indent();
12763     window->DC.TreeDepth++;
12764     PushID(str_id ? str_id : "#TreePush");
12765 }
12766 
TreePush(const void * ptr_id)12767 void ImGui::TreePush(const void* ptr_id)
12768 {
12769     ImGuiWindow* window = GetCurrentWindow();
12770     Indent();
12771     window->DC.TreeDepth++;
12772     PushID(ptr_id ? ptr_id : (const void*)"#TreePush");
12773 }
12774 
TreePushRawID(ImGuiID id)12775 void ImGui::TreePushRawID(ImGuiID id)
12776 {
12777     ImGuiWindow* window = GetCurrentWindow();
12778     Indent();
12779     window->DC.TreeDepth++;
12780     window->IDStack.push_back(id);
12781 }
12782 
TreePop()12783 void ImGui::TreePop()
12784 {
12785     ImGuiContext& g = *GImGui;
12786     ImGuiWindow* window = g.CurrentWindow;
12787     Unindent();
12788 
12789     window->DC.TreeDepth--;
12790     if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())
12791         if (g.NavIdIsAlive && (window->DC.TreeDepthMayJumpToParentOnPop & (1 << window->DC.TreeDepth)))
12792         {
12793             SetNavID(window->IDStack.back(), g.NavLayer);
12794             NavMoveRequestCancel();
12795         }
12796     window->DC.TreeDepthMayJumpToParentOnPop &= (1 << window->DC.TreeDepth) - 1;
12797 
12798     PopID();
12799 }
12800 
Value(const char * prefix,bool b)12801 void ImGui::Value(const char* prefix, bool b)
12802 {
12803     Text("%s: %s", prefix, (b ? "true" : "false"));
12804 }
12805 
Value(const char * prefix,int v)12806 void ImGui::Value(const char* prefix, int v)
12807 {
12808     Text("%s: %d", prefix, v);
12809 }
12810 
Value(const char * prefix,unsigned int v)12811 void ImGui::Value(const char* prefix, unsigned int v)
12812 {
12813     Text("%s: %d", prefix, v);
12814 }
12815 
Value(const char * prefix,float v,const char * float_format)12816 void ImGui::Value(const char* prefix, float v, const char* float_format)
12817 {
12818     if (float_format)
12819     {
12820         char fmt[64];
12821         ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format);
12822         Text(fmt, prefix, v);
12823     }
12824     else
12825     {
12826         Text("%s: %.3f", prefix, v);
12827     }
12828 }
12829 
12830 //-----------------------------------------------------------------------------
12831 // DRAG AND DROP
12832 //-----------------------------------------------------------------------------
12833 
ClearDragDrop()12834 void ImGui::ClearDragDrop()
12835 {
12836     ImGuiContext& g = *GImGui;
12837     g.DragDropActive = false;
12838     g.DragDropPayload.Clear();
12839     g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
12840     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
12841     g.DragDropAcceptFrameCount = -1;
12842 }
12843 
12844 // Call when current ID is active.
12845 // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
BeginDragDropSource(ImGuiDragDropFlags flags)12846 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
12847 {
12848     ImGuiContext& g = *GImGui;
12849     ImGuiWindow* window = g.CurrentWindow;
12850 
12851     bool source_drag_active = false;
12852     ImGuiID source_id = 0;
12853     ImGuiID source_parent_id = 0;
12854     int mouse_button = 0;
12855     if (!(flags & ImGuiDragDropFlags_SourceExtern))
12856     {
12857         source_id = window->DC.LastItemId;
12858         if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
12859             return false;
12860         if (g.IO.MouseDown[mouse_button] == false)
12861             return false;
12862 
12863         if (source_id == 0)
12864         {
12865             // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
12866             // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
12867             if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
12868             {
12869                 IM_ASSERT(0);
12870                 return false;
12871             }
12872 
12873             // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
12874             // We build a throwaway ID based on current ID stack + relative AABB of items in window.
12875             // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
12876             // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
12877             bool is_hovered = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) != 0;
12878             if (!is_hovered && (g.ActiveId == 0 || g.ActiveIdWindow != window))
12879                 return false;
12880             source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
12881             if (is_hovered)
12882                 SetHoveredID(source_id);
12883             if (is_hovered && g.IO.MouseClicked[mouse_button])
12884             {
12885                 SetActiveID(source_id, window);
12886                 FocusWindow(window);
12887             }
12888             if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
12889                 g.ActiveIdAllowOverlap = is_hovered;
12890         }
12891         if (g.ActiveId != source_id)
12892             return false;
12893         source_parent_id = window->IDStack.back();
12894         source_drag_active = IsMouseDragging(mouse_button);
12895     }
12896     else
12897     {
12898         window = NULL;
12899         source_id = ImHash("#SourceExtern", 0);
12900         source_drag_active = true;
12901     }
12902 
12903     if (source_drag_active)
12904     {
12905         if (!g.DragDropActive)
12906         {
12907             IM_ASSERT(source_id != 0);
12908             ClearDragDrop();
12909             ImGuiPayload& payload = g.DragDropPayload;
12910             payload.SourceId = source_id;
12911             payload.SourceParentId = source_parent_id;
12912             g.DragDropActive = true;
12913             g.DragDropSourceFlags = flags;
12914             g.DragDropMouseButton = mouse_button;
12915         }
12916 
12917         if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
12918         {
12919             // FIXME-DRAG
12920             //SetNextWindowPos(g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding);
12921             //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This is better but e.g ColorButton with checkboard has issue with transparent colors :(
12922             SetNextWindowPos(g.IO.MousePos);
12923             PushStyleColor(ImGuiCol_PopupBg, GetStyleColorVec4(ImGuiCol_PopupBg) * ImVec4(1.0f, 1.0f, 1.0f, 0.6f));
12924             BeginTooltip();
12925         }
12926 
12927         if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
12928             window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
12929 
12930         return true;
12931     }
12932     return false;
12933 }
12934 
EndDragDropSource()12935 void ImGui::EndDragDropSource()
12936 {
12937     ImGuiContext& g = *GImGui;
12938     IM_ASSERT(g.DragDropActive);
12939 
12940     if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
12941     {
12942         EndTooltip();
12943         PopStyleColor();
12944         //PopStyleVar();
12945     }
12946 
12947     // Discard the drag if have not called SetDragDropPayload()
12948     if (g.DragDropPayload.DataFrameCount == -1)
12949         ClearDragDrop();
12950 }
12951 
12952 // Use 'cond' to choose to submit payload on drag start or every frame
SetDragDropPayload(const char * type,const void * data,size_t data_size,ImGuiCond cond)12953 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
12954 {
12955     ImGuiContext& g = *GImGui;
12956     ImGuiPayload& payload = g.DragDropPayload;
12957     if (cond == 0)
12958         cond = ImGuiCond_Always;
12959 
12960     IM_ASSERT(type != NULL);
12961     IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 12 characters long");
12962     IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
12963     IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
12964     IM_ASSERT(payload.SourceId != 0);                               // Not called between BeginDragDropSource() and EndDragDropSource()
12965 
12966     if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
12967     {
12968         // Copy payload
12969         ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
12970         g.DragDropPayloadBufHeap.resize(0);
12971         if (data_size > sizeof(g.DragDropPayloadBufLocal))
12972         {
12973             // Store in heap
12974             g.DragDropPayloadBufHeap.resize((int)data_size);
12975             payload.Data = g.DragDropPayloadBufHeap.Data;
12976             memcpy((void*)(intptr_t)payload.Data, data, data_size);
12977         }
12978         else if (data_size > 0)
12979         {
12980             // Store locally
12981             memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
12982             payload.Data = g.DragDropPayloadBufLocal;
12983             memcpy((void*)(intptr_t)payload.Data, data, data_size);
12984         }
12985         else
12986         {
12987             payload.Data = NULL;
12988         }
12989         payload.DataSize = (int)data_size;
12990     }
12991     payload.DataFrameCount = g.FrameCount;
12992 
12993     return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
12994 }
12995 
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)12996 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
12997 {
12998     ImGuiContext& g = *GImGui;
12999     if (!g.DragDropActive)
13000         return false;
13001 
13002     ImGuiWindow* window = g.CurrentWindow;
13003     if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
13004         return false;
13005     IM_ASSERT(id != 0);
13006     if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
13007         return false;
13008 
13009     g.DragDropTargetRect = bb;
13010     g.DragDropTargetId = id;
13011     return true;
13012 }
13013 
13014 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
13015 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
13016 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
13017 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()13018 bool ImGui::BeginDragDropTarget()
13019 {
13020     ImGuiContext& g = *GImGui;
13021     if (!g.DragDropActive)
13022         return false;
13023 
13024     ImGuiWindow* window = g.CurrentWindow;
13025     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
13026         return false;
13027     if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
13028         return false;
13029 
13030     const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
13031     ImGuiID id = window->DC.LastItemId;
13032     if (id == 0)
13033         id = window->GetIDFromRectangle(display_rect);
13034     if (g.DragDropPayload.SourceId == id)
13035         return false;
13036 
13037     g.DragDropTargetRect = display_rect;
13038     g.DragDropTargetId = id;
13039     return true;
13040 }
13041 
IsDragDropPayloadBeingAccepted()13042 bool ImGui::IsDragDropPayloadBeingAccepted()
13043 {
13044     ImGuiContext& g = *GImGui;
13045     return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
13046 }
13047 
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)13048 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
13049 {
13050     ImGuiContext& g = *GImGui;
13051     ImGuiWindow* window = g.CurrentWindow;
13052     ImGuiPayload& payload = g.DragDropPayload;
13053     IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
13054     IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
13055     if (type != NULL && !payload.IsDataType(type))
13056         return NULL;
13057 
13058     // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
13059     // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
13060     const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
13061     ImRect r = g.DragDropTargetRect;
13062     float r_surface = r.GetWidth() * r.GetHeight();
13063     if (r_surface < g.DragDropAcceptIdCurrRectSurface)
13064     {
13065         g.DragDropAcceptIdCurr = g.DragDropTargetId;
13066         g.DragDropAcceptIdCurrRectSurface = r_surface;
13067     }
13068 
13069     // Render default drop visuals
13070     payload.Preview = was_accepted_previously;
13071     flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
13072     if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
13073     {
13074         // FIXME-DRAG: Settle on a proper default visuals for drop target.
13075         r.Expand(3.5f);
13076         bool push_clip_rect = !window->ClipRect.Contains(r);
13077         if (push_clip_rect) window->DrawList->PushClipRectFullScreen();
13078         window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
13079         if (push_clip_rect) window->DrawList->PopClipRect();
13080     }
13081 
13082     g.DragDropAcceptFrameCount = g.FrameCount;
13083     payload.Delivery = was_accepted_previously && !IsMouseDown(g.DragDropMouseButton); // For extern drag sources affecting os window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased()
13084     if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
13085         return NULL;
13086 
13087     return &payload;
13088 }
13089 
13090 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()13091 void ImGui::EndDragDropTarget()
13092 {
13093     ImGuiContext& g = *GImGui; (void)g;
13094     IM_ASSERT(g.DragDropActive);
13095 }
13096 
13097 //-----------------------------------------------------------------------------
13098 // PLATFORM DEPENDENT HELPERS
13099 //-----------------------------------------------------------------------------
13100 
13101 #if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
13102 #undef WIN32_LEAN_AND_MEAN
13103 #define WIN32_LEAN_AND_MEAN
13104 #ifndef __MINGW32__
13105 #include <Windows.h>
13106 #else
13107 #include <windows.h>
13108 #endif
13109 #endif
13110 
13111 // Win32 API clipboard implementation
13112 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
13113 
13114 #ifdef _MSC_VER
13115 #pragma comment(lib, "user32")
13116 #endif
13117 
GetClipboardTextFn_DefaultImpl(void *)13118 static const char* GetClipboardTextFn_DefaultImpl(void*)
13119 {
13120     static ImVector<char> buf_local;
13121     buf_local.clear();
13122     if (!OpenClipboard(NULL))
13123         return NULL;
13124     HANDLE wbuf_handle = GetClipboardData(CF_UNICODETEXT);
13125     if (wbuf_handle == NULL)
13126     {
13127         CloseClipboard();
13128         return NULL;
13129     }
13130     if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle))
13131     {
13132         int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
13133         buf_local.resize(buf_len);
13134         ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
13135     }
13136     GlobalUnlock(wbuf_handle);
13137     CloseClipboard();
13138     return buf_local.Data;
13139 }
13140 
SetClipboardTextFn_DefaultImpl(void *,const char * text)13141 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
13142 {
13143     if (!OpenClipboard(NULL))
13144         return;
13145     const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
13146     HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
13147     if (wbuf_handle == NULL)
13148     {
13149         CloseClipboard();
13150         return;
13151     }
13152     ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle);
13153     ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
13154     GlobalUnlock(wbuf_handle);
13155     EmptyClipboard();
13156     SetClipboardData(CF_UNICODETEXT, wbuf_handle);
13157     CloseClipboard();
13158 }
13159 
13160 #else
13161 
13162 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
GetClipboardTextFn_DefaultImpl(void *)13163 static const char* GetClipboardTextFn_DefaultImpl(void*)
13164 {
13165     ImGuiContext& g = *GImGui;
13166     return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
13167 }
13168 
13169 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
SetClipboardTextFn_DefaultImpl(void *,const char * text)13170 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
13171 {
13172     ImGuiContext& g = *GImGui;
13173     g.PrivateClipboard.clear();
13174     const char* text_end = text + strlen(text);
13175     g.PrivateClipboard.resize((int)(text_end - text) + 1);
13176     memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
13177     g.PrivateClipboard[(int)(text_end - text)] = 0;
13178 }
13179 
13180 #endif
13181 
13182 // Win32 API IME support (for Asian languages, etc.)
13183 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
13184 
13185 #include <imm.h>
13186 #ifdef _MSC_VER
13187 #pragma comment(lib, "imm32")
13188 #endif
13189 
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)13190 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
13191 {
13192     // Notify OS Input Method Editor of text input position
13193     if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle)
13194         if (HIMC himc = ImmGetContext(hwnd))
13195         {
13196             COMPOSITIONFORM cf;
13197             cf.ptCurrentPos.x = x;
13198             cf.ptCurrentPos.y = y;
13199             cf.dwStyle = CFS_FORCE_POSITION;
13200             ImmSetCompositionWindow(himc, &cf);
13201         }
13202 }
13203 
13204 #else
13205 
ImeSetInputScreenPosFn_DefaultImpl(int,int)13206 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
13207 
13208 #endif
13209 
13210 //-----------------------------------------------------------------------------
13211 // HELP
13212 //-----------------------------------------------------------------------------
13213 
ShowMetricsWindow(bool * p_open)13214 void ImGui::ShowMetricsWindow(bool* p_open)
13215 {
13216     if (ImGui::Begin("ImGui Metrics", p_open))
13217     {
13218         ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
13219         ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
13220         ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3);
13221         ImGui::Text("%d allocations", (int)GImAllocatorActiveAllocationsCount);
13222         static bool show_clip_rects = true;
13223         ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_clip_rects);
13224         ImGui::Separator();
13225 
13226         struct Funcs
13227         {
13228             static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
13229             {
13230                 bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size);
13231                 if (draw_list == ImGui::GetWindowDrawList())
13232                 {
13233                     ImGui::SameLine();
13234                     ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
13235                     if (node_open) ImGui::TreePop();
13236                     return;
13237                 }
13238 
13239                 ImDrawList* overlay_draw_list = ImGui::GetOverlayDrawList(); // Render additional visuals into the top-most draw list
13240                 if (window && ImGui::IsItemHovered())
13241                     overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
13242                 if (!node_open)
13243                     return;
13244 
13245                 int elem_offset = 0;
13246                 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
13247                 {
13248                     if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
13249                         continue;
13250                     if (pcmd->UserCallback)
13251                     {
13252                         ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
13253                         continue;
13254                     }
13255                     ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
13256                     bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %4d %s vtx, tex 0x%p, clip_rect (%4.0f,%4.0f)-(%4.0f,%4.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);
13257                     if (show_clip_rects && ImGui::IsItemHovered())
13258                     {
13259                         ImRect clip_rect = pcmd->ClipRect;
13260                         ImRect vtxs_rect;
13261                         for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
13262                             vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
13263                         clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255));
13264                         vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255));
13265                     }
13266                     if (!pcmd_node_open)
13267                         continue;
13268 
13269                     // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
13270                     ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
13271                     while (clipper.Step())
13272                         for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
13273                         {
13274                             char buf[300];
13275                             char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
13276                             ImVec2 triangles_pos[3];
13277                             for (int n = 0; n < 3; n++, vtx_i++)
13278                             {
13279                                 ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i];
13280                                 triangles_pos[n] = v.pos;
13281                                 buf_p += ImFormatString(buf_p, (int)(buf_end - buf_p), "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", (n == 0) ? "vtx" : "   ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
13282                             }
13283                             ImGui::Selectable(buf, false);
13284                             if (ImGui::IsItemHovered())
13285                             {
13286                                 ImDrawListFlags backup_flags = overlay_draw_list->Flags;
13287                                 overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles.
13288                                 overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f);
13289                                 overlay_draw_list->Flags = backup_flags;
13290                             }
13291                         }
13292                     ImGui::TreePop();
13293                 }
13294                 ImGui::TreePop();
13295             }
13296 
13297             static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
13298             {
13299                 if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
13300                     return;
13301                 for (int i = 0; i < windows.Size; i++)
13302                     Funcs::NodeWindow(windows[i], "Window");
13303                 ImGui::TreePop();
13304             }
13305 
13306             static void NodeWindow(ImGuiWindow* window, const char* label)
13307             {
13308                 if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
13309                     return;
13310                 ImGuiWindowFlags flags = window->Flags;
13311                 NodeDrawList(window, window->DrawList, "DrawList");
13312                 ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y);
13313                 ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s..)", flags,
13314                     (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip)   ? "Tooltip "   : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
13315                     (flags & ImGuiWindowFlags_Modal)       ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "");
13316                 ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetScrollMaxX(window), window->Scroll.y, GetScrollMaxY(window));
13317                 ImGui::BulletText("Active: %d, WriteAccessed: %d", window->Active, window->WriteAccessed);
13318                 ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
13319                 ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
13320                 if (window->NavRectRel[0].IsInverted())
13321                     ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y);
13322                 else
13323                     ImGui::BulletText("NavRectRel[0]: <None>");
13324                 if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
13325                 if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
13326                 if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
13327                 {
13328                     for (int n = 0; n < window->ColumnsStorage.Size; n++)
13329                     {
13330                         const ImGuiColumnsSet* columns = &window->ColumnsStorage[n];
13331                         if (ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
13332                         {
13333                             ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->MaxX - columns->MinX, columns->MinX, columns->MaxX);
13334                             for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
13335                                 ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, OffsetNormToPixels(columns, columns->Columns[column_n].OffsetNorm));
13336                             ImGui::TreePop();
13337                         }
13338                     }
13339                     ImGui::TreePop();
13340                 }
13341                 ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
13342                 ImGui::TreePop();
13343             }
13344         };
13345 
13346         // Access private state, we are going to display the draw lists from last frame
13347         ImGuiContext& g = *GImGui;
13348         Funcs::NodeWindows(g.Windows, "Windows");
13349         if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
13350         {
13351             for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
13352                 Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
13353             ImGui::TreePop();
13354         }
13355         if (ImGui::TreeNode("Popups", "Open Popups Stack (%d)", g.OpenPopupStack.Size))
13356         {
13357             for (int i = 0; i < g.OpenPopupStack.Size; i++)
13358             {
13359                 ImGuiWindow* window = g.OpenPopupStack[i].Window;
13360                 ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : "");
13361             }
13362             ImGui::TreePop();
13363         }
13364         if (ImGui::TreeNode("Internal state"))
13365         {
13366             const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
13367             ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
13368             ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
13369             ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec)", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
13370             ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), ActiveIdSource: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, input_source_names[g.ActiveIdSource]);
13371             ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
13372             ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
13373             ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
13374             ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
13375             ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
13376             ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
13377             ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
13378             ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
13379             ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
13380             ImGui::TreePop();
13381         }
13382     }
13383     ImGui::End();
13384 }
13385 
13386 //-----------------------------------------------------------------------------
13387 
13388 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
13389 // 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.
13390 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
13391 #include "imgui_user.inl"
13392 #endif
13393 
13394 //-----------------------------------------------------------------------------
13395