1 // dear imgui, v1.60 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 /*
14 
15  Index
16  - MISSION STATEMENT
17  - END-USER GUIDE
18  - PROGRAMMER GUIDE (read me!)
19    - Read first
20    - How to update to a newer version of Dear ImGui
21    - Getting started with integrating Dear ImGui in your code/engine
22    - Using gamepad/keyboard navigation [BETA]
23  - API BREAKING CHANGES (read me when you update!)
24  - ISSUES & TODO LIST
25  - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
26    - How can I help?
27    - How can I display an image? What is ImTextureID, how does it works?
28    - How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on labels and the ID stack.
29    - How can I tell when Dear ImGui wants my mouse/keyboard inputs VS when I can pass them to my application?
30    - How can I load a different font than the default?
31    - How can I easily use icons in my application?
32    - How can I load multiple fonts?
33    - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
34    - How can I preserve my Dear ImGui context across reloading a DLL? (loss of the global/static variables)
35    - How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
36    - I integrated Dear ImGui in my engine and the text or lines are blurry..
37    - I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
38  - ISSUES & TODO-LIST
39  - CODE
40 
41 
42  MISSION STATEMENT
43  =================
44 
45  - Easy to use to create code-driven and data-driven tools
46  - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools
47  - Easy to hack and improve
48  - Minimize screen real-estate usage
49  - Minimize setup and maintenance
50  - Minimize state storage on user side
51  - Portable, minimize dependencies, run on target (consoles, phones, etc.)
52  - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window, opening a tree node
53    for the first time, etc. but a typical frame won't allocate anything)
54 
55  Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
56  - Doesn't look fancy, doesn't animate
57  - Limited layout features, intricate layouts are typically crafted in code
58 
59 
60  END-USER GUIDE
61  ==============
62 
63  - Double-click on title bar to collapse window.
64  - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
65  - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
66  - Click and drag on any empty space to move window.
67  - TAB/SHIFT+TAB to cycle through keyboard editable fields.
68  - CTRL+Click on a slider or drag box to input value as text.
69  - Use mouse wheel to scroll.
70  - Text editor:
71    - Hold SHIFT or use mouse to select text.
72    - CTRL+Left/Right to word jump.
73    - CTRL+Shift+Left/Right to select words.
74    - CTRL+A our Double-Click to select all.
75    - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
76    - CTRL+Z,CTRL+Y to undo/redo.
77    - ESCAPE to revert text to its original value.
78    - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
79    - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
80  - Gamepad navigation: see suggested mappings in imgui.h ImGuiNavInput_
81 
82 
83  PROGRAMMER GUIDE
84  ================
85 
86  READ FIRST
87 
88  - Read the FAQ below this section!
89  - Your code creates the UI, if your code doesn't run the UI is gone! == very dynamic UI, no construction/destructions steps, less data retention
90    on your side, no state duplication, less sync, less bugs.
91  - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
92  - You can learn about immediate-mode gui principles at http://www.johno.se/book/imgui.html or watch http://mollyrocket.com/861
93 
94  HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
95 
96  - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
97  - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
98    If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed from the public API.
99    If you have a problem with a missing function/symbols, search for its name in the code, there will likely be a comment about it.
100    Please report any issue to the GitHub page!
101  - Try to keep your copy of dear imgui reasonably up to date.
102 
103  GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
104 
105  - Add the Dear ImGui source files to your projects, using your preferred build system.
106    It is recommended you build the .cpp files as part of your project and not as a library.
107  - You can later customize the imconfig.h file to tweak some compilation time behavior, such as integrating imgui types with your own maths types.
108  - See examples/ folder for standalone sample applications.
109  - You may be able to grab and copy a ready made imgui_impl_*** file from the examples/.
110  - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
111 
112  - Init: retrieve the ImGuiIO structure with ImGui::GetIO() and fill the fields marked 'Settings': at minimum you need to set io.DisplaySize
113    (application resolution). Later on you will fill your keyboard mapping, clipboard handlers, and other advanced features but for a basic
114    integration you don't need to worry about it all.
115  - Init: call io.Fonts->GetTexDataAsRGBA32(...), it will build the font atlas texture, then load the texture pixels into graphics memory.
116  - Every frame:
117     - In your main loop as early a possible, fill the IO fields marked 'Input' (e.g. mouse position, buttons, keyboard info, etc.)
118     - Call ImGui::NewFrame() to begin the frame
119     - You can use any ImGui function you want between NewFrame() and Render()
120     - Call ImGui::Render() as late as you can to end the frame and finalize render data. it will call your io.RenderDrawListFn handler.
121        (Even if you don't render, call Render() and ignore the callback, or call EndFrame() instead. Otherwhise some features will break)
122  - All rendering information are stored into command-lists until ImGui::Render() is called.
123  - Dear ImGui never touches or knows about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide.
124  - Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases
125    of your own application.
126  - Refer to the examples applications in the examples/ folder for instruction on how to setup your code.
127  - A minimal application skeleton may be:
128 
129      // Application init
130      ImGui::CreateContext();
131      ImGuiIO& io = ImGui::GetIO();
132      io.DisplaySize.x = 1920.0f;
133      io.DisplaySize.y = 1280.0f;
134      // TODO: Fill others settings of the io structure later.
135 
136      // Load texture atlas (there is a default font so you don't need to care about choosing a font yet)
137      unsigned char* pixels;
138      int width, height;
139      io.Fonts->GetTexDataAsRGBA32(pixels, &width, &height);
140      // TODO: At this points you've got the texture data and you need to upload that your your graphic system:
141      MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA)
142      // TODO: Store your texture pointer/identifier (whatever your engine uses) in 'io.Fonts->TexID'. This will be passed back to your via the renderer.
143      io.Fonts->TexID = (void*)texture;
144 
145      // Application main loop
146      while (true)
147      {
148         // Setup low-level inputs (e.g. on Win32, GetKeyboardState(), or write to those fields from your Windows message loop handlers, etc.)
149         ImGuiIO& io = ImGui::GetIO();
150         io.DeltaTime = 1.0f/60.0f;
151         io.MousePos = mouse_pos;
152         io.MouseDown[0] = mouse_button_0;
153         io.MouseDown[1] = mouse_button_1;
154 
155         // Call NewFrame(), after this point you can use ImGui::* functions anytime
156         ImGui::NewFrame();
157 
158         // Most of your application code here
159         MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
160         MyGameRender(); // may use any ImGui functions as well!
161 
162         // Render & swap video buffers
163         ImGui::Render();
164         MyImGuiRenderFunction(ImGui::GetDrawData());
165         SwapBuffers();
166      }
167 
168      // Shutdown
169      ImGui::DestroyContext();
170 
171 
172  - A minimal render function skeleton may be:
173 
174     void void MyRenderFunction(ImDrawData* draw_data)
175     {
176        // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
177        // TODO: Setup viewport, orthographic projection matrix
178        // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
179        for (int n = 0; n < draw_data->CmdListsCount; n++)
180        {
181           const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by ImGui
182           const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by ImGui
183           for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
184           {
185              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
186              if (pcmd->UserCallback)
187              {
188                  pcmd->UserCallback(cmd_list, pcmd);
189              }
190              else
191              {
192                  // The texture for the draw call is specified by pcmd->TextureId.
193                  // The vast majority of draw calls with use the imgui texture atlas, which value you have set yourself during initialization.
194                  MyEngineBindTexture(pcmd->TextureId);
195 
196                  // We are using scissoring to clip some objects. All low-level graphics API supports it.
197                  // If your engine doesn't support scissoring yet, you will get some small glitches (some elements outside their bounds) which you can fix later.
198                  MyEngineScissor((int)pcmd->ClipRect.x, (int)pcmd->ClipRect.y, (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y));
199 
200                  // Render 'pcmd->ElemCount/3' indexed triangles.
201                  // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits if your engine doesn't support 16-bits indices.
202                  MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
203              }
204              idx_buffer += pcmd->ElemCount;
205           }
206        }
207     }
208 
209  - The examples/ folders contains many functional implementation of the pseudo-code above.
210  - When calling NewFrame(), the 'io.WantCaptureMouse'/'io.WantCaptureKeyboard'/'io.WantTextInput' flags are updated.
211    They tell you if ImGui intends to use your inputs. So for example, if 'io.WantCaptureMouse' is set you would typically want to hide
212    mouse inputs from the rest of your application. Read the FAQ below for more information about those flags.
213 
214  USING GAMEPAD/KEYBOARD NAVIGATION [BETA]
215 
216  - Ask questions and report issues at https://github.com/ocornut/imgui/issues/787
217  - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
218  - Keyboard:
219     - Set io.NavFlags |= ImGuiNavFlags_EnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays.
220     - When keyboard navigation is active (io.NavActive + NavFlags_EnableKeyboard), the io.WantCaptureKeyboard flag will be set.
221       For more advanced uses, you may want to read from:
222        - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
223        - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
224        - or query focus information with e.g. IsWindowFocused(), IsItemFocused() etc. functions.
225       Please reach out if you think the game vs navigation input sharing could be improved.
226  - Gamepad:
227     - Set io.NavFlags |= ImGuiNavFlags_EnableGamepad to enable. Fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame().
228     - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
229          0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
230     - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
231       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, maybe a power curve, etc.).
232     - 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 to toggle the target.
233       Please reach out if you think the game vs navigation input sharing could be improved.
234  - Mouse:
235     - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
236     - Consoles/Tablet/Phone users: Consider using Synergy host (on your computer) + uSynergy.c (in your console/tablet/phone app) to use your PC mouse/keyboard.
237     - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiNavFlags_MoveMouse flag in io.NavFlags.
238       Enabling ImGuiNavFlags_MoveMouse instructs dear imgui to move your mouse cursor along with navigation movements.
239       When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantMoveMouse' to notify you that it wants the mouse cursor to be moved.
240       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.
241       (If you set the ImGuiNavFlags_MoveMouse flag but don't honor 'io.WantMoveMouse' properly, imgui will misbehave as it will see your mouse as moving back and forth.)
242       (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
243        to set a boolean to ignore your other external mouse positions until the external source is moved again.)
244 
245 
246  API BREAKING CHANGES
247  ====================
248 
249  Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix.
250  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.
251  Also read releases logs https://github.com/ocornut/imgui/releases for more details.
252 
253  - 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is really usable in typical conditions at the moment.
254  - 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.
255  - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
256                        - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
257                        - removed Shutdown() function, as DestroyContext() serve this purpose.
258                        - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwhise CreateContext() will create its own font atlas instance.
259                        - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
260                        - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
261  - 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.
262  - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
263  - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
264  - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
265  - 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.
266  - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
267  - 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
268  - 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.
269  - 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.
270  - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
271  - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
272                      - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
273  - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
274  - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
275  - 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.
276  - 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.
277                        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.
278  - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
279  - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
280  - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
281  - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
282  - 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.
283  - 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.
284  - 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.
285                        removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
286  - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
287  - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
288  - 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).
289  - 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)".
290  - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
291                      - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
292                      - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
293  - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
294  - 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.
295  - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame.
296  - 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.
297  - 2017/08/13 (1.51) - renamed ImGuiCol_Columns*** to ImGuiCol_Separator***. Kept redirection enums (will obsolete).
298  - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete).
299  - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
300  - 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.
301                      - 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.
302                      - 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))'
303  - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
304  - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
305  - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
306  - 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().
307  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
308  - 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.
309  - 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.
310  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
311                        If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you.
312                        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.
313                        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.
314                            ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
315                            {
316                                float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
317                                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);
318                            }
319                        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.
320  - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
321  - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
322  - 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).
323  - 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.
324  - 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).
325  - 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)
326  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
327  - 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.
328  - 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.
329  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
330  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
331  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
332                        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.
333                        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!
334  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
335  - 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.
336  - 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
337  - 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.
338                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
339  - 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.
340                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
341                      - 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.
342                      - the signature of the io.RenderDrawListsFn handler has changed!
343                             ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
344                        became:
345                             ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
346                               argument   'cmd_lists'        -> 'draw_data->CmdLists'
347                               argument   'cmd_lists_count'  -> 'draw_data->CmdListsCount'
348                               ImDrawList 'commands'         -> 'CmdBuffer'
349                               ImDrawList 'vtx_buffer'       -> 'VtxBuffer'
350                               ImDrawList  n/a               -> 'IdxBuffer' (new)
351                               ImDrawCmd  'vtx_count'        -> 'ElemCount'
352                               ImDrawCmd  'clip_rect'        -> 'ClipRect'
353                               ImDrawCmd  'user_callback'    -> 'UserCallback'
354                               ImDrawCmd  'texture_id'       -> 'TextureId'
355                      - 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.
356                      - 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!
357                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
358  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
359  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
360  - 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.
361  - 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
362  - 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!
363  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
364  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
365  - 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.
366  - 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.
367  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
368  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
369  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
370  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
371  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
372  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
373  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
374  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
375  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
376  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
377  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
378  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
379  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
380  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
381  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
382  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
383  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
384               (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
385                        this sequence:
386                            const void* png_data;
387                            unsigned int png_size;
388                            ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size);
389                            // <Copy to GPU>
390                        became:
391                            unsigned char* pixels;
392                            int width, height;
393                            io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
394                            // <Copy to GPU>
395                            io.Fonts->TexID = (your_texture_identifier);
396                        you now have much more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
397                        it is now recommended that you sample the font texture with bilinear interpolation.
398               (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
399               (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
400               (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
401  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
402  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
403  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
404  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
405  - 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)
406  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
407  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
408  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
409  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
410  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
411  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
412 
413 
414  ISSUES & TODO-LIST
415  ==================
416  See TODO.txt
417 
418 
419  FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
420  ======================================
421 
422  Q: How can I help?
423  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!
424     - Convince your company to fund development time! Individual users: you can also become a Patron (patreon.com/imgui) or donate on PayPal! See README.
425     - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
426       You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1269). Visuals are ideal as they inspire other programmers.
427       But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
428     - 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).
429 
430  Q: How can I display an image? What is ImTextureID, how does it works?
431  A: ImTextureID is a void* used to pass renderer-agnostic texture references around until it hits your render function.
432     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!
433     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.
434     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.
435     Refer to examples applications, where each renderer (in a imgui_impl_xxxx.cpp file) is treating ImTextureID as a different thing.
436     (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!)
437     To display a custom image/texture within an ImGui window, you may use ImGui::Image(), ImGui::ImageButton(), ImDrawList::AddImage() functions.
438     Dear ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use.
439     You may call ImGui::ShowMetricsWindow() to explore active draw lists and visualize/understand how the draw data is generated.
440     It is your responsibility to get textures uploaded to your GPU.
441 
442  Q: Can I have multiple widgets with the same label? Can I have widget without a label?
443  A: Yes. A primer on labels and the ID stack...
444 
445    - Elements that are typically not clickable, such as Text() items don't need an ID.
446 
447    - Interactive widgets require state to be carried over multiple frames (most typically Dear ImGui often needs to remember what is
448      the "active" widget). to do so they need a unique ID. unique ID are typically derived from a string label, an integer index or a pointer.
449 
450        Button("OK");          // Label = "OK",     ID = hash of "OK"
451        Button("Cancel");      // Label = "Cancel", ID = hash of "Cancel"
452 
453    - ID are uniquely scoped within windows, tree nodes, etc. so no conflict can happen if you have two buttons called "OK"
454      in two different windows or in two different locations of a tree.
455 
456    - If you have a same ID twice in the same location, you'll have a conflict:
457 
458        Button("OK");
459        Button("OK");          // ID collision! Both buttons will be treated as the same.
460 
461      Fear not! this is easy to solve and there are many ways to solve it!
462 
463    - When passing a label you can optionally specify extra unique ID information within string itself.
464      Use "##" to pass a complement to the ID that won't be visible to the end-user.
465      This helps solving the simple collision cases when you know which items are going to be created.
466 
467        Button("Play");        // Label = "Play",   ID = hash of "Play"
468        Button("Play##foo1");  // Label = "Play",   ID = hash of "Play##foo1" (different from above)
469        Button("Play##foo2");  // Label = "Play",   ID = hash of "Play##foo2" (different from above)
470 
471    - If you want to completely hide the label, but still need an ID:
472 
473        Checkbox("##On", &b);  // Label = "",       ID = hash of "##On" (no label!)
474 
475    - Occasionally/rarely you might want change a label while preserving a constant ID. This allows you to animate labels.
476      For example you may want to include varying information in a window title bar, but windows are uniquely identified by their ID..
477      Use "###" to pass a label that isn't part of ID:
478 
479        Button("Hello###ID";   // Label = "Hello",  ID = hash of "ID"
480        Button("World###ID";   // Label = "World",  ID = hash of "ID" (same as above)
481 
482        sprintf(buf, "My game (%f FPS)###MyGame", fps);
483        Begin(buf);            // Variable label,   ID = hash of "MyGame"
484 
485    - Use PushID() / PopID() to create scopes and avoid ID conflicts within the same Window.
486      This is the most convenient way of distinguishing ID if you are iterating and creating many UI elements.
487      You can push a pointer, a string or an integer value. Remember that ID are formed from the concatenation of _everything_ in the ID stack!
488 
489        for (int i = 0; i < 100; i++)
490        {
491          PushID(i);
492          Button("Click");   // Label = "Click",  ID = hash of integer + "label" (unique)
493          PopID();
494        }
495 
496        for (int i = 0; i < 100; i++)
497        {
498          MyObject* obj = Objects[i];
499          PushID(obj);
500          Button("Click");   // Label = "Click",  ID = hash of pointer + "label" (unique)
501          PopID();
502        }
503 
504        for (int i = 0; i < 100; i++)
505        {
506          MyObject* obj = Objects[i];
507          PushID(obj->Name);
508          Button("Click");   // Label = "Click",  ID = hash of string + "label" (unique)
509          PopID();
510        }
511 
512    - More example showing that you can stack multiple prefixes into the ID stack:
513 
514        Button("Click");     // Label = "Click",  ID = hash of "Click"
515        PushID("node");
516        Button("Click");     // Label = "Click",  ID = hash of "node" + "Click"
517          PushID(my_ptr);
518            Button("Click"); // Label = "Click",  ID = hash of "node" + ptr + "Click"
519          PopID();
520        PopID();
521 
522    - Tree nodes implicitly creates a scope for you by calling PushID().
523 
524        Button("Click");     // Label = "Click",  ID = hash of "Click"
525        if (TreeNode("node"))
526        {
527          Button("Click");   // Label = "Click",  ID = hash of "node" + "Click"
528          TreePop();
529        }
530 
531    - When working with trees, ID are used to preserve the open/close state of each tree node.
532      Depending on your use cases you may want to use strings, indices or pointers as ID.
533       e.g. when displaying a single object that may change over time (dynamic 1-1 relationship), using a static string as ID will preserve your
534        node open/closed state when the targeted object change.
535       e.g. when displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state differently.
536        experiment and see what makes more sense!
537 
538  Q: How can I tell when Dear ImGui wants my mouse/keyboard inputs VS when I can pass them to my application?
539  A: You can read the 'io.WantCaptureMouse'/'io.WantCaptureKeyboard'/'ioWantTextInput' flags from the ImGuiIO structure.
540     - When 'io.WantCaptureMouse' or 'io.WantCaptureKeyboard' flags are set you may want to discard/hide the inputs from the rest of your application.
541     - 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).
542     Preferably read the flags after calling ImGui::NewFrame() to avoid them lagging by one frame. But reading those flags before calling NewFrame() is
543     also generally ok, as the bool toggles fairly rarely and you don't generally expect to interact with either Dear ImGui or your application during
544     the same frame when that transition occurs. Dear ImGui is tracking dragging and widget activity that may occur outside the boundary of a window,
545     so 'io.WantCaptureMouse' is more accurate and correct than checking if a window is hovered.
546     (Advanced note: text input releases focus on Return 'KeyDown', so the following Return 'KeyUp' event that your application receive will typically
547      have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
548      were for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
549 
550  Q: How can I load a different font than the default? (default is an embedded version of ProggyClean.ttf, rendered at size 13)
551  A: Use the font atlas to load the TTF/OTF file you want:
552       ImGuiIO& io = ImGui::GetIO();
553       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
554       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
555 
556     New programmers: remember that in C/C++ and most programming languages if you want to use a backslash \ in a string literal you need to write a double backslash "\\":
557       io.Fonts->AddFontFromFileTTF("MyDataFolder\MyFontFile.ttf", size_in_pixels);   // WRONG
558       io.Fonts->AddFontFromFileTTF("MyDataFolder\\MyFontFile.ttf", size_in_pixels);  // CORRECT
559       io.Fonts->AddFontFromFileTTF("MyDataFolder/MyFontFile.ttf", size_in_pixels);   // ALSO CORRECT
560 
561  Q: How can I easily use icons in my application?
562  A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you main font. Then you can refer to icons within your
563     strings. Read 'How can I load multiple fonts?' and the file 'misc/fonts/README.txt' for instructions and useful header files.
564 
565  Q: How can I load multiple fonts?
566  A: Use the font atlas to pack them into a single texture:
567     (Read misc/fonts/README.txt and the code in ImFontAtlas for more details.)
568 
569       ImGuiIO& io = ImGui::GetIO();
570       ImFont* font0 = io.Fonts->AddFontDefault();
571       ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
572       ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
573       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
574       // the first loaded font gets used by default
575       // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
576 
577       // Options
578       ImFontConfig config;
579       config.OversampleH = 3;
580       config.OversampleV = 1;
581       config.GlyphOffset.y -= 2.0f;      // Move everything by 2 pixels up
582       config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters
583       io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config);
584 
585       // Combine multiple fonts into one (e.g. for icon fonts)
586       ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
587       ImFontConfig config;
588       config.MergeMode = true;
589       io.Fonts->AddFontDefault();
590       io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font
591       io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs
592 
593  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
594  A: When loading a font, pass custom Unicode ranges to specify the glyphs to load.
595 
596       // Add default Japanese ranges
597       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese());
598 
599       // 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)
600       ImVector<ImWchar> ranges;
601       ImFontAtlas::GlyphRangesBuilder builder;
602       builder.AddText("Hello world");                        // Add a string (here "Hello world" contains 7 unique characters)
603       builder.AddChar(0x7262);                               // Add a specific character
604       builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges
605       builder.BuildRanges(&ranges);                          // Build the final result (ordered ranges with all the unique characters submitted)
606       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data);
607 
608     All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8 by using the u8"hello" syntax.
609     Specifying literal in your source code using a local code page (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work!
610     Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
611 
612     Text input: it is up to your application to pass the right character code to io.AddInputCharacter(). The applications in examples/ are doing that.
613     For languages using IME, on Windows you can copy the Hwnd of your application to io.ImeWindowHandle.
614     The default implementation of io.ImeSetInputScreenPosFn() on Windows will set your IME position correctly.
615 
616  Q: How can I preserve my Dear ImGui context across reloading a DLL? (loss of the global/static variables)
617  A: Create your own context 'ctx = CreateContext()' + 'SetCurrentContext(ctx)' and your own font atlas 'ctx->GetIO().Fonts = new ImFontAtlas()'
618     so you don't rely on the default globals.
619 
620  Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
621  A: - You can create a dummy window. Call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flag,
622       push a ImGuiCol_WindowBg with zero alpha, then retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
623     - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows.
624     - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create your own ImDrawListSharedData.
625 
626  Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
627  A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
628     Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
629 
630  Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
631  A: You are probably mishandling the clipping rectangles in your render function.
632     Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
633 
634 
635  - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window.
636         this is also useful to set yourself in the context of another window (to get/set other settings)
637  - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug".
638  - 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
639         of a deep nested inner loop in your code.
640  - tip: you can call Render() multiple times (e.g for VR renders).
641  - tip: call and read the ShowDemoWindow() code in imgui_demo.cpp for more example of how to use ImGui!
642 
643 */
644 
645 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
646 #define _CRT_SECURE_NO_WARNINGS
647 #endif
648 
649 #include "imgui.h"
650 #define IMGUI_DEFINE_MATH_OPERATORS
651 #include "imgui_internal.h"
652 
653 #include <ctype.h>                         // toupper, isprint
654 #include <stdlib.h>                        // NULL, malloc, free, qsort, atoi
655 #include <stdio.h>                         // vsnprintf, sscanf, printf
656 #if defined(_MSC_VER) && _MSC_VER <= 1500  // MSVC 2008 or earlier
657 #include <stddef.h>                        // intptr_t
658 #else
659 #include <stdint.h>  // intptr_t
660 #endif
661 
662 #define IMGUI_DEBUG_NAV_SCORING 0
663 #define IMGUI_DEBUG_NAV_RECTS 0
664 
665 // Visual Studio warnings
666 #ifdef _MSC_VER
667 #pragma warning(disable : 4127)  // condition expression is constant
668 #pragma warning(disable : 4505)  // unreferenced local function has been removed (stb stuff)
669 #pragma warning(disable : 4996)  // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
670 #endif
671 
672 // Clang warnings with -Weverything
673 #ifdef __clang__
674 #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!
675 #pragma clang diagnostic ignored "-Wold-style-cast"            // warning : use of old-style cast                              // yes, they are more terse.
676 #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.
677 #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.
678 #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.
679 #pragma clang diagnostic ignored "-Wglobal-constructors"       // warning : declaration requires a global destructor           // similar to above, not sure what the exact difference it.
680 #pragma clang diagnostic ignored "-Wsign-conversion"           // warning : implicit conversion changes signedness             //
681 #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.
682 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"  // warning : cast to 'void *' from smaller integer type 'int' //
683 #elif defined(__GNUC__)
684 #pragma GCC diagnostic ignored "-Wunused-function"      // warning: 'xxxx' defined but not used
685 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"  // warning: cast to pointer from integer of different size
686 #pragma GCC diagnostic ignored "-Wformat"               // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
687 #pragma GCC diagnostic ignored "-Wdouble-promotion"     // warning: implicit conversion from 'float' to 'double' when passing argument to function
688 #pragma GCC diagnostic ignored "-Wconversion"           // warning: conversion to 'xxxx' from 'xxxx' may alter its value
689 #pragma GCC diagnostic ignored "-Wcast-qual"            // warning: cast from type 'xxxx' to type 'xxxx' casts away qualifiers
690 #pragma GCC diagnostic ignored "-Wformat-nonliteral"    // warning: format not a string literal, format string not checked
691 #pragma GCC diagnostic ignored "-Wstrict-overflow"      // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
692 #endif
693 
694 // Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall
695 #ifdef _MSC_VER
696 #define IMGUI_CDECL __cdecl
697 #else
698 #define IMGUI_CDECL
699 #endif
700 
701 //-------------------------------------------------------------------------
702 // Forward Declarations
703 //-------------------------------------------------------------------------
704 
705 static bool IsKeyPressedMap(ImGuiKey key, bool repeat = true);
706 
707 static ImFont* GetDefaultFont();
708 static void SetCurrentWindow(ImGuiWindow* window);
709 static void SetWindowScrollX(ImGuiWindow* window, float new_scroll_x);
710 static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y);
711 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond);
712 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond);
713 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond);
714 static ImGuiWindow* FindHoveredWindow();
715 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
716 static void CheckStacksSize(ImGuiWindow* window, bool write);
717 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
718 
719 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
720 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_list, ImGuiWindow* window);
721 static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
722 
723 static ImGuiWindowSettings* AddWindowSettings(const char* name);
724 
725 static void LoadIniSettingsFromDisk(const char* ini_filename);
726 static void LoadIniSettingsFromMemory(const char* buf);
727 static void SaveIniSettingsToDisk(const char* ini_filename);
728 static void SaveIniSettingsToMemory(ImVector<char>& out_buf);
729 static void MarkIniSettingsDirty(ImGuiWindow* window);
730 
731 static ImRect GetViewportRect();
732 
733 static void ClosePopupToLevel(int remaining);
734 static ImGuiWindow* GetFrontMostModalRootWindow();
735 
736 static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data);
737 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
738 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);
739 
740 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size);
741 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size);
742 static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2);
743 static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format);
744 
745 namespace ImGui
746 {
747 static void NavUpdate();
748 static void NavUpdateWindowing();
749 static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id);
750 
751 static void UpdateMovingWindow();
752 static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
753 static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window);
754 }  // namespace ImGui
755 
756 //-----------------------------------------------------------------------------
757 // Platform dependent default implementations
758 //-----------------------------------------------------------------------------
759 
760 static const char* GetClipboardTextFn_DefaultImpl(void* user_data);
761 static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
762 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
763 
764 //-----------------------------------------------------------------------------
765 // Context
766 //-----------------------------------------------------------------------------
767 
768 // Current context pointer. Implicitely used by all ImGui functions. Always assumed to be != NULL.
769 // CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
770 // If you use DLL hotreloading you might need to call SetCurrentContext() after reloading code from this file.
771 // 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:
772 // - 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
773 // - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts)
774 #ifndef GImGui
775 ImGuiContext* GImGui = NULL;
776 #endif
777 
778 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
779 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
780 // 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.
781 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)782 static void* MallocWrapper(size_t size, void* user_data)
783 {
784 	(void)user_data;
785 	return malloc(size);
786 }
FreeWrapper(void * ptr,void * user_data)787 static void FreeWrapper(void* ptr, void* user_data)
788 {
789 	(void)user_data;
790 	free(ptr);
791 }
792 #else
MallocWrapper(size_t size,void * user_data)793 static void* MallocWrapper(size_t size, void* user_data)
794 {
795 	(void)user_data;
796 	(void)size;
797 	IM_ASSERT(0);
798 	return NULL;
799 }
FreeWrapper(void * ptr,void * user_data)800 static void FreeWrapper(void* ptr, void* user_data)
801 {
802 	(void)user_data;
803 	(void)ptr;
804 	IM_ASSERT(0);
805 }
806 #endif
807 
808 static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
809 static void (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
810 static void* GImAllocatorUserData = NULL;
811 static size_t GImAllocatorActiveAllocationsCount = 0;
812 
813 //-----------------------------------------------------------------------------
814 // User facing structures
815 //-----------------------------------------------------------------------------
816 
ImGuiStyle()817 ImGuiStyle::ImGuiStyle()
818 {
819 	Alpha = 1.0f;                           // Global alpha applies to everything in ImGui
820 	WindowPadding = ImVec2(8, 8);           // Padding within a window
821 	WindowRounding = 7.0f;                  // Radius of window corners rounding. Set to 0.0f to have rectangular windows
822 	WindowBorderSize = 1.0f;                // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
823 	WindowMinSize = ImVec2(32, 32);         // Minimum window size
824 	WindowTitleAlign = ImVec2(0.0f, 0.5f);  // Alignment for title bar text
825 	ChildRounding = 0.0f;                   // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
826 	ChildBorderSize = 1.0f;                 // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
827 	PopupRounding = 0.0f;                   // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
828 	PopupBorderSize = 1.0f;                 // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
829 	FramePadding = ImVec2(4, 3);            // Padding within a framed rectangle (used by most widgets)
830 	FrameRounding = 0.0f;                   // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
831 	FrameBorderSize = 0.0f;                 // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
832 	ItemSpacing = ImVec2(8, 4);             // Horizontal and vertical spacing between widgets/lines
833 	ItemInnerSpacing = ImVec2(4, 4);        // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
834 	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!
835 	IndentSpacing = 21.0f;                  // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
836 	ColumnsMinSpacing = 6.0f;               // Minimum horizontal spacing between two columns
837 	ScrollbarSize = 16.0f;                  // Width of the vertical scrollbar, Height of the horizontal scrollbar
838 	ScrollbarRounding = 9.0f;               // Radius of grab corners rounding for scrollbar
839 	GrabMinSize = 10.0f;                    // Minimum width/height of a grab box for slider/scrollbar
840 	GrabRounding = 0.0f;                    // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
841 	ButtonTextAlign = ImVec2(0.5f, 0.5f);   // Alignment of button text when button is larger than text.
842 	DisplayWindowPadding = ImVec2(22, 22);  // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows.
843 	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.
844 	MouseCursorScale = 1.0f;                // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
845 	AntiAliasedLines = true;                // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
846 	AntiAliasedFill = true;                 // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
847 	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.
848 
849 	ImGui::StyleColorsClassic(this);
850 }
851 
852 // 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.
853 // 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)854 void ImGuiStyle::ScaleAllSizes(float scale_factor)
855 {
856 	WindowPadding = ImFloor(WindowPadding * scale_factor);
857 	WindowRounding = ImFloor(WindowRounding * scale_factor);
858 	WindowMinSize = ImFloor(WindowMinSize * scale_factor);
859 	ChildRounding = ImFloor(ChildRounding * scale_factor);
860 	PopupRounding = ImFloor(PopupRounding * scale_factor);
861 	FramePadding = ImFloor(FramePadding * scale_factor);
862 	FrameRounding = ImFloor(FrameRounding * scale_factor);
863 	ItemSpacing = ImFloor(ItemSpacing * scale_factor);
864 	ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
865 	TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
866 	IndentSpacing = ImFloor(IndentSpacing * scale_factor);
867 	ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
868 	ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
869 	ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
870 	GrabMinSize = ImFloor(GrabMinSize * scale_factor);
871 	GrabRounding = ImFloor(GrabRounding * scale_factor);
872 	DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
873 	DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
874 	MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
875 }
876 
ImGuiIO()877 ImGuiIO::ImGuiIO()
878 {
879 	// Most fields are initialized with zero
880 	memset(this, 0, sizeof(*this));
881 
882 	// Settings
883 	DisplaySize = ImVec2(-1.0f, -1.0f);
884 	DeltaTime = 1.0f / 60.0f;
885 	NavFlags = 0x00;
886 	IniSavingRate = 5.0f;
887 	IniFilename = "imgui.ini";
888 	LogFilename = "imgui_log.txt";
889 	MouseDoubleClickTime = 0.30f;
890 	MouseDoubleClickMaxDist = 6.0f;
891 	for (int i = 0; i < ImGuiKey_COUNT; i++)
892 		KeyMap[i] = -1;
893 	KeyRepeatDelay = 0.250f;
894 	KeyRepeatRate = 0.050f;
895 	UserData = NULL;
896 
897 	Fonts = NULL;
898 	FontGlobalScale = 1.0f;
899 	FontDefault = NULL;
900 	FontAllowUserScaling = false;
901 	DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
902 	DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f);
903 
904 	// Advanced/subtle behaviors
905 #ifdef __APPLE__
906 	OptMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
907 #else
908 	OptMacOSXBehaviors = false;
909 #endif
910 	OptCursorBlink = true;
911 
912 	// Settings (User Functions)
913 	GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;  // Platform dependent default implementations
914 	SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
915 	ClipboardUserData = NULL;
916 	ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
917 	ImeWindowHandle = NULL;
918 
919 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
920 	RenderDrawListsFn = NULL;
921 #endif
922 
923 	// Input (NB: we already have memset zero the entire structure)
924 	MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
925 	MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
926 	MouseDragThreshold = 6.0f;
927 	for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
928 	for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
929 	for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
930 }
931 
932 // Pass in translated ASCII characters for text input.
933 // - with glfw you can get those from the callback set in glfwSetCharCallback()
934 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(ImWchar c)935 void ImGuiIO::AddInputCharacter(ImWchar c)
936 {
937 	const int n = ImStrlenW(InputCharacters);
938 	if (n + 1 < IM_ARRAYSIZE(InputCharacters))
939 	{
940 		InputCharacters[n] = c;
941 		InputCharacters[n + 1] = '\0';
942 	}
943 }
944 
AddInputCharactersUTF8(const char * utf8_chars)945 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
946 {
947 	// We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more
948 	const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar);
949 	ImWchar wchars[wchars_buf_len];
950 	ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL);
951 	for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++)
952 		AddInputCharacter(wchars[i]);
953 }
954 
955 //-----------------------------------------------------------------------------
956 // HELPERS
957 //-----------------------------------------------------------------------------
958 
959 #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL)*255.0f + ((_VAL) >= 0 ? 0.5f : -0.5f)))  // Unsaturated, for display purpose
960 #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f))                  // Saturated, always output 0..255
961 
962 // Play it nice with Windows users. Notepad in 2015 still doesn't display text data with Unix-style \n.
963 #ifdef _WIN32
964 #define IM_NEWLINE "\r\n"
965 #else
966 #define IM_NEWLINE "\n"
967 #endif
968 
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)969 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
970 {
971 	ImVec2 ap = p - a;
972 	ImVec2 ab_dir = b - a;
973 	float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
974 	if (dot < 0.0f)
975 		return a;
976 	float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
977 	if (dot > ab_len_sqr)
978 		return b;
979 	return a + ab_dir * dot / ab_len_sqr;
980 }
981 
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)982 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
983 {
984 	bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
985 	bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
986 	bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
987 	return ((b1 == b2) && (b2 == b3));
988 }
989 
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)990 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
991 {
992 	ImVec2 v0 = b - a;
993 	ImVec2 v1 = c - a;
994 	ImVec2 v2 = p - a;
995 	const float denom = v0.x * v1.y - v1.x * v0.y;
996 	out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
997 	out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
998 	out_u = 1.0f - out_v - out_w;
999 }
1000 
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1001 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1002 {
1003 	ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1004 	ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1005 	ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1006 	float dist2_ab = ImLengthSqr(p - proj_ab);
1007 	float dist2_bc = ImLengthSqr(p - proj_bc);
1008 	float dist2_ca = ImLengthSqr(p - proj_ca);
1009 	float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1010 	if (m == dist2_ab)
1011 		return proj_ab;
1012 	if (m == dist2_bc)
1013 		return proj_bc;
1014 	return proj_ca;
1015 }
1016 
ImStricmp(const char * str1,const char * str2)1017 int ImStricmp(const char* str1, const char* str2)
1018 {
1019 	int d;
1020 	while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1)
1021 	{
1022 		str1++;
1023 		str2++;
1024 	}
1025 	return d;
1026 }
1027 
ImStrnicmp(const char * str1,const char * str2,size_t count)1028 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1029 {
1030 	int d = 0;
1031 	while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1)
1032 	{
1033 		str1++;
1034 		str2++;
1035 		count--;
1036 	}
1037 	return d;
1038 }
1039 
ImStrncpy(char * dst,const char * src,size_t count)1040 void ImStrncpy(char* dst, const char* src, size_t count)
1041 {
1042 	if (count < 1) return;
1043 	strncpy(dst, src, count);
1044 	dst[count - 1] = 0;
1045 }
1046 
ImStrdup(const char * str)1047 char* ImStrdup(const char* str)
1048 {
1049 	size_t len = strlen(str) + 1;
1050 	void* buf = ImGui::MemAlloc(len);
1051 	return (char*)memcpy(buf, (const void*)str, len);
1052 }
1053 
ImStrchrRange(const char * str,const char * str_end,char c)1054 char* ImStrchrRange(const char* str, const char* str_end, char c)
1055 {
1056 	for (; str < str_end; str++)
1057 		if (*str == c)
1058 			return (char*)str;
1059 	return NULL;
1060 }
1061 
ImStrlenW(const ImWchar * str)1062 int ImStrlenW(const ImWchar* str)
1063 {
1064 	int n = 0;
1065 	while (*str++) n++;
1066 	return n;
1067 }
1068 
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1069 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin)  // find beginning-of-line
1070 {
1071 	while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1072 		buf_mid_line--;
1073 	return buf_mid_line;
1074 }
1075 
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1076 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1077 {
1078 	if (!needle_end)
1079 		needle_end = needle + strlen(needle);
1080 
1081 	const char un0 = (char)toupper(*needle);
1082 	while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1083 	{
1084 		if (toupper(*haystack) == un0)
1085 		{
1086 			const char* b = needle + 1;
1087 			for (const char* a = haystack + 1; b < needle_end; a++, b++)
1088 				if (toupper(*a) != toupper(*b))
1089 					break;
1090 			if (b == needle_end)
1091 				return haystack;
1092 		}
1093 		haystack++;
1094 	}
1095 	return NULL;
1096 }
1097 
ImAtoi(const char * src,int * output)1098 static const char* ImAtoi(const char* src, int* output)
1099 {
1100 	int negative = 0;
1101 	if (*src == '-')
1102 	{
1103 		negative = 1;
1104 		src++;
1105 	}
1106 	if (*src == '+')
1107 	{
1108 		src++;
1109 	}
1110 	int v = 0;
1111 	while (*src >= '0' && *src <= '9')
1112 		v = (v * 10) + (*src++ - '0');
1113 	*output = negative ? -v : v;
1114 	return src;
1115 }
1116 
1117 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1118 // 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.
1119 // B) When buf==NULL vsnprintf() will return the output size.
1120 #ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1121 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1122 {
1123 	va_list args;
1124 	va_start(args, fmt);
1125 	int w = vsnprintf(buf, buf_size, fmt, args);
1126 	va_end(args);
1127 	if (buf == NULL)
1128 		return w;
1129 	if (w == -1 || w >= (int)buf_size)
1130 		w = (int)buf_size - 1;
1131 	buf[w] = 0;
1132 	return w;
1133 }
1134 
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1135 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1136 {
1137 	int w = vsnprintf(buf, buf_size, fmt, args);
1138 	if (buf == NULL)
1139 		return w;
1140 	if (w == -1 || w >= (int)buf_size)
1141 		w = (int)buf_size - 1;
1142 	buf[w] = 0;
1143 	return w;
1144 }
1145 #endif  // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1146 
1147 // Pass data_size==0 for zero-terminated strings
1148 // 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)1149 ImU32 ImHash(const void* data, int data_size, ImU32 seed)
1150 {
1151 	static ImU32 crc32_lut[256] = {0};
1152 	if (!crc32_lut[1])
1153 	{
1154 		const ImU32 polynomial = 0xEDB88320;
1155 		for (ImU32 i = 0; i < 256; i++)
1156 		{
1157 			ImU32 crc = i;
1158 			for (ImU32 j = 0; j < 8; j++)
1159 				crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial);
1160 			crc32_lut[i] = crc;
1161 		}
1162 	}
1163 
1164 	seed = ~seed;
1165 	ImU32 crc = seed;
1166 	const unsigned char* current = (const unsigned char*)data;
1167 
1168 	if (data_size > 0)
1169 	{
1170 		// Known size
1171 		while (data_size--)
1172 			crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++];
1173 	}
1174 	else
1175 	{
1176 		// Zero-terminated string
1177 		while (unsigned char c = *current++)
1178 		{
1179 			// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1180 			// Because this syntax is rarely used we are optimizing for the common case.
1181 			// - If we reach ### in the string we discard the hash so far and reset to the seed.
1182 			// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller.
1183 			if (c == '#' && current[0] == '#' && current[1] == '#')
1184 				crc = seed;
1185 			crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1186 		}
1187 	}
1188 	return ~crc;
1189 }
1190 
1191 //-----------------------------------------------------------------------------
1192 // ImText* helpers
1193 //-----------------------------------------------------------------------------
1194 
1195 // Convert UTF-8 to 32-bits character, process single character input.
1196 // Based on stb_from_utf8() from github.com/nothings/stb/
1197 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1198 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1199 {
1200 	unsigned int c = (unsigned int)-1;
1201 	const unsigned char* str = (const unsigned char*)in_text;
1202 	if (!(*str & 0x80))
1203 	{
1204 		c = (unsigned int)(*str++);
1205 		*out_char = c;
1206 		return 1;
1207 	}
1208 	if ((*str & 0xe0) == 0xc0)
1209 	{
1210 		*out_char = 0xFFFD;  // will be invalid but not end of string
1211 		if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1212 		if (*str < 0xc2) return 2;
1213 		c = (unsigned int)((*str++ & 0x1f) << 6);
1214 		if ((*str & 0xc0) != 0x80) return 2;
1215 		c += (*str++ & 0x3f);
1216 		*out_char = c;
1217 		return 2;
1218 	}
1219 	if ((*str & 0xf0) == 0xe0)
1220 	{
1221 		*out_char = 0xFFFD;  // will be invalid but not end of string
1222 		if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1223 		if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1224 		if (*str == 0xed && str[1] > 0x9f) return 3;  // str[1] < 0x80 is checked below
1225 		c = (unsigned int)((*str++ & 0x0f) << 12);
1226 		if ((*str & 0xc0) != 0x80) return 3;
1227 		c += (unsigned int)((*str++ & 0x3f) << 6);
1228 		if ((*str & 0xc0) != 0x80) return 3;
1229 		c += (*str++ & 0x3f);
1230 		*out_char = c;
1231 		return 3;
1232 	}
1233 	if ((*str & 0xf8) == 0xf0)
1234 	{
1235 		*out_char = 0xFFFD;  // will be invalid but not end of string
1236 		if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1237 		if (*str > 0xf4) return 4;
1238 		if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1239 		if (*str == 0xf4 && str[1] > 0x8f) return 4;  // str[1] < 0x80 is checked below
1240 		c = (unsigned int)((*str++ & 0x07) << 18);
1241 		if ((*str & 0xc0) != 0x80) return 4;
1242 		c += (unsigned int)((*str++ & 0x3f) << 12);
1243 		if ((*str & 0xc0) != 0x80) return 4;
1244 		c += (unsigned int)((*str++ & 0x3f) << 6);
1245 		if ((*str & 0xc0) != 0x80) return 4;
1246 		c += (*str++ & 0x3f);
1247 		// utf-8 encodings of values used in surrogate pairs are invalid
1248 		if ((c & 0xFFFFF800) == 0xD800) return 4;
1249 		*out_char = c;
1250 		return 4;
1251 	}
1252 	*out_char = 0;
1253 	return 0;
1254 }
1255 
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1256 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1257 {
1258 	ImWchar* buf_out = buf;
1259 	ImWchar* buf_end = buf + buf_size;
1260 	while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1261 	{
1262 		unsigned int c;
1263 		in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1264 		if (c == 0)
1265 			break;
1266 		if (c < 0x10000)  // FIXME: Losing characters that don't fit in 2 bytes
1267 			*buf_out++ = (ImWchar)c;
1268 	}
1269 	*buf_out = 0;
1270 	if (in_text_remaining)
1271 		*in_text_remaining = in_text;
1272 	return (int)(buf_out - buf);
1273 }
1274 
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1275 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1276 {
1277 	int char_count = 0;
1278 	while ((!in_text_end || in_text < in_text_end) && *in_text)
1279 	{
1280 		unsigned int c;
1281 		in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1282 		if (c == 0)
1283 			break;
1284 		if (c < 0x10000)
1285 			char_count++;
1286 	}
1287 	return char_count;
1288 }
1289 
1290 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1291 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1292 {
1293 	if (c < 0x80)
1294 	{
1295 		buf[0] = (char)c;
1296 		return 1;
1297 	}
1298 	if (c < 0x800)
1299 	{
1300 		if (buf_size < 2) return 0;
1301 		buf[0] = (char)(0xc0 + (c >> 6));
1302 		buf[1] = (char)(0x80 + (c & 0x3f));
1303 		return 2;
1304 	}
1305 	if (c >= 0xdc00 && c < 0xe000)
1306 	{
1307 		return 0;
1308 	}
1309 	if (c >= 0xd800 && c < 0xdc00)
1310 	{
1311 		if (buf_size < 4) return 0;
1312 		buf[0] = (char)(0xf0 + (c >> 18));
1313 		buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1314 		buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1315 		buf[3] = (char)(0x80 + ((c)&0x3f));
1316 		return 4;
1317 	}
1318 	//else if (c < 0x10000)
1319 	{
1320 		if (buf_size < 3) return 0;
1321 		buf[0] = (char)(0xe0 + (c >> 12));
1322 		buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
1323 		buf[2] = (char)(0x80 + ((c)&0x3f));
1324 		return 3;
1325 	}
1326 }
1327 
ImTextCountUtf8BytesFromChar(unsigned int c)1328 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1329 {
1330 	if (c < 0x80) return 1;
1331 	if (c < 0x800) return 2;
1332 	if (c >= 0xdc00 && c < 0xe000) return 0;
1333 	if (c >= 0xd800 && c < 0xdc00) return 4;
1334 	return 3;
1335 }
1336 
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1337 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1338 {
1339 	char* buf_out = buf;
1340 	const char* buf_end = buf + buf_size;
1341 	while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1342 	{
1343 		unsigned int c = (unsigned int)(*in_text++);
1344 		if (c < 0x80)
1345 			*buf_out++ = (char)c;
1346 		else
1347 			buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end - buf_out - 1), c);
1348 	}
1349 	*buf_out = 0;
1350 	return (int)(buf_out - buf);
1351 }
1352 
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1353 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1354 {
1355 	int bytes_count = 0;
1356 	while ((!in_text_end || in_text < in_text_end) && *in_text)
1357 	{
1358 		unsigned int c = (unsigned int)(*in_text++);
1359 		if (c < 0x80)
1360 			bytes_count++;
1361 		else
1362 			bytes_count += ImTextCountUtf8BytesFromChar(c);
1363 	}
1364 	return bytes_count;
1365 }
1366 
ColorConvertU32ToFloat4(ImU32 in)1367 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1368 {
1369 	float s = 1.0f / 255.0f;
1370 	return ImVec4(
1371 		((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1372 		((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1373 		((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1374 		((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1375 }
1376 
ColorConvertFloat4ToU32(const ImVec4 & in)1377 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1378 {
1379 	ImU32 out;
1380 	out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1381 	out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1382 	out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1383 	out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1384 	return out;
1385 }
1386 
GetColorU32(ImGuiCol idx,float alpha_mul)1387 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1388 {
1389 	ImGuiStyle& style = GImGui->Style;
1390 	ImVec4 c = style.Colors[idx];
1391 	c.w *= style.Alpha * alpha_mul;
1392 	return ColorConvertFloat4ToU32(c);
1393 }
1394 
GetColorU32(const ImVec4 & col)1395 ImU32 ImGui::GetColorU32(const ImVec4& col)
1396 {
1397 	ImGuiStyle& style = GImGui->Style;
1398 	ImVec4 c = col;
1399 	c.w *= style.Alpha;
1400 	return ColorConvertFloat4ToU32(c);
1401 }
1402 
GetStyleColorVec4(ImGuiCol idx)1403 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
1404 {
1405 	ImGuiStyle& style = GImGui->Style;
1406 	return style.Colors[idx];
1407 }
1408 
GetColorU32(ImU32 col)1409 ImU32 ImGui::GetColorU32(ImU32 col)
1410 {
1411 	float style_alpha = GImGui->Style.Alpha;
1412 	if (style_alpha >= 1.0f)
1413 		return col;
1414 	int a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
1415 	a = (int)(a * style_alpha);  // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
1416 	return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
1417 }
1418 
1419 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1420 // 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)1421 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1422 {
1423 	float K = 0.f;
1424 	if (g < b)
1425 	{
1426 		ImSwap(g, b);
1427 		K = -1.f;
1428 	}
1429 	if (r < g)
1430 	{
1431 		ImSwap(r, g);
1432 		K = -2.f / 6.f - K;
1433 	}
1434 
1435 	const float chroma = r - (g < b ? g : b);
1436 	out_h = fabsf(K + (g - b) / (6.f * chroma + 1e-20f));
1437 	out_s = chroma / (r + 1e-20f);
1438 	out_v = r;
1439 }
1440 
1441 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1442 // 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)1443 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1444 {
1445 	if (s == 0.0f)
1446 	{
1447 		// gray
1448 		out_r = out_g = out_b = v;
1449 		return;
1450 	}
1451 
1452 	h = fmodf(h, 1.0f) / (60.0f / 360.0f);
1453 	int i = (int)h;
1454 	float f = h - (float)i;
1455 	float p = v * (1.0f - s);
1456 	float q = v * (1.0f - s * f);
1457 	float t = v * (1.0f - s * (1.0f - f));
1458 
1459 	switch (i)
1460 	{
1461 		case 0:
1462 			out_r = v;
1463 			out_g = t;
1464 			out_b = p;
1465 			break;
1466 		case 1:
1467 			out_r = q;
1468 			out_g = v;
1469 			out_b = p;
1470 			break;
1471 		case 2:
1472 			out_r = p;
1473 			out_g = v;
1474 			out_b = t;
1475 			break;
1476 		case 3:
1477 			out_r = p;
1478 			out_g = q;
1479 			out_b = v;
1480 			break;
1481 		case 4:
1482 			out_r = t;
1483 			out_g = p;
1484 			out_b = v;
1485 			break;
1486 		case 5:
1487 		default:
1488 			out_r = v;
1489 			out_g = p;
1490 			out_b = q;
1491 			break;
1492 	}
1493 }
1494 
ImFileOpen(const char * filename,const char * mode)1495 FILE* ImFileOpen(const char* filename, const char* mode)
1496 {
1497 #if defined(_WIN32) && !defined(__CYGWIN__)
1498 	// 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)
1499 	const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1500 	const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1501 	ImVector<ImWchar> buf;
1502 	buf.resize(filename_wsize + mode_wsize);
1503 	ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1504 	ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1505 	return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1506 #else
1507 	return fopen(filename, mode);
1508 #endif
1509 }
1510 
1511 // Load file content into memory
1512 // 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)1513 void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, int* out_file_size, int padding_bytes)
1514 {
1515 	IM_ASSERT(filename && file_open_mode);
1516 	if (out_file_size)
1517 		*out_file_size = 0;
1518 
1519 	FILE* f;
1520 	if ((f = ImFileOpen(filename, file_open_mode)) == NULL)
1521 		return NULL;
1522 
1523 	long file_size_signed;
1524 	if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
1525 	{
1526 		fclose(f);
1527 		return NULL;
1528 	}
1529 
1530 	int file_size = (int)file_size_signed;
1531 	void* file_data = ImGui::MemAlloc(file_size + padding_bytes);
1532 	if (file_data == NULL)
1533 	{
1534 		fclose(f);
1535 		return NULL;
1536 	}
1537 	if (fread(file_data, 1, (size_t)file_size, f) != (size_t)file_size)
1538 	{
1539 		fclose(f);
1540 		ImGui::MemFree(file_data);
1541 		return NULL;
1542 	}
1543 	if (padding_bytes > 0)
1544 		memset((void*)(((char*)file_data) + file_size), 0, padding_bytes);
1545 
1546 	fclose(f);
1547 	if (out_file_size)
1548 		*out_file_size = file_size;
1549 
1550 	return file_data;
1551 }
1552 
1553 //-----------------------------------------------------------------------------
1554 // ImGuiStorage
1555 // Helper: Key->value storage
1556 //-----------------------------------------------------------------------------
1557 
1558 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::Pair> & data,ImGuiID key)1559 static ImVector<ImGuiStorage::Pair>::iterator LowerBound(ImVector<ImGuiStorage::Pair>& data, ImGuiID key)
1560 {
1561 	ImVector<ImGuiStorage::Pair>::iterator first = data.begin();
1562 	ImVector<ImGuiStorage::Pair>::iterator last = data.end();
1563 	size_t count = (size_t)(last - first);
1564 	while (count > 0)
1565 	{
1566 		size_t count2 = count >> 1;
1567 		ImVector<ImGuiStorage::Pair>::iterator mid = first + count2;
1568 		if (mid->key < key)
1569 		{
1570 			first = ++mid;
1571 			count -= count2 + 1;
1572 		}
1573 		else
1574 		{
1575 			count = count2;
1576 		}
1577 	}
1578 	return first;
1579 }
1580 
1581 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1582 void ImGuiStorage::BuildSortByKey()
1583 {
1584 	struct StaticFunc
1585 	{
1586 		static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1587 		{
1588 			// We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1589 			if (((const Pair*)lhs)->key > ((const Pair*)rhs)->key) return +1;
1590 			if (((const Pair*)lhs)->key < ((const Pair*)rhs)->key) return -1;
1591 			return 0;
1592 		}
1593 	};
1594 	if (Data.Size > 1)
1595 		qsort(Data.Data, (size_t)Data.Size, sizeof(Pair), StaticFunc::PairCompareByID);
1596 }
1597 
GetInt(ImGuiID key,int default_val) const1598 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1599 {
1600 	ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1601 	if (it == Data.end() || it->key != key)
1602 		return default_val;
1603 	return it->val_i;
1604 }
1605 
GetBool(ImGuiID key,bool default_val) const1606 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1607 {
1608 	return GetInt(key, default_val ? 1 : 0) != 0;
1609 }
1610 
GetFloat(ImGuiID key,float default_val) const1611 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1612 {
1613 	ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1614 	if (it == Data.end() || it->key != key)
1615 		return default_val;
1616 	return it->val_f;
1617 }
1618 
GetVoidPtr(ImGuiID key) const1619 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1620 {
1621 	ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1622 	if (it == Data.end() || it->key != key)
1623 		return NULL;
1624 	return it->val_p;
1625 }
1626 
1627 // 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)1628 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1629 {
1630 	ImVector<Pair>::iterator it = LowerBound(Data, key);
1631 	if (it == Data.end() || it->key != key)
1632 		it = Data.insert(it, Pair(key, default_val));
1633 	return &it->val_i;
1634 }
1635 
GetBoolRef(ImGuiID key,bool default_val)1636 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1637 {
1638 	return (bool*)GetIntRef(key, default_val ? 1 : 0);
1639 }
1640 
GetFloatRef(ImGuiID key,float default_val)1641 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1642 {
1643 	ImVector<Pair>::iterator it = LowerBound(Data, key);
1644 	if (it == Data.end() || it->key != key)
1645 		it = Data.insert(it, Pair(key, default_val));
1646 	return &it->val_f;
1647 }
1648 
GetVoidPtrRef(ImGuiID key,void * default_val)1649 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1650 {
1651 	ImVector<Pair>::iterator it = LowerBound(Data, key);
1652 	if (it == Data.end() || it->key != key)
1653 		it = Data.insert(it, Pair(key, default_val));
1654 	return &it->val_p;
1655 }
1656 
1657 // 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)1658 void ImGuiStorage::SetInt(ImGuiID key, int val)
1659 {
1660 	ImVector<Pair>::iterator it = LowerBound(Data, key);
1661 	if (it == Data.end() || it->key != key)
1662 	{
1663 		Data.insert(it, Pair(key, val));
1664 		return;
1665 	}
1666 	it->val_i = val;
1667 }
1668 
SetBool(ImGuiID key,bool val)1669 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1670 {
1671 	SetInt(key, val ? 1 : 0);
1672 }
1673 
SetFloat(ImGuiID key,float val)1674 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1675 {
1676 	ImVector<Pair>::iterator it = LowerBound(Data, key);
1677 	if (it == Data.end() || it->key != key)
1678 	{
1679 		Data.insert(it, Pair(key, val));
1680 		return;
1681 	}
1682 	it->val_f = val;
1683 }
1684 
SetVoidPtr(ImGuiID key,void * val)1685 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1686 {
1687 	ImVector<Pair>::iterator it = LowerBound(Data, key);
1688 	if (it == Data.end() || it->key != key)
1689 	{
1690 		Data.insert(it, Pair(key, val));
1691 		return;
1692 	}
1693 	it->val_p = val;
1694 }
1695 
SetAllInt(int v)1696 void ImGuiStorage::SetAllInt(int v)
1697 {
1698 	for (int i = 0; i < Data.Size; i++)
1699 		Data[i].val_i = v;
1700 }
1701 
1702 //-----------------------------------------------------------------------------
1703 // ImGuiTextFilter
1704 //-----------------------------------------------------------------------------
1705 
1706 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1707 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1708 {
1709 	if (default_filter)
1710 	{
1711 		ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1712 		Build();
1713 	}
1714 	else
1715 	{
1716 		InputBuf[0] = 0;
1717 		CountGrep = 0;
1718 	}
1719 }
1720 
Draw(const char * label,float width)1721 bool ImGuiTextFilter::Draw(const char* label, float width)
1722 {
1723 	if (width != 0.0f)
1724 		ImGui::PushItemWidth(width);
1725 	bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1726 	if (width != 0.0f)
1727 		ImGui::PopItemWidth();
1728 	if (value_changed)
1729 		Build();
1730 	return value_changed;
1731 }
1732 
split(char separator,ImVector<TextRange> & out)1733 void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>& out)
1734 {
1735 	out.resize(0);
1736 	const char* wb = b;
1737 	const char* we = wb;
1738 	while (we < e)
1739 	{
1740 		if (*we == separator)
1741 		{
1742 			out.push_back(TextRange(wb, we));
1743 			wb = we + 1;
1744 		}
1745 		we++;
1746 	}
1747 	if (wb != we)
1748 		out.push_back(TextRange(wb, we));
1749 }
1750 
Build()1751 void ImGuiTextFilter::Build()
1752 {
1753 	Filters.resize(0);
1754 	TextRange input_range(InputBuf, InputBuf + strlen(InputBuf));
1755 	input_range.split(',', Filters);
1756 
1757 	CountGrep = 0;
1758 	for (int i = 0; i != Filters.Size; i++)
1759 	{
1760 		Filters[i].trim_blanks();
1761 		if (Filters[i].empty())
1762 			continue;
1763 		if (Filters[i].front() != '-')
1764 			CountGrep += 1;
1765 	}
1766 }
1767 
PassFilter(const char * text,const char * text_end) const1768 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
1769 {
1770 	if (Filters.empty())
1771 		return true;
1772 
1773 	if (text == NULL)
1774 		text = "";
1775 
1776 	for (int i = 0; i != Filters.Size; i++)
1777 	{
1778 		const TextRange& f = Filters[i];
1779 		if (f.empty())
1780 			continue;
1781 		if (f.front() == '-')
1782 		{
1783 			// Subtract
1784 			if (ImStristr(text, text_end, f.begin() + 1, f.end()) != NULL)
1785 				return false;
1786 		}
1787 		else
1788 		{
1789 			// Grep
1790 			if (ImStristr(text, text_end, f.begin(), f.end()) != NULL)
1791 				return true;
1792 		}
1793 	}
1794 
1795 	// Implicit * grep
1796 	if (CountGrep == 0)
1797 		return true;
1798 
1799 	return false;
1800 }
1801 
1802 //-----------------------------------------------------------------------------
1803 // ImGuiTextBuffer
1804 //-----------------------------------------------------------------------------
1805 
1806 // On some platform vsnprintf() takes va_list by reference and modifies it.
1807 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1808 #ifndef va_copy
1809 #define va_copy(dest, src) (dest = src)
1810 #endif
1811 
1812 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)1813 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
1814 {
1815 	va_list args_copy;
1816 	va_copy(args_copy, args);
1817 
1818 	int len = ImFormatStringV(NULL, 0, fmt, args);  // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
1819 	if (len <= 0)
1820 		return;
1821 
1822 	const int write_off = Buf.Size;
1823 	const int needed_sz = write_off + len;
1824 	if (write_off + len >= Buf.Capacity)
1825 	{
1826 		int double_capacity = Buf.Capacity * 2;
1827 		Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity);
1828 	}
1829 
1830 	Buf.resize(needed_sz);
1831 	ImFormatStringV(&Buf[write_off - 1], len + 1, fmt, args_copy);
1832 }
1833 
appendf(const char * fmt,...)1834 void ImGuiTextBuffer::appendf(const char* fmt, ...)
1835 {
1836 	va_list args;
1837 	va_start(args, fmt);
1838 	appendfv(fmt, args);
1839 	va_end(args);
1840 }
1841 
1842 //-----------------------------------------------------------------------------
1843 // ImGuiSimpleColumns (internal use only)
1844 //-----------------------------------------------------------------------------
1845 
ImGuiMenuColumns()1846 ImGuiMenuColumns::ImGuiMenuColumns()
1847 {
1848 	Count = 0;
1849 	Spacing = Width = NextWidth = 0.0f;
1850 	memset(Pos, 0, sizeof(Pos));
1851 	memset(NextWidths, 0, sizeof(NextWidths));
1852 }
1853 
Update(int count,float spacing,bool clear)1854 void ImGuiMenuColumns::Update(int count, float spacing, bool clear)
1855 {
1856 	IM_ASSERT(Count <= IM_ARRAYSIZE(Pos));
1857 	Count = count;
1858 	Width = NextWidth = 0.0f;
1859 	Spacing = spacing;
1860 	if (clear) memset(NextWidths, 0, sizeof(NextWidths));
1861 	for (int i = 0; i < Count; i++)
1862 	{
1863 		if (i > 0 && NextWidths[i] > 0.0f)
1864 			Width += Spacing;
1865 		Pos[i] = (float)(int)Width;
1866 		Width += NextWidths[i];
1867 		NextWidths[i] = 0.0f;
1868 	}
1869 }
1870 
DeclColumns(float w0,float w1,float w2)1871 float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2)  // not using va_arg because they promote float to double
1872 {
1873 	NextWidth = 0.0f;
1874 	NextWidths[0] = ImMax(NextWidths[0], w0);
1875 	NextWidths[1] = ImMax(NextWidths[1], w1);
1876 	NextWidths[2] = ImMax(NextWidths[2], w2);
1877 	for (int i = 0; i < 3; i++)
1878 		NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f);
1879 	return ImMax(Width, NextWidth);
1880 }
1881 
CalcExtraSpace(float avail_w)1882 float ImGuiMenuColumns::CalcExtraSpace(float avail_w)
1883 {
1884 	return ImMax(0.0f, avail_w - Width);
1885 }
1886 
1887 //-----------------------------------------------------------------------------
1888 // ImGuiListClipper
1889 //-----------------------------------------------------------------------------
1890 
SetCursorPosYAndSetupDummyPrevLine(float pos_y,float line_height)1891 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
1892 {
1893 	// Set cursor position and a few other things so that SetScrollHere() and Columns() can work when seeking cursor.
1894 	// FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. Consider moving within SetCursorXXX functions?
1895 	ImGui::SetCursorPosY(pos_y);
1896 	ImGuiWindow* window = ImGui::GetCurrentWindow();
1897 	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.
1898 	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.
1899 	if (window->DC.ColumnsSet)
1900 		window->DC.ColumnsSet->CellMinY = window->DC.CursorPos.y;  // Setting this so that cell Y position are set properly
1901 }
1902 
1903 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
1904 // Use case B: Begin() called from constructor with items_height>0
1905 // 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)1906 void ImGuiListClipper::Begin(int count, float items_height)
1907 {
1908 	StartPosY = ImGui::GetCursorPosY();
1909 	ItemsHeight = items_height;
1910 	ItemsCount = count;
1911 	StepNo = 0;
1912 	DisplayEnd = DisplayStart = -1;
1913 	if (ItemsHeight > 0.0f)
1914 	{
1915 		ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd);  // calculate how many to clip/display
1916 		if (DisplayStart > 0)
1917 			SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight);  // advance cursor
1918 		StepNo = 2;
1919 	}
1920 }
1921 
End()1922 void ImGuiListClipper::End()
1923 {
1924 	if (ItemsCount < 0)
1925 		return;
1926 	// 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.
1927 	if (ItemsCount < INT_MAX)
1928 		SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight);  // advance cursor
1929 	ItemsCount = -1;
1930 	StepNo = 3;
1931 }
1932 
Step()1933 bool ImGuiListClipper::Step()
1934 {
1935 	if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems)
1936 	{
1937 		ItemsCount = -1;
1938 		return false;
1939 	}
1940 	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.
1941 	{
1942 		DisplayStart = 0;
1943 		DisplayEnd = 1;
1944 		StartPosY = ImGui::GetCursorPosY();
1945 		StepNo = 1;
1946 		return true;
1947 	}
1948 	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.
1949 	{
1950 		if (ItemsCount == 1)
1951 		{
1952 			ItemsCount = -1;
1953 			return false;
1954 		}
1955 		float items_height = ImGui::GetCursorPosY() - StartPosY;
1956 		IM_ASSERT(items_height > 0.0f);  // If this triggers, it means Item 0 hasn't moved the cursor vertically
1957 		Begin(ItemsCount - 1, items_height);
1958 		DisplayStart++;
1959 		DisplayEnd++;
1960 		StepNo = 3;
1961 		return true;
1962 	}
1963 	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.
1964 	{
1965 		IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
1966 		StepNo = 3;
1967 		return true;
1968 	}
1969 	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.
1970 		End();
1971 	return false;
1972 }
1973 
1974 //-----------------------------------------------------------------------------
1975 // ImGuiWindow
1976 //-----------------------------------------------------------------------------
1977 
ImGuiWindow(ImGuiContext * context,const char * name)1978 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
1979 {
1980 	Name = ImStrdup(name);
1981 	ID = ImHash(name, 0);
1982 	IDStack.push_back(ID);
1983 	Flags = 0;
1984 	PosFloat = Pos = ImVec2(0.0f, 0.0f);
1985 	Size = SizeFull = ImVec2(0.0f, 0.0f);
1986 	SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
1987 	WindowPadding = ImVec2(0.0f, 0.0f);
1988 	WindowRounding = 0.0f;
1989 	WindowBorderSize = 0.0f;
1990 	MoveId = GetID("#MOVE");
1991 	ChildId = 0;
1992 	Scroll = ImVec2(0.0f, 0.0f);
1993 	ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
1994 	ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
1995 	ScrollbarX = ScrollbarY = false;
1996 	ScrollbarSizes = ImVec2(0.0f, 0.0f);
1997 	Active = WasActive = false;
1998 	WriteAccessed = false;
1999 	Collapsed = false;
2000 	CollapseToggleWanted = false;
2001 	SkipItems = false;
2002 	Appearing = false;
2003 	CloseButton = false;
2004 	BeginOrderWithinParent = -1;
2005 	BeginOrderWithinContext = -1;
2006 	BeginCount = 0;
2007 	PopupId = 0;
2008 	AutoFitFramesX = AutoFitFramesY = -1;
2009 	AutoFitOnlyGrows = false;
2010 	AutoFitChildAxises = 0x00;
2011 	AutoPosLastDirection = ImGuiDir_None;
2012 	HiddenFrames = 0;
2013 	SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2014 	SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2015 
2016 	LastFrameActive = -1;
2017 	ItemWidthDefault = 0.0f;
2018 	FontWindowScale = 1.0f;
2019 
2020 	DrawList = IM_NEW(ImDrawList)(&context->DrawListSharedData);
2021 	DrawList->_OwnerName = Name;
2022 	ParentWindow = NULL;
2023 	RootWindow = NULL;
2024 	RootWindowForTitleBarHighlight = NULL;
2025 	RootWindowForTabbing = NULL;
2026 	RootWindowForNav = NULL;
2027 
2028 	NavLastIds[0] = NavLastIds[1] = 0;
2029 	NavRectRel[0] = NavRectRel[1] = ImRect();
2030 	NavLastChildNavWindow = NULL;
2031 
2032 	FocusIdxAllCounter = FocusIdxTabCounter = -1;
2033 	FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX;
2034 	FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX;
2035 }
2036 
~ImGuiWindow()2037 ImGuiWindow::~ImGuiWindow()
2038 {
2039 	IM_DELETE(DrawList);
2040 	IM_DELETE(Name);
2041 	for (int i = 0; i != ColumnsStorage.Size; i++)
2042 		ColumnsStorage[i].~ImGuiColumnsSet();
2043 }
2044 
GetID(const char * str,const char * str_end)2045 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2046 {
2047 	ImGuiID seed = IDStack.back();
2048 	ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
2049 	ImGui::KeepAliveID(id);
2050 	return id;
2051 }
2052 
GetID(const void * ptr)2053 ImGuiID ImGuiWindow::GetID(const void* ptr)
2054 {
2055 	ImGuiID seed = IDStack.back();
2056 	ImGuiID id = ImHash(&ptr, sizeof(void*), seed);
2057 	ImGui::KeepAliveID(id);
2058 	return id;
2059 }
2060 
GetIDNoKeepAlive(const char * str,const char * str_end)2061 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2062 {
2063 	ImGuiID seed = IDStack.back();
2064 	return ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
2065 }
2066 
2067 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2068 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2069 {
2070 	ImGuiID seed = IDStack.back();
2071 	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)};
2072 	ImGuiID id = ImHash(&r_rel, sizeof(r_rel), seed);
2073 	ImGui::KeepAliveID(id);
2074 	return id;
2075 }
2076 
2077 //-----------------------------------------------------------------------------
2078 // Internal API exposed in imgui_internal.h
2079 //-----------------------------------------------------------------------------
2080 
SetCurrentWindow(ImGuiWindow * window)2081 static void SetCurrentWindow(ImGuiWindow* window)
2082 {
2083 	ImGuiContext& g = *GImGui;
2084 	g.CurrentWindow = window;
2085 	if (window)
2086 		g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2087 }
2088 
SetNavID(ImGuiID id,int nav_layer)2089 static void SetNavID(ImGuiID id, int nav_layer)
2090 {
2091 	ImGuiContext& g = *GImGui;
2092 	IM_ASSERT(g.NavWindow);
2093 	IM_ASSERT(nav_layer == 0 || nav_layer == 1);
2094 	g.NavId = id;
2095 	g.NavWindow->NavLastIds[nav_layer] = id;
2096 }
2097 
SetNavIDAndMoveMouse(ImGuiID id,int nav_layer,const ImRect & rect_rel)2098 static void SetNavIDAndMoveMouse(ImGuiID id, int nav_layer, const ImRect& rect_rel)
2099 {
2100 	ImGuiContext& g = *GImGui;
2101 	SetNavID(id, nav_layer);
2102 	g.NavWindow->NavRectRel[nav_layer] = rect_rel;
2103 	g.NavMousePosDirty = true;
2104 	g.NavDisableHighlight = false;
2105 	g.NavDisableMouseHover = true;
2106 }
2107 
SetActiveID(ImGuiID id,ImGuiWindow * window)2108 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2109 {
2110 	ImGuiContext& g = *GImGui;
2111 	g.ActiveIdIsJustActivated = (g.ActiveId != id);
2112 	if (g.ActiveIdIsJustActivated)
2113 		g.ActiveIdTimer = 0.0f;
2114 	g.ActiveId = id;
2115 	g.ActiveIdAllowNavDirFlags = 0;
2116 	g.ActiveIdAllowOverlap = false;
2117 	g.ActiveIdWindow = window;
2118 	if (id)
2119 	{
2120 		g.ActiveIdIsAlive = true;
2121 		g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
2122 	}
2123 }
2124 
GetActiveID()2125 ImGuiID ImGui::GetActiveID()
2126 {
2127 	ImGuiContext& g = *GImGui;
2128 	return g.ActiveId;
2129 }
2130 
SetFocusID(ImGuiID id,ImGuiWindow * window)2131 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
2132 {
2133 	ImGuiContext& g = *GImGui;
2134 	IM_ASSERT(id != 0);
2135 
2136 	// Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it.
2137 	const int nav_layer = window->DC.NavLayerCurrent;
2138 	if (g.NavWindow != window)
2139 		g.NavInitRequest = false;
2140 	g.NavId = id;
2141 	g.NavWindow = window;
2142 	g.NavLayer = nav_layer;
2143 	window->NavLastIds[nav_layer] = id;
2144 	if (window->DC.LastItemId == id)
2145 		window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
2146 
2147 	if (g.ActiveIdSource == ImGuiInputSource_Nav)
2148 		g.NavDisableMouseHover = true;
2149 	else
2150 		g.NavDisableHighlight = true;
2151 }
2152 
ClearActiveID()2153 void ImGui::ClearActiveID()
2154 {
2155 	SetActiveID(0, NULL);
2156 }
2157 
SetHoveredID(ImGuiID id)2158 void ImGui::SetHoveredID(ImGuiID id)
2159 {
2160 	ImGuiContext& g = *GImGui;
2161 	g.HoveredId = id;
2162 	g.HoveredIdAllowOverlap = false;
2163 	g.HoveredIdTimer = (id != 0 && g.HoveredIdPreviousFrame == id) ? (g.HoveredIdTimer + g.IO.DeltaTime) : 0.0f;
2164 }
2165 
GetHoveredID()2166 ImGuiID ImGui::GetHoveredID()
2167 {
2168 	ImGuiContext& g = *GImGui;
2169 	return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
2170 }
2171 
KeepAliveID(ImGuiID id)2172 void ImGui::KeepAliveID(ImGuiID id)
2173 {
2174 	ImGuiContext& g = *GImGui;
2175 	if (g.ActiveId == id)
2176 		g.ActiveIdIsAlive = true;
2177 }
2178 
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)2179 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
2180 {
2181 	// An active popup disable hovering on other windows (apart from its own children)
2182 	// FIXME-OPT: This could be cached/stored within the window.
2183 	ImGuiContext& g = *GImGui;
2184 	if (g.NavWindow)
2185 		if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
2186 			if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
2187 			{
2188 				// For the purpose of those flags we differentiate "standard popup" from "modal popup"
2189 				// NB: The order of those two tests is important because Modal windows are also Popups.
2190 				if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
2191 					return false;
2192 				if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
2193 					return false;
2194 			}
2195 
2196 	return true;
2197 }
2198 
2199 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_offset_y)2200 void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
2201 {
2202 	ImGuiContext& g = *GImGui;
2203 	ImGuiWindow* window = g.CurrentWindow;
2204 	if (window->SkipItems)
2205 		return;
2206 
2207 	// Always align ourselves on pixel boundaries
2208 	const float line_height = ImMax(window->DC.CurrentLineHeight, size.y);
2209 	const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y);
2210 	//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]
2211 	window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
2212 	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));
2213 	window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
2214 	window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
2215 	//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
2216 
2217 	window->DC.PrevLineHeight = line_height;
2218 	window->DC.PrevLineTextBaseOffset = text_base_offset;
2219 	window->DC.CurrentLineHeight = window->DC.CurrentLineTextBaseOffset = 0.0f;
2220 
2221 	// Horizontal layout mode
2222 	if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
2223 		SameLine();
2224 }
2225 
ItemSize(const ImRect & bb,float text_offset_y)2226 void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
2227 {
2228 	ItemSize(bb.GetSize(), text_offset_y);
2229 }
2230 
NavScoreItemGetQuadrant(float dx,float dy)2231 static ImGuiDir NavScoreItemGetQuadrant(float dx, float dy)
2232 {
2233 	if (fabsf(dx) > fabsf(dy))
2234 		return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
2235 	return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
2236 }
2237 
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)2238 static float NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
2239 {
2240 	if (a1 < b0)
2241 		return a1 - b0;
2242 	if (b1 < a0)
2243 		return a0 - b1;
2244 	return 0.0f;
2245 }
2246 
2247 // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)2248 static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
2249 {
2250 	ImGuiContext& g = *GImGui;
2251 	ImGuiWindow* window = g.CurrentWindow;
2252 	if (g.NavLayer != window->DC.NavLayerCurrent)
2253 		return false;
2254 
2255 	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)
2256 	g.NavScoringCount++;
2257 
2258 	// 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)
2259 	if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
2260 	{
2261 		cand.Min.y = ImClamp(cand.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y);
2262 		cand.Max.y = ImClamp(cand.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y);
2263 	}
2264 	else
2265 	{
2266 		cand.Min.x = ImClamp(cand.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
2267 		cand.Max.x = ImClamp(cand.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
2268 	}
2269 
2270 	// Compute distance between boxes
2271 	// FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
2272 	float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
2273 	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
2274 	if (dby != 0.0f && dbx != 0.0f)
2275 		dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
2276 	float dist_box = fabsf(dbx) + fabsf(dby);
2277 
2278 	// 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)
2279 	float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
2280 	float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
2281 	float dist_center = fabsf(dcx) + fabsf(dcy);  // L1 metric (need this for our connectedness guarantee)
2282 
2283 	// Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
2284 	ImGuiDir quadrant;
2285 	float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
2286 	if (dbx != 0.0f || dby != 0.0f)
2287 	{
2288 		// For non-overlapping boxes, use distance between boxes
2289 		dax = dbx;
2290 		day = dby;
2291 		dist_axial = dist_box;
2292 		quadrant = NavScoreItemGetQuadrant(dbx, dby);
2293 	}
2294 	else if (dcx != 0.0f || dcy != 0.0f)
2295 	{
2296 		// For overlapping boxes with different centers, use distance between centers
2297 		dax = dcx;
2298 		day = dcy;
2299 		dist_axial = dist_center;
2300 		quadrant = NavScoreItemGetQuadrant(dcx, dcy);
2301 	}
2302 	else
2303 	{
2304 		// 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)
2305 		quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
2306 	}
2307 
2308 #if IMGUI_DEBUG_NAV_SCORING
2309 	char buf[128];
2310 	if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max))
2311 	{
2312 		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]);
2313 		g.OverlayDrawList.AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100));
2314 		g.OverlayDrawList.AddRect(cand.Min, cand.Max, IM_COL32(255, 255, 0, 200));
2315 		g.OverlayDrawList.AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + ImGui::CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40, 0, 0, 150));
2316 		g.OverlayDrawList.AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
2317 	}
2318 	else if (g.IO.KeyCtrl)  // Hold to preview score in matching quadrant. Press C to rotate.
2319 	{
2320 		if (IsKeyPressedMap(ImGuiKey_C))
2321 		{
2322 			g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3);
2323 			g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f;
2324 		}
2325 		if (quadrant == g.NavMoveDir)
2326 		{
2327 			ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
2328 			g.OverlayDrawList.AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
2329 			g.OverlayDrawList.AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
2330 		}
2331 	}
2332 #endif
2333 
2334 	// Is it in the quadrant we're interesting in moving to?
2335 	bool new_best = false;
2336 	if (quadrant == g.NavMoveDir)
2337 	{
2338 		// Does it beat the current best candidate?
2339 		if (dist_box < result->DistBox)
2340 		{
2341 			result->DistBox = dist_box;
2342 			result->DistCenter = dist_center;
2343 			return true;
2344 		}
2345 		if (dist_box == result->DistBox)
2346 		{
2347 			// Try using distance between center points to break ties
2348 			if (dist_center < result->DistCenter)
2349 			{
2350 				result->DistCenter = dist_center;
2351 				new_best = true;
2352 			}
2353 			else if (dist_center == result->DistCenter)
2354 			{
2355 				// Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
2356 				// (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),
2357 				// 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.
2358 				if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f)  // moving bj to the right/down decreases distance
2359 					new_best = true;
2360 			}
2361 		}
2362 	}
2363 
2364 	// 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
2365 	// are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
2366 	// 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.
2367 	// 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.
2368 	// Disabling it may however lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
2369 	if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match
2370 		if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
2371 			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))
2372 			{
2373 				result->DistAxial = dist_axial;
2374 				new_best = true;
2375 			}
2376 
2377 	return new_best;
2378 }
2379 
NavSaveLastChildNavWindow(ImGuiWindow * child_window)2380 static void NavSaveLastChildNavWindow(ImGuiWindow* child_window)
2381 {
2382 	ImGuiWindow* parent_window = child_window;
2383 	while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
2384 		parent_window = parent_window->ParentWindow;
2385 	if (parent_window && parent_window != child_window)
2386 		parent_window->NavLastChildNavWindow = child_window;
2387 }
2388 
2389 // Call when we are expected to land on Layer 0 after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)2390 static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window)
2391 {
2392 	return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window;
2393 }
2394 
NavRestoreLayer(int layer)2395 static void NavRestoreLayer(int layer)
2396 {
2397 	ImGuiContext& g = *GImGui;
2398 	g.NavLayer = layer;
2399 	if (layer == 0)
2400 		g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow);
2401 	if (layer == 0 && g.NavWindow->NavLastIds[0] != 0)
2402 		SetNavIDAndMoveMouse(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]);
2403 	else
2404 		ImGui::NavInitWindow(g.NavWindow, true);
2405 }
2406 
NavUpdateAnyRequestFlag()2407 static inline void NavUpdateAnyRequestFlag()
2408 {
2409 	ImGuiContext& g = *GImGui;
2410 	g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || IMGUI_DEBUG_NAV_SCORING;
2411 }
2412 
NavMoveRequestButNoResultYet()2413 static bool NavMoveRequestButNoResultYet()
2414 {
2415 	ImGuiContext& g = *GImGui;
2416 	return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
2417 }
2418 
NavMoveRequestCancel()2419 void ImGui::NavMoveRequestCancel()
2420 {
2421 	ImGuiContext& g = *GImGui;
2422 	g.NavMoveRequest = false;
2423 	NavUpdateAnyRequestFlag();
2424 }
2425 
2426 // 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)2427 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
2428 {
2429 	ImGuiContext& g = *GImGui;
2430 	//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.
2431 	//    return;
2432 
2433 	const ImGuiItemFlags item_flags = window->DC.ItemFlags;
2434 	const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
2435 	if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
2436 	{
2437 		// Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
2438 		if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
2439 		{
2440 			g.NavInitResultId = id;
2441 			g.NavInitResultRectRel = nav_bb_rel;
2442 		}
2443 		if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
2444 		{
2445 			g.NavInitRequest = false;  // Found a match, clear request
2446 			NavUpdateAnyRequestFlag();
2447 		}
2448 	}
2449 
2450 	// Scoring for navigation
2451 	if (g.NavId != id && !(item_flags & ImGuiItemFlags_NoNav))
2452 	{
2453 		ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
2454 #if IMGUI_DEBUG_NAV_SCORING
2455 		// [DEBUG] Score all items in NavWindow at all times
2456 		if (!g.NavMoveRequest)
2457 			g.NavMoveDir = g.NavMoveDirLast;
2458 		bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
2459 #else
2460 		bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
2461 #endif
2462 		if (new_best)
2463 		{
2464 			result->ID = id;
2465 			result->ParentID = window->IDStack.back();
2466 			result->Window = window;
2467 			result->RectRel = nav_bb_rel;
2468 		}
2469 	}
2470 
2471 	// Update window-relative bounding box of navigated item
2472 	if (g.NavId == id)
2473 	{
2474 		g.NavWindow = window;  // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
2475 		g.NavLayer = window->DC.NavLayerCurrent;
2476 		g.NavIdIsAlive = true;
2477 		g.NavIdTabCounter = window->FocusIdxTabCounter;
2478 		window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel;  // Store item bounding box (relative to window position)
2479 	}
2480 }
2481 
2482 // Declare item bounding box for clipping and interaction.
2483 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
2484 // 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)2485 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
2486 {
2487 	ImGuiContext& g = *GImGui;
2488 	ImGuiWindow* window = g.CurrentWindow;
2489 
2490 	if (id != 0)
2491 	{
2492 		// Navigation processing runs prior to clipping early-out
2493 		//  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
2494 		//  (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.
2495 		//      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.
2496 		//      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)
2497 		window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
2498 		if (g.NavId == id || g.NavAnyRequest)
2499 			if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
2500 				if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
2501 					NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
2502 	}
2503 
2504 	window->DC.LastItemId = id;
2505 	window->DC.LastItemRect = bb;
2506 	window->DC.LastItemStatusFlags = 0;
2507 
2508 	// Clipping test
2509 	const bool is_clipped = IsClippedEx(bb, id, false);
2510 	if (is_clipped)
2511 		return false;
2512 	//if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2513 
2514 	// We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2515 	if (IsMouseHoveringRect(bb.Min, bb.Max))
2516 		window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
2517 	return true;
2518 }
2519 
2520 // This is roughly matching the behavior of internal-facing ItemHoverable()
2521 // - 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()
2522 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)2523 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2524 {
2525 	ImGuiContext& g = *GImGui;
2526 	ImGuiWindow* window = g.CurrentWindow;
2527 	if (g.NavDisableMouseHover && !g.NavDisableHighlight)
2528 		return IsItemFocused();
2529 
2530 	// Test for bounding box overlap, as updated as ItemAdd()
2531 	if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
2532 		return false;
2533 	IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0);  // Flags not supported by this function
2534 
2535 	// Test if we are hovering the right window (our window could be behind another window)
2536 	// [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.
2537 	// 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.
2538 	//if (g.HoveredWindow != window)
2539 	//    return false;
2540 	if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2541 		return false;
2542 
2543 	// Test if another item is active (e.g. being dragged)
2544 	if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2545 		if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2546 			return false;
2547 
2548 	// Test if interactions on this window are blocked by an active popup or modal
2549 	if (!IsWindowContentHoverable(window, flags))
2550 		return false;
2551 
2552 	// Test if the item is disabled
2553 	if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2554 		return false;
2555 
2556 	// 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.
2557 	if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2558 		return false;
2559 	return true;
2560 }
2561 
2562 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)2563 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2564 {
2565 	ImGuiContext& g = *GImGui;
2566 	if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
2567 		return false;
2568 
2569 	ImGuiWindow* window = g.CurrentWindow;
2570 	if (g.HoveredWindow != window)
2571 		return false;
2572 	if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
2573 		return false;
2574 	if (!IsMouseHoveringRect(bb.Min, bb.Max))
2575 		return false;
2576 	if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_Default))
2577 		return false;
2578 	if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2579 		return false;
2580 
2581 	SetHoveredID(id);
2582 	return true;
2583 }
2584 
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)2585 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
2586 {
2587 	ImGuiContext& g = *GImGui;
2588 	ImGuiWindow* window = g.CurrentWindow;
2589 	if (!bb.Overlaps(window->ClipRect))
2590 		if (id == 0 || id != g.ActiveId)
2591 			if (clip_even_when_logged || !g.LogEnabled)
2592 				return true;
2593 	return false;
2594 }
2595 
FocusableItemRegister(ImGuiWindow * window,ImGuiID id,bool tab_stop)2596 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop)
2597 {
2598 	ImGuiContext& g = *GImGui;
2599 
2600 	const bool allow_keyboard_focus = (window->DC.ItemFlags & (ImGuiItemFlags_AllowKeyboardFocus | ImGuiItemFlags_Disabled)) == ImGuiItemFlags_AllowKeyboardFocus;
2601 	window->FocusIdxAllCounter++;
2602 	if (allow_keyboard_focus)
2603 		window->FocusIdxTabCounter++;
2604 
2605 	// Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item.
2606 	// Note that we can always TAB out of a widget that doesn't allow tabbing in.
2607 	if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab))
2608 		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.
2609 
2610 	if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
2611 		return true;
2612 	if (allow_keyboard_focus && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent)
2613 	{
2614 		g.NavJustTabbedId = id;
2615 		return true;
2616 	}
2617 
2618 	return false;
2619 }
2620 
FocusableItemUnregister(ImGuiWindow * window)2621 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
2622 {
2623 	window->FocusIdxAllCounter--;
2624 	window->FocusIdxTabCounter--;
2625 }
2626 
CalcItemSize(ImVec2 size,float default_x,float default_y)2627 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y)
2628 {
2629 	ImGuiContext& g = *GImGui;
2630 	ImVec2 content_max;
2631 	if (size.x < 0.0f || size.y < 0.0f)
2632 		content_max = g.CurrentWindow->Pos + GetContentRegionMax();
2633 	if (size.x <= 0.0f)
2634 		size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x;
2635 	if (size.y <= 0.0f)
2636 		size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y;
2637 	return size;
2638 }
2639 
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)2640 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
2641 {
2642 	if (wrap_pos_x < 0.0f)
2643 		return 0.0f;
2644 
2645 	ImGuiWindow* window = GetCurrentWindowRead();
2646 	if (wrap_pos_x == 0.0f)
2647 		wrap_pos_x = GetContentRegionMax().x + window->Pos.x;
2648 	else if (wrap_pos_x > 0.0f)
2649 		wrap_pos_x += window->Pos.x - window->Scroll.x;  // wrap_pos_x is provided is window local space
2650 
2651 	return ImMax(wrap_pos_x - pos.x, 1.0f);
2652 }
2653 
2654 //-----------------------------------------------------------------------------
2655 
MemAlloc(size_t sz)2656 void* ImGui::MemAlloc(size_t sz)
2657 {
2658 	GImAllocatorActiveAllocationsCount++;
2659 	return GImAllocatorAllocFunc(sz, GImAllocatorUserData);
2660 }
2661 
MemFree(void * ptr)2662 void ImGui::MemFree(void* ptr)
2663 {
2664 	if (ptr) GImAllocatorActiveAllocationsCount--;
2665 	return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
2666 }
2667 
GetClipboardText()2668 const char* ImGui::GetClipboardText()
2669 {
2670 	return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : "";
2671 }
2672 
SetClipboardText(const char * text)2673 void ImGui::SetClipboardText(const char* text)
2674 {
2675 	if (GImGui->IO.SetClipboardTextFn)
2676 		GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text);
2677 }
2678 
GetVersion()2679 const char* ImGui::GetVersion()
2680 {
2681 	return IMGUI_VERSION;
2682 }
2683 
2684 // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
2685 // 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()2686 ImGuiContext* ImGui::GetCurrentContext()
2687 {
2688 	return GImGui;
2689 }
2690 
SetCurrentContext(ImGuiContext * ctx)2691 void ImGui::SetCurrentContext(ImGuiContext* ctx)
2692 {
2693 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
2694 	IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx);  // For custom thread-based hackery you may want to have control over this.
2695 #else
2696 	GImGui = ctx;
2697 #endif
2698 }
2699 
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)2700 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
2701 {
2702 	GImAllocatorAllocFunc = alloc_func;
2703 	GImAllocatorFreeFunc = free_func;
2704 	GImAllocatorUserData = user_data;
2705 }
2706 
CreateContext(ImFontAtlas * shared_font_atlas)2707 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
2708 {
2709 	ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
2710 	if (GImGui == NULL)
2711 		SetCurrentContext(ctx);
2712 	Initialize(ctx);
2713 	return ctx;
2714 }
2715 
DestroyContext(ImGuiContext * ctx)2716 void ImGui::DestroyContext(ImGuiContext* ctx)
2717 {
2718 	if (ctx == NULL)
2719 		ctx = GImGui;
2720 	Shutdown(ctx);
2721 	if (GImGui == ctx)
2722 		SetCurrentContext(NULL);
2723 	IM_DELETE(ctx);
2724 }
2725 
GetIO()2726 ImGuiIO& ImGui::GetIO()
2727 {
2728 	IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2729 	return GImGui->IO;
2730 }
2731 
GetStyle()2732 ImGuiStyle& ImGui::GetStyle()
2733 {
2734 	IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2735 	return GImGui->Style;
2736 }
2737 
2738 // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
GetDrawData()2739 ImDrawData* ImGui::GetDrawData()
2740 {
2741 	ImGuiContext& g = *GImGui;
2742 	return g.DrawData.Valid ? &g.DrawData : NULL;
2743 }
2744 
GetTime()2745 float ImGui::GetTime()
2746 {
2747 	return GImGui->Time;
2748 }
2749 
GetFrameCount()2750 int ImGui::GetFrameCount()
2751 {
2752 	return GImGui->FrameCount;
2753 }
2754 
GetOverlayDrawList()2755 ImDrawList* ImGui::GetOverlayDrawList()
2756 {
2757 	return &GImGui->OverlayDrawList;
2758 }
2759 
GetDrawListSharedData()2760 ImDrawListSharedData* ImGui::GetDrawListSharedData()
2761 {
2762 	return &GImGui->DrawListSharedData;
2763 }
2764 
2765 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)2766 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
2767 {
2768 	ImGuiContext& g = *GImGui;
2769 	IM_ASSERT(window == g.NavWindow);
2770 	bool init_for_nav = false;
2771 	if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
2772 		if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
2773 			init_for_nav = true;
2774 	if (init_for_nav)
2775 	{
2776 		SetNavID(0, g.NavLayer);
2777 		g.NavInitRequest = true;
2778 		g.NavInitRequestFromMove = false;
2779 		g.NavInitResultId = 0;
2780 		g.NavInitResultRectRel = ImRect();
2781 		NavUpdateAnyRequestFlag();
2782 	}
2783 	else
2784 	{
2785 		g.NavId = window->NavLastIds[0];
2786 	}
2787 }
2788 
NavCalcPreferredMousePos()2789 static ImVec2 NavCalcPreferredMousePos()
2790 {
2791 	ImGuiContext& g = *GImGui;
2792 	ImGuiWindow* window = g.NavWindow;
2793 	if (!window)
2794 		return g.IO.MousePos;
2795 	const ImRect& rect_rel = window->NavRectRel[g.NavLayer];
2796 	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()));
2797 	ImRect visible_rect = GetViewportRect();
2798 	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.
2799 }
2800 
FindWindowIndex(ImGuiWindow * window)2801 static int FindWindowIndex(ImGuiWindow* window)  // FIXME-OPT O(N)
2802 {
2803 	ImGuiContext& g = *GImGui;
2804 	for (int i = g.Windows.Size - 1; i >= 0; i--)
2805 		if (g.Windows[i] == window)
2806 			return i;
2807 	return -1;
2808 }
2809 
FindWindowNavigable(int i_start,int i_stop,int dir)2810 static ImGuiWindow* FindWindowNavigable(int i_start, int i_stop, int dir)  // FIXME-OPT O(N)
2811 {
2812 	ImGuiContext& g = *GImGui;
2813 	for (int i = i_start; i >= 0 && i < g.Windows.Size && i != i_stop; i += dir)
2814 		if (ImGui::IsWindowNavFocusable(g.Windows[i]))
2815 			return g.Windows[i];
2816 	return NULL;
2817 }
2818 
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)2819 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
2820 {
2821 	ImGuiContext& g = *GImGui;
2822 	if (mode == ImGuiInputReadMode_Down)
2823 		return g.IO.NavInputs[n];  // Instant, read analog input (0.0f..1.0f, as provided by user)
2824 
2825 	const float t = g.IO.NavInputsDownDuration[n];
2826 	if (t < 0.0f && mode == ImGuiInputReadMode_Released)  // Return 1.0f when just released, no repeat, ignore analog input.
2827 		return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
2828 	if (t < 0.0f)
2829 		return 0.0f;
2830 	if (mode == ImGuiInputReadMode_Pressed)  // Return 1.0f when just pressed, no repeat, ignore analog input.
2831 		return (t == 0.0f) ? 1.0f : 0.0f;
2832 	if (mode == ImGuiInputReadMode_Repeat)
2833 		return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f);
2834 	if (mode == ImGuiInputReadMode_RepeatSlow)
2835 		return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f);
2836 	if (mode == ImGuiInputReadMode_RepeatFast)
2837 		return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f);
2838 	return 0.0f;
2839 }
2840 
2841 // Equivalent of IsKeyDown() for NavInputs[]
IsNavInputDown(ImGuiNavInput n)2842 static bool IsNavInputDown(ImGuiNavInput n)
2843 {
2844 	return GImGui->IO.NavInputs[n] > 0.0f;
2845 }
2846 
2847 // Equivalent of IsKeyPressed() for NavInputs[]
IsNavInputPressed(ImGuiNavInput n,ImGuiInputReadMode mode)2848 static bool IsNavInputPressed(ImGuiNavInput n, ImGuiInputReadMode mode)
2849 {
2850 	return ImGui::GetNavInputAmount(n, mode) > 0.0f;
2851 }
2852 
IsNavInputPressedAnyOfTwo(ImGuiNavInput n1,ImGuiNavInput n2,ImGuiInputReadMode mode)2853 static bool IsNavInputPressedAnyOfTwo(ImGuiNavInput n1, ImGuiNavInput n2, ImGuiInputReadMode mode)
2854 {
2855 	return (ImGui::GetNavInputAmount(n1, mode) + ImGui::GetNavInputAmount(n2, mode)) > 0.0f;
2856 }
2857 
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)2858 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
2859 {
2860 	ImVec2 delta(0.0f, 0.0f);
2861 	if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
2862 		delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode));
2863 	if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
2864 		delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode));
2865 	if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
2866 		delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
2867 	if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
2868 		delta *= slow_factor;
2869 	if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
2870 		delta *= fast_factor;
2871 	return delta;
2872 }
2873 
NavUpdateWindowingHighlightWindow(int focus_change_dir)2874 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
2875 {
2876 	ImGuiContext& g = *GImGui;
2877 	IM_ASSERT(g.NavWindowingTarget);
2878 	if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
2879 		return;
2880 
2881 	const int i_current = FindWindowIndex(g.NavWindowingTarget);
2882 	ImGuiWindow* window_target = FindWindowNavigable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
2883 	if (!window_target)
2884 		window_target = FindWindowNavigable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir);
2885 	g.NavWindowingTarget = window_target;
2886 	g.NavWindowingToggleLayer = false;
2887 }
2888 
2889 // Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer)
NavUpdateWindowing()2890 static void ImGui::NavUpdateWindowing()
2891 {
2892 	ImGuiContext& g = *GImGui;
2893 	ImGuiWindow* apply_focus_window = NULL;
2894 	bool apply_toggle_layer = false;
2895 
2896 	bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
2897 	bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard);
2898 	if (start_windowing_with_gamepad || start_windowing_with_keyboard)
2899 		if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavigable(g.Windows.Size - 1, -INT_MAX, -1))
2900 		{
2901 			g.NavWindowingTarget = window->RootWindowForTabbing;
2902 			g.NavWindowingHighlightTimer = g.NavWindowingHighlightAlpha = 0.0f;
2903 			g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
2904 			g.NavWindowingInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
2905 		}
2906 
2907 	// Gamepad update
2908 	g.NavWindowingHighlightTimer += g.IO.DeltaTime;
2909 	if (g.NavWindowingTarget && g.NavWindowingInputSource == ImGuiInputSource_NavGamepad)
2910 	{
2911 		// 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
2912 		g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.20f) / 0.05f));
2913 
2914 		// Select window to focus
2915 		const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
2916 		if (focus_change_dir != 0)
2917 		{
2918 			NavUpdateWindowingHighlightWindow(focus_change_dir);
2919 			g.NavWindowingHighlightAlpha = 1.0f;
2920 		}
2921 
2922 		// Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most)
2923 		if (!IsNavInputDown(ImGuiNavInput_Menu))
2924 		{
2925 			g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f);  // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
2926 			if (g.NavWindowingToggleLayer && g.NavWindow)
2927 				apply_toggle_layer = true;
2928 			else if (!g.NavWindowingToggleLayer)
2929 				apply_focus_window = g.NavWindowingTarget;
2930 			g.NavWindowingTarget = NULL;
2931 		}
2932 	}
2933 
2934 	// Keyboard: Focus
2935 	if (g.NavWindowingTarget && g.NavWindowingInputSource == ImGuiInputSource_NavKeyboard)
2936 	{
2937 		// Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
2938 		g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.15f) / 0.04f));  // 1.0f
2939 		if (IsKeyPressedMap(ImGuiKey_Tab, true))
2940 			NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
2941 		if (!g.IO.KeyCtrl)
2942 			apply_focus_window = g.NavWindowingTarget;
2943 	}
2944 
2945 	// Keyboard: Press and Release ALT to toggle menu layer
2946 	// 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
2947 	if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
2948 		if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
2949 			apply_toggle_layer = true;
2950 
2951 	// Move window
2952 	if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
2953 	{
2954 		ImVec2 move_delta;
2955 		if (g.NavWindowingInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
2956 			move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
2957 		if (g.NavWindowingInputSource == ImGuiInputSource_NavGamepad)
2958 			move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
2959 		if (move_delta.x != 0.0f || move_delta.y != 0.0f)
2960 		{
2961 			const float NAV_MOVE_SPEED = 800.0f;
2962 			const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
2963 			g.NavWindowingTarget->PosFloat += move_delta * move_speed;
2964 			g.NavDisableMouseHover = true;
2965 			MarkIniSettingsDirty(g.NavWindowingTarget);
2966 		}
2967 	}
2968 
2969 	// Apply final focus
2970 	if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindowForTabbing))
2971 	{
2972 		g.NavDisableHighlight = false;
2973 		g.NavDisableMouseHover = true;
2974 		apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
2975 		ClosePopupsOverWindow(apply_focus_window);
2976 		FocusWindow(apply_focus_window);
2977 		if (apply_focus_window->NavLastIds[0] == 0)
2978 			NavInitWindow(apply_focus_window, false);
2979 
2980 		// If the window only has a menu layer, select it directly
2981 		if (apply_focus_window->DC.NavLayerActiveMask == (1 << 1))
2982 			g.NavLayer = 1;
2983 	}
2984 	if (apply_focus_window)
2985 		g.NavWindowingTarget = NULL;
2986 
2987 	// Apply menu/layer toggle
2988 	if (apply_toggle_layer && g.NavWindow)
2989 	{
2990 		ImGuiWindow* new_nav_window = g.NavWindow;
2991 		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)
2992 			new_nav_window = new_nav_window->ParentWindow;
2993 		if (new_nav_window != g.NavWindow)
2994 		{
2995 			ImGuiWindow* old_nav_window = g.NavWindow;
2996 			FocusWindow(new_nav_window);
2997 			new_nav_window->NavLastChildNavWindow = old_nav_window;
2998 		}
2999 		g.NavDisableHighlight = false;
3000 		g.NavDisableMouseHover = true;
3001 		NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0);
3002 	}
3003 }
3004 
3005 // NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated.
NavScrollToBringItemIntoView(ImGuiWindow * window,ImRect & item_rect_rel)3006 static void NavScrollToBringItemIntoView(ImGuiWindow* window, ImRect& item_rect_rel)
3007 {
3008 	// Scroll to keep newly navigated item fully into view
3009 	ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1));
3010 	//g.OverlayDrawList.AddRect(window->Pos + window_rect_rel.Min, window->Pos + window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG]
3011 	if (window_rect_rel.Contains(item_rect_rel))
3012 		return;
3013 
3014 	ImGuiContext& g = *GImGui;
3015 	if (window->ScrollbarX && item_rect_rel.Min.x < window_rect_rel.Min.x)
3016 	{
3017 		window->ScrollTarget.x = item_rect_rel.Min.x + window->Scroll.x - g.Style.ItemSpacing.x;
3018 		window->ScrollTargetCenterRatio.x = 0.0f;
3019 	}
3020 	else if (window->ScrollbarX && item_rect_rel.Max.x >= window_rect_rel.Max.x)
3021 	{
3022 		window->ScrollTarget.x = item_rect_rel.Max.x + window->Scroll.x + g.Style.ItemSpacing.x;
3023 		window->ScrollTargetCenterRatio.x = 1.0f;
3024 	}
3025 	if (item_rect_rel.Min.y < window_rect_rel.Min.y)
3026 	{
3027 		window->ScrollTarget.y = item_rect_rel.Min.y + window->Scroll.y - g.Style.ItemSpacing.y;
3028 		window->ScrollTargetCenterRatio.y = 0.0f;
3029 	}
3030 	else if (item_rect_rel.Max.y >= window_rect_rel.Max.y)
3031 	{
3032 		window->ScrollTarget.y = item_rect_rel.Max.y + window->Scroll.y + g.Style.ItemSpacing.y;
3033 		window->ScrollTargetCenterRatio.y = 1.0f;
3034 	}
3035 
3036 	// Estimate upcoming scroll so we can offset our relative mouse position so mouse position can be applied immediately (under this block)
3037 	ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
3038 	item_rect_rel.Translate(window->Scroll - next_scroll);
3039 }
3040 
NavUpdate()3041 static void ImGui::NavUpdate()
3042 {
3043 	ImGuiContext& g = *GImGui;
3044 	g.IO.WantMoveMouse = false;
3045 
3046 #if 0
3047     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);
3048 #endif
3049 
3050 	// Update Keyboard->Nav inputs mapping
3051 	memset(g.IO.NavInputs + ImGuiNavInput_InternalStart_, 0, (ImGuiNavInput_COUNT - ImGuiNavInput_InternalStart_) * sizeof(g.IO.NavInputs[0]));
3052 	if (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard)
3053 	{
3054 #define NAV_MAP_KEY(_KEY, _NAV_INPUT) \
3055 	if (g.IO.KeyMap[_KEY] != -1 && IsKeyDown(g.IO.KeyMap[_KEY])) g.IO.NavInputs[_NAV_INPUT] = 1.0f;
3056 		NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate);
3057 		NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input);
3058 		NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel);
3059 		NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_);
3060 		NAV_MAP_KEY(ImGuiKey_RightArrow, ImGuiNavInput_KeyRight_);
3061 		NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_);
3062 		NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_);
3063 		if (g.IO.KeyCtrl) g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
3064 		if (g.IO.KeyShift) g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
3065 		if (g.IO.KeyAlt) g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f;
3066 #undef NAV_MAP_KEY
3067 	}
3068 
3069 	memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration));
3070 	for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++)
3071 		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;
3072 
3073 	// Process navigation init request (select first/default focus)
3074 	if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove))
3075 	{
3076 		// Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
3077 		IM_ASSERT(g.NavWindow);
3078 		if (g.NavInitRequestFromMove)
3079 			SetNavIDAndMoveMouse(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel);
3080 		else
3081 			SetNavID(g.NavInitResultId, g.NavLayer);
3082 		g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
3083 	}
3084 	g.NavInitRequest = false;
3085 	g.NavInitRequestFromMove = false;
3086 	g.NavInitResultId = 0;
3087 	g.NavJustMovedToId = 0;
3088 
3089 	// Process navigation move request
3090 	if (g.NavMoveRequest && (g.NavMoveResultLocal.ID != 0 || g.NavMoveResultOther.ID != 0))
3091 	{
3092 		// Select which result to use
3093 		ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
3094 		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
3095 			if ((g.NavMoveResultOther.DistBox < g.NavMoveResultLocal.DistBox) || (g.NavMoveResultOther.DistBox == g.NavMoveResultLocal.DistBox && g.NavMoveResultOther.DistCenter < g.NavMoveResultLocal.DistCenter))
3096 				result = &g.NavMoveResultOther;
3097 
3098 		IM_ASSERT(g.NavWindow && result->Window);
3099 
3100 		// Scroll to keep newly navigated item fully into view
3101 		if (g.NavLayer == 0)
3102 			NavScrollToBringItemIntoView(result->Window, result->RectRel);
3103 
3104 		// Apply result from previous frame navigation directional move request
3105 		ClearActiveID();
3106 		g.NavWindow = result->Window;
3107 		SetNavIDAndMoveMouse(result->ID, g.NavLayer, result->RectRel);
3108 		g.NavJustMovedToId = result->ID;
3109 		g.NavMoveFromClampedRefRect = false;
3110 	}
3111 
3112 	// When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
3113 	if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
3114 	{
3115 		IM_ASSERT(g.NavMoveRequest);
3116 		if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
3117 			g.NavDisableHighlight = false;
3118 		g.NavMoveRequestForward = ImGuiNavForward_None;
3119 	}
3120 
3121 	// Apply application mouse position movement, after we had a chance to process move request result.
3122 	if (g.NavMousePosDirty && g.NavIdIsAlive)
3123 	{
3124 		// Set mouse position given our knowledge of the nav widget position from last frame
3125 		if (g.IO.NavFlags & ImGuiNavFlags_MoveMouse)
3126 		{
3127 			g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredMousePos();
3128 			g.IO.WantMoveMouse = true;
3129 		}
3130 		g.NavMousePosDirty = false;
3131 	}
3132 	g.NavIdIsAlive = false;
3133 	g.NavJustTabbedId = 0;
3134 	IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
3135 
3136 	// 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
3137 	if (g.NavWindow)
3138 		NavSaveLastChildNavWindow(g.NavWindow);
3139 	if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0)
3140 		g.NavWindow->NavLastChildNavWindow = NULL;
3141 
3142 	NavUpdateWindowing();
3143 
3144 	// Set output flags for user application
3145 	g.IO.NavActive = (g.IO.NavFlags & (ImGuiNavFlags_EnableGamepad | ImGuiNavFlags_EnableKeyboard)) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
3146 	g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest;
3147 
3148 	// Process NavCancel input (to close a popup, get back to parent, clear focus)
3149 	if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
3150 	{
3151 		if (g.ActiveId != 0)
3152 		{
3153 			ClearActiveID();
3154 		}
3155 		else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
3156 		{
3157 			// Exit child window
3158 			ImGuiWindow* child_window = g.NavWindow;
3159 			ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
3160 			IM_ASSERT(child_window->ChildId != 0);
3161 			FocusWindow(parent_window);
3162 			SetNavID(child_window->ChildId, 0);
3163 			g.NavIdIsAlive = false;
3164 			if (g.NavDisableMouseHover)
3165 				g.NavMousePosDirty = true;
3166 		}
3167 		else if (g.OpenPopupStack.Size > 0)
3168 		{
3169 			// Close open popup/menu
3170 			if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
3171 				ClosePopupToLevel(g.OpenPopupStack.Size - 1);
3172 		}
3173 		else if (g.NavLayer != 0)
3174 		{
3175 			// Leave the "menu" layer
3176 			NavRestoreLayer(0);
3177 		}
3178 		else
3179 		{
3180 			// Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
3181 			if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
3182 				g.NavWindow->NavLastIds[0] = 0;
3183 			g.NavId = 0;
3184 		}
3185 	}
3186 
3187 	// Process manual activation request
3188 	g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
3189 	if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
3190 	{
3191 		bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
3192 		bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
3193 		if (g.ActiveId == 0 && activate_pressed)
3194 			g.NavActivateId = g.NavId;
3195 		if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
3196 			g.NavActivateDownId = g.NavId;
3197 		if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
3198 			g.NavActivatePressedId = g.NavId;
3199 		if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
3200 			g.NavInputId = g.NavId;
3201 	}
3202 	if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
3203 		g.NavDisableHighlight = true;
3204 	if (g.NavActivateId != 0)
3205 		IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
3206 	g.NavMoveRequest = false;
3207 
3208 	// Process programmatic activation request
3209 	if (g.NavNextActivateId != 0)
3210 		g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
3211 	g.NavNextActivateId = 0;
3212 
3213 	// Initiate directional inputs request
3214 	const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags;
3215 	if (g.NavMoveRequestForward == ImGuiNavForward_None)
3216 	{
3217 		g.NavMoveDir = ImGuiDir_None;
3218 		if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
3219 		{
3220 			if ((allowed_dir_flags & (1 << ImGuiDir_Left)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft, ImGuiNavInput_KeyLeft_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Left;
3221 			if ((allowed_dir_flags & (1 << ImGuiDir_Right)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadRight, ImGuiNavInput_KeyRight_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Right;
3222 			if ((allowed_dir_flags & (1 << ImGuiDir_Up)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadUp, ImGuiNavInput_KeyUp_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Up;
3223 			if ((allowed_dir_flags & (1 << ImGuiDir_Down)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadDown, ImGuiNavInput_KeyDown_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Down;
3224 		}
3225 	}
3226 	else
3227 	{
3228 		// 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)
3229 		IM_ASSERT(g.NavMoveDir != ImGuiDir_None);
3230 		IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
3231 		g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
3232 	}
3233 
3234 	if (g.NavMoveDir != ImGuiDir_None)
3235 	{
3236 		g.NavMoveRequest = true;
3237 		g.NavMoveDirLast = g.NavMoveDir;
3238 	}
3239 
3240 	// 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
3241 	if (g.NavMoveRequest && g.NavId == 0)
3242 	{
3243 		g.NavInitRequest = g.NavInitRequestFromMove = true;
3244 		g.NavInitResultId = 0;
3245 		g.NavDisableHighlight = false;
3246 	}
3247 
3248 	NavUpdateAnyRequestFlag();
3249 
3250 	// Scrolling
3251 	if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
3252 	{
3253 		// *Fallback* manual-scroll with NavUp/NavDown when window has no navigable item
3254 		ImGuiWindow* window = g.NavWindow;
3255 		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.
3256 		if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
3257 		{
3258 			if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
3259 				SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
3260 			if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
3261 				SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
3262 		}
3263 
3264 		// *Normal* Manual scroll with NavScrollXXX keys
3265 		// Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
3266 		ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f / 10.0f, 10.0f);
3267 		if (scroll_dir.x != 0.0f && window->ScrollbarX)
3268 		{
3269 			SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
3270 			g.NavMoveFromClampedRefRect = true;
3271 		}
3272 		if (scroll_dir.y != 0.0f)
3273 		{
3274 			SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
3275 			g.NavMoveFromClampedRefRect = true;
3276 		}
3277 	}
3278 
3279 	// Reset search results
3280 	g.NavMoveResultLocal.Clear();
3281 	g.NavMoveResultOther.Clear();
3282 
3283 	// 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
3284 	if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
3285 	{
3286 		ImGuiWindow* window = g.NavWindow;
3287 		ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1));
3288 		if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
3289 		{
3290 			float pad = window->CalcFontSize() * 0.5f;
3291 			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
3292 			window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel);
3293 			g.NavId = 0;
3294 		}
3295 		g.NavMoveFromClampedRefRect = false;
3296 	}
3297 
3298 	// 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)
3299 	ImRect nav_rect_rel = (g.NavWindow && g.NavWindow->NavRectRel[g.NavLayer].IsFinite()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
3300 	g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
3301 	g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x);
3302 	g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
3303 	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().
3304 	//g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
3305 	g.NavScoringCount = 0;
3306 #if IMGUI_DEBUG_NAV_RECTS
3307 	if (g.NavWindow)
3308 	{
3309 		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));
3310 	}  // [DEBUG]
3311 	if (g.NavWindow)
3312 	{
3313 		ImU32 col = (g.NavWindow->HiddenFrames <= 0) ? IM_COL32(255, 0, 255, 255) : IM_COL32(255, 0, 0, 255);
3314 		ImVec2 p = NavCalcPreferredMousePos();
3315 		char buf[32];
3316 		ImFormatString(buf, 32, "%d", g.NavLayer);
3317 		g.OverlayDrawList.AddCircleFilled(p, 3.0f, col);
3318 		g.OverlayDrawList.AddText(NULL, 13.0f, p + ImVec2(8, -4), col, buf);
3319 	}
3320 #endif
3321 }
3322 
UpdateMovingWindow()3323 static void ImGui::UpdateMovingWindow()
3324 {
3325 	ImGuiContext& g = *GImGui;
3326 	if (g.MovingWindow && g.MovingWindow->MoveId == g.ActiveId && g.ActiveIdSource == ImGuiInputSource_Mouse)
3327 	{
3328 		// We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3329 		// We track it to preserve Focus and so that ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3330 		KeepAliveID(g.ActiveId);
3331 		IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3332 		ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3333 		if (g.IO.MouseDown[0])
3334 		{
3335 			ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3336 			if (moving_window->PosFloat.x != pos.x || moving_window->PosFloat.y != pos.y)
3337 			{
3338 				MarkIniSettingsDirty(moving_window);
3339 				moving_window->PosFloat = pos;
3340 			}
3341 			FocusWindow(g.MovingWindow);
3342 		}
3343 		else
3344 		{
3345 			ClearActiveID();
3346 			g.MovingWindow = NULL;
3347 		}
3348 	}
3349 	else
3350 	{
3351 		// When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3352 		if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3353 		{
3354 			KeepAliveID(g.ActiveId);
3355 			if (!g.IO.MouseDown[0])
3356 				ClearActiveID();
3357 		}
3358 		g.MovingWindow = NULL;
3359 	}
3360 }
3361 
NewFrame()3362 void ImGui::NewFrame()
3363 {
3364 	IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3365 	ImGuiContext& g = *GImGui;
3366 
3367 	// Check user data
3368 	// (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)
3369 	IM_ASSERT(g.Initialized);
3370 	IM_ASSERT(g.IO.DeltaTime >= 0.0f && "Need a positive DeltaTime (zero is tolerated but will cause some timing issues)");
3371 	IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value");
3372 	IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3373 	IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3374 	IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting");
3375 	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)");
3376 	IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
3377 	for (int n = 0; n < ImGuiKey_COUNT; n++)
3378 		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)");
3379 
3380 	// 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)
3381 	if (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard)
3382 		IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
3383 
3384 	// Load settings on first frame
3385 	if (!g.SettingsLoaded)
3386 	{
3387 		IM_ASSERT(g.SettingsWindows.empty());
3388 		LoadIniSettingsFromDisk(g.IO.IniFilename);
3389 		g.SettingsLoaded = true;
3390 	}
3391 
3392 	g.Time += g.IO.DeltaTime;
3393 	g.FrameCount += 1;
3394 	g.TooltipOverrideCount = 0;
3395 	g.WindowsActiveCount = 0;
3396 
3397 	SetCurrentFont(GetDefaultFont());
3398 	IM_ASSERT(g.Font->IsLoaded());
3399 	g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3400 	g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3401 
3402 	g.OverlayDrawList.Clear();
3403 	g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID);
3404 	g.OverlayDrawList.PushClipRectFullScreen();
3405 	g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
3406 
3407 	// Mark rendering data as invalid to prevent user who may have a handle on it to use it
3408 	g.DrawData.Clear();
3409 
3410 	// Clear reference to active widget if the widget isn't alive anymore
3411 	if (!g.HoveredIdPreviousFrame)
3412 		g.HoveredIdTimer = 0.0f;
3413 	g.HoveredIdPreviousFrame = g.HoveredId;
3414 	g.HoveredId = 0;
3415 	g.HoveredIdAllowOverlap = false;
3416 	if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3417 		ClearActiveID();
3418 	if (g.ActiveId)
3419 		g.ActiveIdTimer += g.IO.DeltaTime;
3420 	g.ActiveIdPreviousFrame = g.ActiveId;
3421 	g.ActiveIdIsAlive = false;
3422 	g.ActiveIdIsJustActivated = false;
3423 	if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
3424 		g.ScalarAsInputTextId = 0;
3425 
3426 	// Elapse drag & drop payload
3427 	if (g.DragDropActive && g.DragDropPayload.DataFrameCount + 1 < g.FrameCount)
3428 	{
3429 		ClearDragDrop();
3430 		g.DragDropPayloadBufHeap.clear();
3431 		memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
3432 	}
3433 	g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3434 	g.DragDropAcceptIdCurr = 0;
3435 	g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3436 
3437 	// Update keyboard input state
3438 	memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3439 	for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3440 		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;
3441 
3442 	// Update gamepad/keyboard directional navigation
3443 	NavUpdate();
3444 
3445 	// Update mouse input state
3446 	// 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
3447 	if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3448 		g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3449 	else
3450 		g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3451 	if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3452 		g.NavDisableMouseHover = false;
3453 
3454 	g.IO.MousePosPrev = g.IO.MousePos;
3455 	for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3456 	{
3457 		g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3458 		g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3459 		g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3460 		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;
3461 		g.IO.MouseDoubleClicked[i] = false;
3462 		if (g.IO.MouseClicked[i])
3463 		{
3464 			if (g.Time - g.IO.MouseClickedTime[i] < g.IO.MouseDoubleClickTime)
3465 			{
3466 				if (ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3467 					g.IO.MouseDoubleClicked[i] = true;
3468 				g.IO.MouseClickedTime[i] = -FLT_MAX;  // so the third click isn't turned into a double-click
3469 			}
3470 			else
3471 			{
3472 				g.IO.MouseClickedTime[i] = g.Time;
3473 			}
3474 			g.IO.MouseClickedPos[i] = g.IO.MousePos;
3475 			g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3476 			g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3477 		}
3478 		else if (g.IO.MouseDown[i])
3479 		{
3480 			ImVec2 mouse_delta = g.IO.MousePos - g.IO.MouseClickedPos[i];
3481 			g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, mouse_delta.x < 0.0f ? -mouse_delta.x : mouse_delta.x);
3482 			g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, mouse_delta.y < 0.0f ? -mouse_delta.y : mouse_delta.y);
3483 			g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(mouse_delta));
3484 		}
3485 		if (g.IO.MouseClicked[i])  // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3486 			g.NavDisableMouseHover = false;
3487 	}
3488 
3489 	// Calculate frame-rate for the user, as a purely luxurious feature
3490 	g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3491 	g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3492 	g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3493 	g.IO.Framerate = 1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame));
3494 
3495 	// Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3496 	UpdateMovingWindow();
3497 
3498 	// Delay saving settings so we don't spam disk too much
3499 	if (g.SettingsDirtyTimer > 0.0f)
3500 	{
3501 		g.SettingsDirtyTimer -= g.IO.DeltaTime;
3502 		if (g.SettingsDirtyTimer <= 0.0f)
3503 			SaveIniSettingsToDisk(g.IO.IniFilename);
3504 	}
3505 
3506 	// Find the window we are hovering
3507 	// - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3508 	// - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point.
3509 	// - 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.
3510 	g.HoveredWindow = (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) ? g.MovingWindow : FindHoveredWindow();
3511 	g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
3512 
3513 	ImGuiWindow* modal_window = GetFrontMostModalRootWindow();
3514 	if (modal_window != NULL)
3515 	{
3516 		g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3517 		if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3518 			g.HoveredRootWindow = g.HoveredWindow = NULL;
3519 	}
3520 	else
3521 	{
3522 		g.ModalWindowDarkeningRatio = 0.0f;
3523 	}
3524 
3525 	// Update the WantCaptureMouse/WantCaptureKeyboard flags, so user can capture/discard the inputs away from the rest of their application.
3526 	// When clicking outside of a window we assume the click is owned by the application and won't request capture. We need to track click ownership.
3527 	int mouse_earliest_button_down = -1;
3528 	bool mouse_any_down = false;
3529 	for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3530 	{
3531 		if (g.IO.MouseClicked[i])
3532 			g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
3533 		mouse_any_down |= g.IO.MouseDown[i];
3534 		if (g.IO.MouseDown[i])
3535 			if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3536 				mouse_earliest_button_down = i;
3537 	}
3538 	bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3539 	if (g.WantCaptureMouseNextFrame != -1)
3540 		g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3541 	else
3542 		g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
3543 
3544 	if (g.WantCaptureKeyboardNextFrame != -1)
3545 		g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3546 	else
3547 		g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3548 	if (g.IO.NavActive && (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard) && !(g.IO.NavFlags & ImGuiNavFlags_NoCaptureKeyboard))
3549 		g.IO.WantCaptureKeyboard = true;
3550 
3551 	g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : 0;
3552 	g.MouseCursor = ImGuiMouseCursor_Arrow;
3553 	g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3554 	g.OsImePosRequest = ImVec2(1.0f, 1.0f);  // OS Input Method Editor showing on top-left of our window by default
3555 
3556 	// If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3557 	// FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3558 	bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3559 	if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3560 		g.HoveredWindow = g.HoveredRootWindow = NULL;
3561 
3562 	// Mouse wheel scrolling, scale
3563 	if (g.HoveredWindow && !g.HoveredWindow->Collapsed && (g.IO.MouseWheel != 0.0f || g.IO.MouseWheelH != 0.0f))
3564 	{
3565 		// 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).
3566 		ImGuiWindow* window = g.HoveredWindow;
3567 		ImGuiWindow* scroll_window = window;
3568 		while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs) && scroll_window->ParentWindow)
3569 			scroll_window = scroll_window->ParentWindow;
3570 		const bool scroll_allowed = !(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs);
3571 
3572 		if (g.IO.MouseWheel != 0.0f)
3573 		{
3574 			if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3575 			{
3576 				// Zoom / Scale window
3577 				const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3578 				const float scale = new_font_scale / window->FontWindowScale;
3579 				window->FontWindowScale = new_font_scale;
3580 
3581 				const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3582 				window->Pos += offset;
3583 				window->PosFloat += offset;
3584 				window->Size *= scale;
3585 				window->SizeFull *= scale;
3586 			}
3587 			else if (!g.IO.KeyCtrl && scroll_allowed)
3588 			{
3589 				// Mouse wheel vertical scrolling
3590 				float scroll_amount = 5 * scroll_window->CalcFontSize();
3591 				scroll_amount = (float)(int)ImMin(scroll_amount, (scroll_window->ContentsRegionRect.GetHeight() + scroll_window->WindowPadding.y * 2.0f) * 0.67f);
3592 				SetWindowScrollY(scroll_window, scroll_window->Scroll.y - g.IO.MouseWheel * scroll_amount);
3593 			}
3594 		}
3595 		if (g.IO.MouseWheelH != 0.0f && scroll_allowed)
3596 		{
3597 			// Mouse wheel horizontal scrolling (for hardware that supports it)
3598 			float scroll_amount = scroll_window->CalcFontSize();
3599 			if (!g.IO.KeyCtrl && !(window->Flags & ImGuiWindowFlags_NoScrollWithMouse))
3600 				SetWindowScrollX(window, window->Scroll.x - g.IO.MouseWheelH * scroll_amount);
3601 		}
3602 	}
3603 
3604 	// Pressing TAB activate widget focus
3605 	if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false))
3606 	{
3607 		if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3608 			g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3609 		else
3610 			g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0;
3611 	}
3612 	g.NavIdTabCounter = INT_MAX;
3613 
3614 	// Mark all windows as not visible
3615 	for (int i = 0; i != g.Windows.Size; i++)
3616 	{
3617 		ImGuiWindow* window = g.Windows[i];
3618 		window->WasActive = window->Active;
3619 		window->Active = false;
3620 		window->WriteAccessed = false;
3621 	}
3622 
3623 	// Closing the focused window restore focus to the first active root window in descending z-order
3624 	if (g.NavWindow && !g.NavWindow->WasActive)
3625 		FocusFrontMostActiveWindow(NULL);
3626 
3627 	// No window should be open at the beginning of the frame.
3628 	// But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3629 	g.CurrentWindowStack.resize(0);
3630 	g.CurrentPopupStack.resize(0);
3631 	ClosePopupsOverWindow(g.NavWindow);
3632 
3633 	// Create implicit window - we will only render it if the user has added something to it.
3634 	// We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3635 	SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
3636 	Begin("Debug##Default");
3637 }
3638 
SettingsHandlerWindow_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)3639 static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
3640 {
3641 	ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0));
3642 	if (!settings)
3643 		settings = AddWindowSettings(name);
3644 	return (void*)settings;
3645 }
3646 
SettingsHandlerWindow_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)3647 static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
3648 {
3649 	ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
3650 	float x, y;
3651 	int i;
3652 	if (sscanf(line, "Pos=%f,%f", &x, &y) == 2)
3653 		settings->Pos = ImVec2(x, y);
3654 	else if (sscanf(line, "Size=%f,%f", &x, &y) == 2)
3655 		settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize);
3656 	else if (sscanf(line, "Collapsed=%d", &i) == 1)
3657 		settings->Collapsed = (i != 0);
3658 }
3659 
SettingsHandlerWindow_WriteAll(ImGuiContext * imgui_ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)3660 static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
3661 {
3662 	// Gather data from windows that were active during this session
3663 	ImGuiContext& g = *imgui_ctx;
3664 	for (int i = 0; i != g.Windows.Size; i++)
3665 	{
3666 		ImGuiWindow* window = g.Windows[i];
3667 		if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
3668 			continue;
3669 		ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID);
3670 		if (!settings)
3671 			settings = AddWindowSettings(window->Name);
3672 		settings->Pos = window->Pos;
3673 		settings->Size = window->SizeFull;
3674 		settings->Collapsed = window->Collapsed;
3675 	}
3676 
3677 	// Write a buffer
3678 	// If a window wasn't opened in this session we preserve its settings
3679 	buf->reserve(buf->size() + g.SettingsWindows.Size * 96);  // ballpark reserve
3680 	for (int i = 0; i != g.SettingsWindows.Size; i++)
3681 	{
3682 		const ImGuiWindowSettings* settings = &g.SettingsWindows[i];
3683 		if (settings->Pos.x == FLT_MAX)
3684 			continue;
3685 		const char* name = settings->Name;
3686 		if (const char* p = strstr(name, "###"))  // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
3687 			name = p;
3688 		buf->appendf("[%s][%s]\n", handler->TypeName, name);
3689 		buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
3690 		buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
3691 		buf->appendf("Collapsed=%d\n", settings->Collapsed);
3692 		buf->appendf("\n");
3693 	}
3694 }
3695 
Initialize(ImGuiContext * context)3696 void ImGui::Initialize(ImGuiContext* context)
3697 {
3698 	ImGuiContext& g = *context;
3699 	IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3700 	g.LogClipboard = IM_NEW(ImGuiTextBuffer)();
3701 
3702 	// Add .ini handle for ImGuiWindow type
3703 	ImGuiSettingsHandler ini_handler;
3704 	ini_handler.TypeName = "Window";
3705 	ini_handler.TypeHash = ImHash("Window", 0, 0);
3706 	ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen;
3707 	ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine;
3708 	ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll;
3709 	g.SettingsHandlers.push_front(ini_handler);
3710 
3711 	g.Initialized = true;
3712 }
3713 
3714 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)3715 void ImGui::Shutdown(ImGuiContext* context)
3716 {
3717 	ImGuiContext& g = *context;
3718 
3719 	// 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)
3720 	if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3721 		IM_DELETE(g.IO.Fonts);
3722 
3723 	// Cleanup of other data are conditional on actually having initialize ImGui.
3724 	if (!g.Initialized)
3725 		return;
3726 
3727 	SaveIniSettingsToDisk(g.IO.IniFilename);
3728 
3729 	// Clear everything else
3730 	for (int i = 0; i < g.Windows.Size; i++)
3731 		IM_DELETE(g.Windows[i]);
3732 	g.Windows.clear();
3733 	g.WindowsSortBuffer.clear();
3734 	g.CurrentWindow = NULL;
3735 	g.CurrentWindowStack.clear();
3736 	g.WindowsById.Clear();
3737 	g.NavWindow = NULL;
3738 	g.HoveredWindow = NULL;
3739 	g.HoveredRootWindow = NULL;
3740 	g.ActiveIdWindow = NULL;
3741 	g.MovingWindow = NULL;
3742 	for (int i = 0; i < g.SettingsWindows.Size; i++)
3743 		IM_DELETE(g.SettingsWindows[i].Name);
3744 	g.ColorModifiers.clear();
3745 	g.StyleModifiers.clear();
3746 	g.FontStack.clear();
3747 	g.OpenPopupStack.clear();
3748 	g.CurrentPopupStack.clear();
3749 	g.DrawDataBuilder.ClearFreeMemory();
3750 	g.OverlayDrawList.ClearFreeMemory();
3751 	g.PrivateClipboard.clear();
3752 	g.InputTextState.Text.clear();
3753 	g.InputTextState.InitialText.clear();
3754 	g.InputTextState.TempTextBuffer.clear();
3755 
3756 	g.SettingsWindows.clear();
3757 	g.SettingsHandlers.clear();
3758 
3759 	if (g.LogFile && g.LogFile != stdout)
3760 	{
3761 		fclose(g.LogFile);
3762 		g.LogFile = NULL;
3763 	}
3764 	if (g.LogClipboard)
3765 		IM_DELETE(g.LogClipboard);
3766 
3767 	g.Initialized = false;
3768 }
3769 
FindWindowSettings(ImGuiID id)3770 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
3771 {
3772 	ImGuiContext& g = *GImGui;
3773 	for (int i = 0; i != g.SettingsWindows.Size; i++)
3774 		if (g.SettingsWindows[i].Id == id)
3775 			return &g.SettingsWindows[i];
3776 	return NULL;
3777 }
3778 
AddWindowSettings(const char * name)3779 static ImGuiWindowSettings* AddWindowSettings(const char* name)
3780 {
3781 	ImGuiContext& g = *GImGui;
3782 	g.SettingsWindows.push_back(ImGuiWindowSettings());
3783 	ImGuiWindowSettings* settings = &g.SettingsWindows.back();
3784 	settings->Name = ImStrdup(name);
3785 	settings->Id = ImHash(name, 0);
3786 	return settings;
3787 }
3788 
LoadIniSettingsFromDisk(const char * ini_filename)3789 static void LoadIniSettingsFromDisk(const char* ini_filename)
3790 {
3791 	if (!ini_filename)
3792 		return;
3793 	char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", NULL, +1);
3794 	if (!file_data)
3795 		return;
3796 	LoadIniSettingsFromMemory(file_data);
3797 	ImGui::MemFree(file_data);
3798 }
3799 
FindSettingsHandler(const char * type_name)3800 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
3801 {
3802 	ImGuiContext& g = *GImGui;
3803 	const ImGuiID type_hash = ImHash(type_name, 0, 0);
3804 	for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
3805 		if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
3806 			return &g.SettingsHandlers[handler_n];
3807 	return NULL;
3808 }
3809 
3810 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * buf_readonly)3811 static void LoadIniSettingsFromMemory(const char* buf_readonly)
3812 {
3813 	// For convenience and to make the code simpler, we'll write zero terminators inside the buffer. So let's create a writable copy.
3814 	char* buf = ImStrdup(buf_readonly);
3815 	char* buf_end = buf + strlen(buf);
3816 
3817 	ImGuiContext& g = *GImGui;
3818 	void* entry_data = NULL;
3819 	ImGuiSettingsHandler* entry_handler = NULL;
3820 
3821 	char* line_end = NULL;
3822 	for (char* line = buf; line < buf_end; line = line_end + 1)
3823 	{
3824 		// Skip new lines markers, then find end of the line
3825 		while (*line == '\n' || *line == '\r')
3826 			line++;
3827 		line_end = line;
3828 		while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
3829 			line_end++;
3830 		line_end[0] = 0;
3831 
3832 		if (line[0] == '[' && line_end > line && line_end[-1] == ']')
3833 		{
3834 			// Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
3835 			line_end[-1] = 0;
3836 			const char* name_end = line_end - 1;
3837 			const char* type_start = line + 1;
3838 			char* type_end = ImStrchrRange(type_start, name_end, ']');
3839 			const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
3840 			if (!type_end || !name_start)
3841 			{
3842 				name_start = type_start;  // Import legacy entries that have no type
3843 				type_start = "Window";
3844 			}
3845 			else
3846 			{
3847 				*type_end = 0;  // Overwrite first ']'
3848 				name_start++;   // Skip second '['
3849 			}
3850 			entry_handler = ImGui::FindSettingsHandler(type_start);
3851 			entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
3852 		}
3853 		else if (entry_handler != NULL && entry_data != NULL)
3854 		{
3855 			// Let type handler parse the line
3856 			entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
3857 		}
3858 	}
3859 	ImGui::MemFree(buf);
3860 	g.SettingsLoaded = true;
3861 }
3862 
SaveIniSettingsToDisk(const char * ini_filename)3863 static void SaveIniSettingsToDisk(const char* ini_filename)
3864 {
3865 	ImGuiContext& g = *GImGui;
3866 	g.SettingsDirtyTimer = 0.0f;
3867 	if (!ini_filename)
3868 		return;
3869 
3870 	ImVector<char> buf;
3871 	SaveIniSettingsToMemory(buf);
3872 
3873 	FILE* f = ImFileOpen(ini_filename, "wt");
3874 	if (!f)
3875 		return;
3876 	fwrite(buf.Data, sizeof(char), (size_t)buf.Size, f);
3877 	fclose(f);
3878 }
3879 
SaveIniSettingsToMemory(ImVector<char> & out_buf)3880 static void SaveIniSettingsToMemory(ImVector<char>& out_buf)
3881 {
3882 	ImGuiContext& g = *GImGui;
3883 	g.SettingsDirtyTimer = 0.0f;
3884 
3885 	ImGuiTextBuffer buf;
3886 	for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
3887 	{
3888 		ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
3889 		handler->WriteAllFn(&g, handler, &buf);
3890 	}
3891 
3892 	buf.Buf.pop_back();  // Remove extra zero-terminator used by ImGuiTextBuffer
3893 	out_buf.swap(buf.Buf);
3894 }
3895 
MarkIniSettingsDirty()3896 void ImGui::MarkIniSettingsDirty()
3897 {
3898 	ImGuiContext& g = *GImGui;
3899 	if (g.SettingsDirtyTimer <= 0.0f)
3900 		g.SettingsDirtyTimer = g.IO.IniSavingRate;
3901 }
3902 
MarkIniSettingsDirty(ImGuiWindow * window)3903 static void MarkIniSettingsDirty(ImGuiWindow* window)
3904 {
3905 	ImGuiContext& g = *GImGui;
3906 	if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
3907 		if (g.SettingsDirtyTimer <= 0.0f)
3908 			g.SettingsDirtyTimer = g.IO.IniSavingRate;
3909 }
3910 
3911 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)3912 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
3913 {
3914 	const ImGuiWindow* a = *(const ImGuiWindow**)lhs;
3915 	const ImGuiWindow* b = *(const ImGuiWindow**)rhs;
3916 	if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
3917 		return d;
3918 	if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
3919 		return d;
3920 	return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
3921 }
3922 
AddWindowToSortedBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)3923 static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
3924 {
3925 	out_sorted_windows->push_back(window);
3926 	if (window->Active)
3927 	{
3928 		int count = window->DC.ChildWindows.Size;
3929 		if (count > 1)
3930 			qsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
3931 		for (int i = 0; i < count; i++)
3932 		{
3933 			ImGuiWindow* child = window->DC.ChildWindows[i];
3934 			if (child->Active)
3935 				AddWindowToSortedBuffer(out_sorted_windows, child);
3936 		}
3937 	}
3938 }
3939 
AddDrawListToDrawData(ImVector<ImDrawList * > * out_render_list,ImDrawList * draw_list)3940 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_render_list, ImDrawList* draw_list)
3941 {
3942 	if (draw_list->CmdBuffer.empty())
3943 		return;
3944 
3945 	// Remove trailing command if unused
3946 	ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
3947 	if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
3948 	{
3949 		draw_list->CmdBuffer.pop_back();
3950 		if (draw_list->CmdBuffer.empty())
3951 			return;
3952 	}
3953 
3954 	// 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.
3955 	IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
3956 	IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
3957 	IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
3958 
3959 	// Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
3960 	// If this assert triggers because you are drawing lots of stuff manually:
3961 	// 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.
3962 	// 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.
3963 	//    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:
3964 	//      glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
3965 	//    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.
3966 	// 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.
3967 	if (sizeof(ImDrawIdx) == 2)
3968 		IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
3969 
3970 	out_render_list->push_back(draw_list);
3971 }
3972 
AddWindowToDrawData(ImVector<ImDrawList * > * out_render_list,ImGuiWindow * window)3973 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
3974 {
3975 	AddDrawListToDrawData(out_render_list, window->DrawList);
3976 	for (int i = 0; i < window->DC.ChildWindows.Size; i++)
3977 	{
3978 		ImGuiWindow* child = window->DC.ChildWindows[i];
3979 		if (child->Active && child->HiddenFrames <= 0)  // clipped children may have been marked not active
3980 			AddWindowToDrawData(out_render_list, child);
3981 	}
3982 }
3983 
AddWindowToDrawDataSelectLayer(ImGuiWindow * window)3984 static void AddWindowToDrawDataSelectLayer(ImGuiWindow* window)
3985 {
3986 	ImGuiContext& g = *GImGui;
3987 	g.IO.MetricsActiveWindows++;
3988 	if (window->Flags & ImGuiWindowFlags_Tooltip)
3989 		AddWindowToDrawData(&g.DrawDataBuilder.Layers[1], window);
3990 	else
3991 		AddWindowToDrawData(&g.DrawDataBuilder.Layers[0], window);
3992 }
3993 
FlattenIntoSingleLayer()3994 void ImDrawDataBuilder::FlattenIntoSingleLayer()
3995 {
3996 	int n = Layers[0].Size;
3997 	int size = n;
3998 	for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
3999 		size += Layers[i].Size;
4000 	Layers[0].resize(size);
4001 	for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
4002 	{
4003 		ImVector<ImDrawList*>& layer = Layers[layer_n];
4004 		if (layer.empty())
4005 			continue;
4006 		memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
4007 		n += layer.Size;
4008 		layer.resize(0);
4009 	}
4010 }
4011 
SetupDrawData(ImVector<ImDrawList * > * draw_lists,ImDrawData * out_draw_data)4012 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* out_draw_data)
4013 {
4014 	out_draw_data->Valid = true;
4015 	out_draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
4016 	out_draw_data->CmdListsCount = draw_lists->Size;
4017 	out_draw_data->TotalVtxCount = out_draw_data->TotalIdxCount = 0;
4018 	for (int n = 0; n < draw_lists->Size; n++)
4019 	{
4020 		out_draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
4021 		out_draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
4022 	}
4023 }
4024 
4025 // 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)4026 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
4027 {
4028 	ImGuiWindow* window = GetCurrentWindow();
4029 	window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
4030 	window->ClipRect = window->DrawList->_ClipRectStack.back();
4031 }
4032 
PopClipRect()4033 void ImGui::PopClipRect()
4034 {
4035 	ImGuiWindow* window = GetCurrentWindow();
4036 	window->DrawList->PopClipRect();
4037 	window->ClipRect = window->DrawList->_ClipRectStack.back();
4038 }
4039 
4040 // 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()4041 void ImGui::EndFrame()
4042 {
4043 	ImGuiContext& g = *GImGui;
4044 	IM_ASSERT(g.Initialized);               // Forgot to call ImGui::NewFrame()
4045 	if (g.FrameCountEnded == g.FrameCount)  // Don't process EndFrame() multiple times.
4046 		return;
4047 
4048 	// Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
4049 	if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.OsImePosRequest - g.OsImePosSet) > 0.0001f)
4050 	{
4051 		g.IO.ImeSetInputScreenPosFn((int)g.OsImePosRequest.x, (int)g.OsImePosRequest.y);
4052 		g.OsImePosSet = g.OsImePosRequest;
4053 	}
4054 
4055 	// Hide implicit "Debug" window if it hasn't been used
4056 	IM_ASSERT(g.CurrentWindowStack.Size == 1);  // Mismatched Begin()/End() calls
4057 	if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
4058 		g.CurrentWindow->Active = false;
4059 	End();
4060 
4061 	if (g.ActiveId == 0 && g.HoveredId == 0)
4062 	{
4063 		if (!g.NavWindow || !g.NavWindow->Appearing)  // Unless we just made a window/popup appear
4064 		{
4065 			// Click to focus window and start moving (after we're done with all our widgets)
4066 			if (g.IO.MouseClicked[0])
4067 			{
4068 				if (g.HoveredRootWindow != NULL)
4069 				{
4070 					// Set ActiveId even if the _NoMove flag is set, without it dragging away from a window with _NoMove would activate hover on other windows.
4071 					FocusWindow(g.HoveredWindow);
4072 					SetActiveID(g.HoveredWindow->MoveId, g.HoveredWindow);
4073 					g.NavDisableHighlight = true;
4074 					g.ActiveIdClickOffset = g.IO.MousePos - g.HoveredRootWindow->Pos;
4075 					if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove) && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoMove))
4076 						g.MovingWindow = g.HoveredWindow;
4077 				}
4078 				else if (g.NavWindow != NULL && GetFrontMostModalRootWindow() == NULL)
4079 				{
4080 					// Clicking on void disable focus
4081 					FocusWindow(NULL);
4082 				}
4083 			}
4084 
4085 			// With right mouse button we close popups without changing focus
4086 			// (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger)
4087 			if (g.IO.MouseClicked[1])
4088 			{
4089 				// Find the top-most window between HoveredWindow and the front most Modal Window.
4090 				// This is where we can trim the popup stack.
4091 				ImGuiWindow* modal = GetFrontMostModalRootWindow();
4092 				bool hovered_window_above_modal = false;
4093 				if (modal == NULL)
4094 					hovered_window_above_modal = true;
4095 				for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
4096 				{
4097 					ImGuiWindow* window = g.Windows[i];
4098 					if (window == modal)
4099 						break;
4100 					if (window == g.HoveredWindow)
4101 						hovered_window_above_modal = true;
4102 				}
4103 				ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal);
4104 			}
4105 		}
4106 	}
4107 
4108 	// Sort the window list so that all child windows are after their parent
4109 	// We cannot do that on FocusWindow() because childs may not exist yet
4110 	g.WindowsSortBuffer.resize(0);
4111 	g.WindowsSortBuffer.reserve(g.Windows.Size);
4112 	for (int i = 0; i != g.Windows.Size; i++)
4113 	{
4114 		ImGuiWindow* window = g.Windows[i];
4115 		if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))  // if a child is active its parent will add it
4116 			continue;
4117 		AddWindowToSortedBuffer(&g.WindowsSortBuffer, window);
4118 	}
4119 
4120 	IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size);  // we done something wrong
4121 	g.Windows.swap(g.WindowsSortBuffer);
4122 
4123 	// Clear Input data for next frame
4124 	g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
4125 	memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
4126 	memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
4127 
4128 	g.FrameCountEnded = g.FrameCount;
4129 }
4130 
Render()4131 void ImGui::Render()
4132 {
4133 	ImGuiContext& g = *GImGui;
4134 	IM_ASSERT(g.Initialized);  // Forgot to call ImGui::NewFrame()
4135 
4136 	if (g.FrameCountEnded != g.FrameCount)
4137 		ImGui::EndFrame();
4138 	g.FrameCountRendered = g.FrameCount;
4139 
4140 	// Skip render altogether if alpha is 0.0
4141 	// Note that vertex buffers have been created and are wasted, so it is best practice that you don't create windows in the first place, or consistently respond to Begin() returning false.
4142 	if (g.Style.Alpha > 0.0f)
4143 	{
4144 		// Gather windows to render
4145 		g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0;
4146 		g.DrawDataBuilder.Clear();
4147 		ImGuiWindow* window_to_render_front_most = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget : NULL;
4148 		for (int n = 0; n != g.Windows.Size; n++)
4149 		{
4150 			ImGuiWindow* window = g.Windows[n];
4151 			if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0 && window != window_to_render_front_most)
4152 				AddWindowToDrawDataSelectLayer(window);
4153 		}
4154 		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
4155 			AddWindowToDrawDataSelectLayer(window_to_render_front_most);
4156 		g.DrawDataBuilder.FlattenIntoSingleLayer();
4157 
4158 		// Draw software mouse cursor if requested
4159 		ImVec2 offset, size, uv[4];
4160 		if (g.IO.MouseDrawCursor && g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &offset, &size, &uv[0], &uv[2]))
4161 		{
4162 			const ImVec2 pos = g.IO.MousePos - offset;
4163 			const ImTextureID tex_id = g.IO.Fonts->TexID;
4164 			const float sc = g.Style.MouseCursorScale;
4165 			g.OverlayDrawList.PushTextureID(tex_id);
4166 			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
4167 			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
4168 			g.OverlayDrawList.AddImage(tex_id, pos, pos + size * sc, uv[2], uv[3], IM_COL32(0, 0, 0, 255));                                         // Black border
4169 			g.OverlayDrawList.AddImage(tex_id, pos, pos + size * sc, uv[0], uv[1], IM_COL32(255, 255, 255, 255));                                   // White fill
4170 			g.OverlayDrawList.PopTextureID();
4171 		}
4172 		if (!g.OverlayDrawList.VtxBuffer.empty())
4173 			AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.OverlayDrawList);
4174 
4175 		// Setup ImDrawData structure for end-user
4176 		SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
4177 		g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
4178 		g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
4179 
4180 		// Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData()
4181 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4182 		if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
4183 			g.IO.RenderDrawListsFn(&g.DrawData);
4184 #endif
4185 	}
4186 }
4187 
FindRenderedTextEnd(const char * text,const char * text_end)4188 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
4189 {
4190 	const char* text_display_end = text;
4191 	if (!text_end)
4192 		text_end = (const char*)-1;
4193 
4194 	while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
4195 		text_display_end++;
4196 	return text_display_end;
4197 }
4198 
4199 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)4200 void ImGui::LogText(const char* fmt, ...)
4201 {
4202 	ImGuiContext& g = *GImGui;
4203 	if (!g.LogEnabled)
4204 		return;
4205 
4206 	va_list args;
4207 	va_start(args, fmt);
4208 	if (g.LogFile)
4209 	{
4210 		vfprintf(g.LogFile, fmt, args);
4211 	}
4212 	else
4213 	{
4214 		g.LogClipboard->appendfv(fmt, args);
4215 	}
4216 	va_end(args);
4217 }
4218 
4219 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
4220 // 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)4221 static void LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL)
4222 {
4223 	ImGuiContext& g = *GImGui;
4224 	ImGuiWindow* window = g.CurrentWindow;
4225 
4226 	if (!text_end)
4227 		text_end = ImGui::FindRenderedTextEnd(text, text_end);
4228 
4229 	const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1);
4230 	if (ref_pos)
4231 		window->DC.LogLinePosY = ref_pos->y;
4232 
4233 	const char* text_remaining = text;
4234 	if (g.LogStartDepth > window->DC.TreeDepth)  // Re-adjust padding if we have popped out of our starting depth
4235 		g.LogStartDepth = window->DC.TreeDepth;
4236 	const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth);
4237 	for (;;)
4238 	{
4239 		// Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
4240 		const char* line_end = text_remaining;
4241 		while (line_end < text_end)
4242 			if (*line_end == '\n')
4243 				break;
4244 			else
4245 				line_end++;
4246 		if (line_end >= text_end)
4247 			line_end = NULL;
4248 
4249 		const bool is_first_line = (text == text_remaining);
4250 		bool is_last_line = false;
4251 		if (line_end == NULL)
4252 		{
4253 			is_last_line = true;
4254 			line_end = text_end;
4255 		}
4256 		if (line_end != NULL && !(is_last_line && (line_end - text_remaining) == 0))
4257 		{
4258 			const int char_count = (int)(line_end - text_remaining);
4259 			if (log_new_line || !is_first_line)
4260 				ImGui::LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, text_remaining);
4261 			else
4262 				ImGui::LogText(" %.*s", char_count, text_remaining);
4263 		}
4264 
4265 		if (is_last_line)
4266 			break;
4267 		text_remaining = line_end + 1;
4268 	}
4269 }
4270 
4271 // Internal ImGui functions to render text
4272 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)4273 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
4274 {
4275 	ImGuiContext& g = *GImGui;
4276 	ImGuiWindow* window = g.CurrentWindow;
4277 
4278 	// Hide anything after a '##' string
4279 	const char* text_display_end;
4280 	if (hide_text_after_hash)
4281 	{
4282 		text_display_end = FindRenderedTextEnd(text, text_end);
4283 	}
4284 	else
4285 	{
4286 		if (!text_end)
4287 			text_end = text + strlen(text);  // FIXME-OPT
4288 		text_display_end = text_end;
4289 	}
4290 
4291 	const int text_len = (int)(text_display_end - text);
4292 	if (text_len > 0)
4293 	{
4294 		window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
4295 		if (g.LogEnabled)
4296 			LogRenderedText(&pos, text, text_display_end);
4297 	}
4298 }
4299 
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)4300 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
4301 {
4302 	ImGuiContext& g = *GImGui;
4303 	ImGuiWindow* window = g.CurrentWindow;
4304 
4305 	if (!text_end)
4306 		text_end = text + strlen(text);  // FIXME-OPT
4307 
4308 	const int text_len = (int)(text_end - text);
4309 	if (text_len > 0)
4310 	{
4311 		window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
4312 		if (g.LogEnabled)
4313 			LogRenderedText(&pos, text, text_end);
4314 	}
4315 }
4316 
4317 // Default clip_rect uses (pos_min,pos_max)
4318 // 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)4319 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)
4320 {
4321 	// Hide anything after a '##' string
4322 	const char* text_display_end = FindRenderedTextEnd(text, text_end);
4323 	const int text_len = (int)(text_display_end - text);
4324 	if (text_len == 0)
4325 		return;
4326 
4327 	ImGuiContext& g = *GImGui;
4328 	ImGuiWindow* window = g.CurrentWindow;
4329 
4330 	// Perform CPU side clipping for single clipped element to avoid using scissor state
4331 	ImVec2 pos = pos_min;
4332 	const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
4333 
4334 	const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
4335 	const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
4336 	bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
4337 	if (clip_rect)  // If we had no explicit clipping rectangle then pos==clip_min
4338 		need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
4339 
4340 	// Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
4341 	if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
4342 	if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
4343 
4344 	// Render
4345 	if (need_clipping)
4346 	{
4347 		ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
4348 		window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
4349 	}
4350 	else
4351 	{
4352 		window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
4353 	}
4354 	if (g.LogEnabled)
4355 		LogRenderedText(&pos, text, text_display_end);
4356 }
4357 
4358 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)4359 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
4360 {
4361 	ImGuiContext& g = *GImGui;
4362 	ImGuiWindow* window = g.CurrentWindow;
4363 	window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
4364 	const float border_size = g.Style.FrameBorderSize;
4365 	if (border && border_size > 0.0f)
4366 	{
4367 		window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
4368 		window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
4369 	}
4370 }
4371 
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)4372 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
4373 {
4374 	ImGuiContext& g = *GImGui;
4375 	ImGuiWindow* window = g.CurrentWindow;
4376 	const float border_size = g.Style.FrameBorderSize;
4377 	if (border_size > 0.0f)
4378 	{
4379 		window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
4380 		window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
4381 	}
4382 }
4383 
4384 // Render a triangle to denote expanded/collapsed state
RenderTriangle(ImVec2 p_min,ImGuiDir dir,float scale)4385 void ImGui::RenderTriangle(ImVec2 p_min, ImGuiDir dir, float scale)
4386 {
4387 	ImGuiContext& g = *GImGui;
4388 	ImGuiWindow* window = g.CurrentWindow;
4389 
4390 	const float h = g.FontSize * 1.00f;
4391 	float r = h * 0.40f * scale;
4392 	ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale);
4393 
4394 	ImVec2 a, b, c;
4395 	switch (dir)
4396 	{
4397 		case ImGuiDir_Up:
4398 		case ImGuiDir_Down:
4399 			if (dir == ImGuiDir_Up) r = -r;
4400 			center.y -= r * 0.25f;
4401 			a = ImVec2(0, 1) * r;
4402 			b = ImVec2(-0.866f, -0.5f) * r;
4403 			c = ImVec2(+0.866f, -0.5f) * r;
4404 			break;
4405 		case ImGuiDir_Left:
4406 		case ImGuiDir_Right:
4407 			if (dir == ImGuiDir_Left) r = -r;
4408 			center.x -= r * 0.25f;
4409 			a = ImVec2(1, 0) * r;
4410 			b = ImVec2(-0.500f, +0.866f) * r;
4411 			c = ImVec2(-0.500f, -0.866f) * r;
4412 			break;
4413 		case ImGuiDir_None:
4414 		case ImGuiDir_Count_:
4415 			IM_ASSERT(0);
4416 			break;
4417 	}
4418 
4419 	window->DrawList->AddTriangleFilled(center + a, center + b, center + c, GetColorU32(ImGuiCol_Text));
4420 }
4421 
RenderBullet(ImVec2 pos)4422 void ImGui::RenderBullet(ImVec2 pos)
4423 {
4424 	ImGuiContext& g = *GImGui;
4425 	ImGuiWindow* window = g.CurrentWindow;
4426 	window->DrawList->AddCircleFilled(pos, GImGui->FontSize * 0.20f, GetColorU32(ImGuiCol_Text), 8);
4427 }
4428 
RenderCheckMark(ImVec2 pos,ImU32 col,float sz)4429 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
4430 {
4431 	ImGuiContext& g = *GImGui;
4432 	ImGuiWindow* window = g.CurrentWindow;
4433 
4434 	float thickness = ImMax(sz / 5.0f, 1.0f);
4435 	sz -= thickness * 0.5f;
4436 	pos += ImVec2(thickness * 0.25f, thickness * 0.25f);
4437 
4438 	float third = sz / 3.0f;
4439 	float bx = pos.x + third;
4440 	float by = pos.y + sz - third * 0.5f;
4441 	window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
4442 	window->DrawList->PathLineTo(ImVec2(bx, by));
4443 	window->DrawList->PathLineTo(ImVec2(bx + third * 2, by - third * 2));
4444 	window->DrawList->PathStroke(col, false, thickness);
4445 }
4446 
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)4447 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
4448 {
4449 	ImGuiContext& g = *GImGui;
4450 	if (id != g.NavId)
4451 		return;
4452 	if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
4453 		return;
4454 	ImGuiWindow* window = ImGui::GetCurrentWindow();
4455 	if (window->DC.NavHideHighlightOneFrame)
4456 		return;
4457 
4458 	float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
4459 	ImRect display_rect = bb;
4460 	display_rect.ClipWith(window->ClipRect);
4461 	if (flags & ImGuiNavHighlightFlags_TypeDefault)
4462 	{
4463 		const float THICKNESS = 2.0f;
4464 		const float DISTANCE = 3.0f + THICKNESS * 0.5f;
4465 		display_rect.Expand(ImVec2(DISTANCE, DISTANCE));
4466 		bool fully_visible = window->ClipRect.Contains(display_rect);
4467 		if (!fully_visible)
4468 			window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
4469 		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);
4470 		if (!fully_visible)
4471 			window->DrawList->PopClipRect();
4472 	}
4473 	if (flags & ImGuiNavHighlightFlags_TypeThin)
4474 	{
4475 		window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
4476 	}
4477 }
4478 
4479 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4480 // 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)4481 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
4482 {
4483 	ImGuiContext& g = *GImGui;
4484 
4485 	const char* text_display_end;
4486 	if (hide_text_after_double_hash)
4487 		text_display_end = FindRenderedTextEnd(text, text_end);  // Hide anything after a '##' string
4488 	else
4489 		text_display_end = text_end;
4490 
4491 	ImFont* font = g.Font;
4492 	const float font_size = g.FontSize;
4493 	if (text == text_display_end)
4494 		return ImVec2(0.0f, font_size);
4495 	ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
4496 
4497 	// Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field)
4498 	const float font_scale = font_size / font->FontSize;
4499 	const float character_spacing_x = 1.0f * font_scale;
4500 	if (text_size.x > 0.0f)
4501 		text_size.x -= character_spacing_x;
4502 	text_size.x = (float)(int)(text_size.x + 0.95f);
4503 
4504 	return text_size;
4505 }
4506 
4507 // Helper to calculate coarse clipping of large list of evenly sized items.
4508 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
4509 // 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)4510 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
4511 {
4512 	ImGuiContext& g = *GImGui;
4513 	ImGuiWindow* window = g.CurrentWindow;
4514 	if (g.LogEnabled)
4515 	{
4516 		// If logging is active, do not perform any clipping
4517 		*out_items_display_start = 0;
4518 		*out_items_display_end = items_count;
4519 		return;
4520 	}
4521 	if (window->SkipItems)
4522 	{
4523 		*out_items_display_start = *out_items_display_end = 0;
4524 		return;
4525 	}
4526 
4527 	const ImVec2 pos = window->DC.CursorPos;
4528 	int start = (int)((window->ClipRect.Min.y - pos.y) / items_height);
4529 	int end = (int)((window->ClipRect.Max.y - pos.y) / items_height);
4530 	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
4531 		start--;
4532 	if (g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down)
4533 		end++;
4534 
4535 	start = ImClamp(start, 0, items_count);
4536 	end = ImClamp(end + 1, start, items_count);
4537 	*out_items_display_start = start;
4538 	*out_items_display_end = end;
4539 }
4540 
4541 // Find window given position, search front-to-back
4542 // 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()4543 static ImGuiWindow* FindHoveredWindow()
4544 {
4545 	ImGuiContext& g = *GImGui;
4546 	for (int i = g.Windows.Size - 1; i >= 0; i--)
4547 	{
4548 		ImGuiWindow* window = g.Windows[i];
4549 		if (!window->Active)
4550 			continue;
4551 		if (window->Flags & ImGuiWindowFlags_NoInputs)
4552 			continue;
4553 
4554 		// Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4555 		ImRect bb(window->WindowRectClipped.Min - g.Style.TouchExtraPadding, window->WindowRectClipped.Max + g.Style.TouchExtraPadding);
4556 		if (bb.Contains(g.IO.MousePos))
4557 			return window;
4558 	}
4559 	return NULL;
4560 }
4561 
4562 // Test if mouse cursor is hovering given rectangle
4563 // NB- Rectangle is clipped by our current clip setting
4564 // 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)4565 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4566 {
4567 	ImGuiContext& g = *GImGui;
4568 	ImGuiWindow* window = g.CurrentWindow;
4569 
4570 	// Clip
4571 	ImRect rect_clipped(r_min, r_max);
4572 	if (clip)
4573 		rect_clipped.ClipWith(window->ClipRect);
4574 
4575 	// Expand for touch input
4576 	const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4577 	return rect_for_touch.Contains(g.IO.MousePos);
4578 }
4579 
IsKeyPressedMap(ImGuiKey key,bool repeat)4580 static bool IsKeyPressedMap(ImGuiKey key, bool repeat)
4581 {
4582 	const int key_index = GImGui->IO.KeyMap[key];
4583 	return (key_index >= 0) ? ImGui::IsKeyPressed(key_index, repeat) : false;
4584 }
4585 
GetKeyIndex(ImGuiKey imgui_key)4586 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4587 {
4588 	IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4589 	return GImGui->IO.KeyMap[imgui_key];
4590 }
4591 
4592 // 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)4593 bool ImGui::IsKeyDown(int user_key_index)
4594 {
4595 	if (user_key_index < 0) return false;
4596 	IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown));
4597 	return GImGui->IO.KeysDown[user_key_index];
4598 }
4599 
CalcTypematicPressedRepeatAmount(float t,float t_prev,float repeat_delay,float repeat_rate)4600 int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate)
4601 {
4602 	if (t == 0.0f)
4603 		return 1;
4604 	if (t <= repeat_delay || repeat_rate <= 0.0f)
4605 		return 0;
4606 	const int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t_prev - repeat_delay) / repeat_rate);
4607 	return (count > 0) ? count : 0;
4608 }
4609 
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)4610 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4611 {
4612 	ImGuiContext& g = *GImGui;
4613 	if (key_index < 0) return false;
4614 	IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4615 	const float t = g.IO.KeysDownDuration[key_index];
4616 	return CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, repeat_delay, repeat_rate);
4617 }
4618 
IsKeyPressed(int user_key_index,bool repeat)4619 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4620 {
4621 	ImGuiContext& g = *GImGui;
4622 	if (user_key_index < 0) return false;
4623 	IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4624 	const float t = g.IO.KeysDownDuration[user_key_index];
4625 	if (t == 0.0f)
4626 		return true;
4627 	if (repeat && t > g.IO.KeyRepeatDelay)
4628 		return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4629 	return false;
4630 }
4631 
IsKeyReleased(int user_key_index)4632 bool ImGui::IsKeyReleased(int user_key_index)
4633 {
4634 	ImGuiContext& g = *GImGui;
4635 	if (user_key_index < 0) return false;
4636 	IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4637 	return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4638 }
4639 
IsMouseDown(int button)4640 bool ImGui::IsMouseDown(int button)
4641 {
4642 	ImGuiContext& g = *GImGui;
4643 	IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4644 	return g.IO.MouseDown[button];
4645 }
4646 
IsAnyMouseDown()4647 bool ImGui::IsAnyMouseDown()
4648 {
4649 	ImGuiContext& g = *GImGui;
4650 	for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4651 		if (g.IO.MouseDown[n])
4652 			return true;
4653 	return false;
4654 }
4655 
IsMouseClicked(int button,bool repeat)4656 bool ImGui::IsMouseClicked(int button, bool repeat)
4657 {
4658 	ImGuiContext& g = *GImGui;
4659 	IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4660 	const float t = g.IO.MouseDownDuration[button];
4661 	if (t == 0.0f)
4662 		return true;
4663 
4664 	if (repeat && t > g.IO.KeyRepeatDelay)
4665 	{
4666 		float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
4667 		if ((fmodf(t - delay, rate) > rate * 0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate * 0.5f))
4668 			return true;
4669 	}
4670 
4671 	return false;
4672 }
4673 
IsMouseReleased(int button)4674 bool ImGui::IsMouseReleased(int button)
4675 {
4676 	ImGuiContext& g = *GImGui;
4677 	IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4678 	return g.IO.MouseReleased[button];
4679 }
4680 
IsMouseDoubleClicked(int button)4681 bool ImGui::IsMouseDoubleClicked(int button)
4682 {
4683 	ImGuiContext& g = *GImGui;
4684 	IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4685 	return g.IO.MouseDoubleClicked[button];
4686 }
4687 
IsMouseDragging(int button,float lock_threshold)4688 bool ImGui::IsMouseDragging(int button, float lock_threshold)
4689 {
4690 	ImGuiContext& g = *GImGui;
4691 	IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4692 	if (!g.IO.MouseDown[button])
4693 		return false;
4694 	if (lock_threshold < 0.0f)
4695 		lock_threshold = g.IO.MouseDragThreshold;
4696 	return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4697 }
4698 
GetMousePos()4699 ImVec2 ImGui::GetMousePos()
4700 {
4701 	return GImGui->IO.MousePos;
4702 }
4703 
4704 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4705 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4706 {
4707 	ImGuiContext& g = *GImGui;
4708 	if (g.CurrentPopupStack.Size > 0)
4709 		return g.OpenPopupStack[g.CurrentPopupStack.Size - 1].OpenMousePos;
4710 	return g.IO.MousePos;
4711 }
4712 
4713 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position
IsMousePosValid(const ImVec2 * mouse_pos)4714 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4715 {
4716 	if (mouse_pos == NULL)
4717 		mouse_pos = &GImGui->IO.MousePos;
4718 	const float MOUSE_INVALID = -256000.0f;
4719 	return mouse_pos->x >= MOUSE_INVALID && mouse_pos->y >= MOUSE_INVALID;
4720 }
4721 
4722 // 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)4723 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
4724 {
4725 	ImGuiContext& g = *GImGui;
4726 	IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4727 	if (lock_threshold < 0.0f)
4728 		lock_threshold = g.IO.MouseDragThreshold;
4729 	if (g.IO.MouseDown[button])
4730 		if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4731 			return g.IO.MousePos - g.IO.MouseClickedPos[button];  // Assume we can only get active with left-mouse button (at the moment).
4732 	return ImVec2(0.0f, 0.0f);
4733 }
4734 
ResetMouseDragDelta(int button)4735 void ImGui::ResetMouseDragDelta(int button)
4736 {
4737 	ImGuiContext& g = *GImGui;
4738 	IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4739 	// NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4740 	g.IO.MouseClickedPos[button] = g.IO.MousePos;
4741 }
4742 
GetMouseCursor()4743 ImGuiMouseCursor ImGui::GetMouseCursor()
4744 {
4745 	return GImGui->MouseCursor;
4746 }
4747 
SetMouseCursor(ImGuiMouseCursor cursor_type)4748 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4749 {
4750 	GImGui->MouseCursor = cursor_type;
4751 }
4752 
CaptureKeyboardFromApp(bool capture)4753 void ImGui::CaptureKeyboardFromApp(bool capture)
4754 {
4755 	GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4756 }
4757 
CaptureMouseFromApp(bool capture)4758 void ImGui::CaptureMouseFromApp(bool capture)
4759 {
4760 	GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4761 }
4762 
IsItemActive()4763 bool ImGui::IsItemActive()
4764 {
4765 	ImGuiContext& g = *GImGui;
4766 	if (g.ActiveId)
4767 	{
4768 		ImGuiWindow* window = g.CurrentWindow;
4769 		return g.ActiveId == window->DC.LastItemId;
4770 	}
4771 	return false;
4772 }
4773 
IsItemFocused()4774 bool ImGui::IsItemFocused()
4775 {
4776 	ImGuiContext& g = *GImGui;
4777 	return g.NavId && !g.NavDisableHighlight && g.NavId == g.CurrentWindow->DC.LastItemId;
4778 }
4779 
IsItemClicked(int mouse_button)4780 bool ImGui::IsItemClicked(int mouse_button)
4781 {
4782 	return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_Default);
4783 }
4784 
IsAnyItemHovered()4785 bool ImGui::IsAnyItemHovered()
4786 {
4787 	ImGuiContext& g = *GImGui;
4788 	return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4789 }
4790 
IsAnyItemActive()4791 bool ImGui::IsAnyItemActive()
4792 {
4793 	ImGuiContext& g = *GImGui;
4794 	return g.ActiveId != 0;
4795 }
4796 
IsAnyItemFocused()4797 bool ImGui::IsAnyItemFocused()
4798 {
4799 	ImGuiContext& g = *GImGui;
4800 	return g.NavId != 0 && !g.NavDisableHighlight;
4801 }
4802 
IsItemVisible()4803 bool ImGui::IsItemVisible()
4804 {
4805 	ImGuiWindow* window = GetCurrentWindowRead();
4806 	return window->ClipRect.Overlaps(window->DC.LastItemRect);
4807 }
4808 
4809 // 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()4810 void ImGui::SetItemAllowOverlap()
4811 {
4812 	ImGuiContext& g = *GImGui;
4813 	if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
4814 		g.HoveredIdAllowOverlap = true;
4815 	if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
4816 		g.ActiveIdAllowOverlap = true;
4817 }
4818 
GetItemRectMin()4819 ImVec2 ImGui::GetItemRectMin()
4820 {
4821 	ImGuiWindow* window = GetCurrentWindowRead();
4822 	return window->DC.LastItemRect.Min;
4823 }
4824 
GetItemRectMax()4825 ImVec2 ImGui::GetItemRectMax()
4826 {
4827 	ImGuiWindow* window = GetCurrentWindowRead();
4828 	return window->DC.LastItemRect.Max;
4829 }
4830 
GetItemRectSize()4831 ImVec2 ImGui::GetItemRectSize()
4832 {
4833 	ImGuiWindow* window = GetCurrentWindowRead();
4834 	return window->DC.LastItemRect.GetSize();
4835 }
4836 
GetViewportRect()4837 static ImRect GetViewportRect()
4838 {
4839 	ImGuiContext& g = *GImGui;
4840 	if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y)
4841 		return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax);
4842 	return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4843 }
4844 
4845 // 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)4846 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip)
4847 {
4848 	ImGuiContext& g = *GImGui;
4849 	char window_name[16];
4850 	ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
4851 	if (override_previous_tooltip)
4852 		if (ImGuiWindow* window = FindWindowByName(window_name))
4853 			if (window->Active)
4854 			{
4855 				// Hide previous tooltips. We can't easily "reset" the content of a window so we create a new one.
4856 				window->HiddenFrames = 1;
4857 				ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
4858 			}
4859 	ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoNav;
4860 	Begin(window_name, NULL, flags | extra_flags);
4861 }
4862 
SetTooltipV(const char * fmt,va_list args)4863 void ImGui::SetTooltipV(const char* fmt, va_list args)
4864 {
4865 	BeginTooltipEx(0, true);
4866 	TextV(fmt, args);
4867 	EndTooltip();
4868 }
4869 
SetTooltip(const char * fmt,...)4870 void ImGui::SetTooltip(const char* fmt, ...)
4871 {
4872 	va_list args;
4873 	va_start(args, fmt);
4874 	SetTooltipV(fmt, args);
4875 	va_end(args);
4876 }
4877 
BeginTooltip()4878 void ImGui::BeginTooltip()
4879 {
4880 	BeginTooltipEx(0, false);
4881 }
4882 
EndTooltip()4883 void ImGui::EndTooltip()
4884 {
4885 	IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);  // Mismatched BeginTooltip()/EndTooltip() calls
4886 	End();
4887 }
4888 
4889 // Mark popup as open (toggle toward open state).
4890 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
4891 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
4892 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id)4893 void ImGui::OpenPopupEx(ImGuiID id)
4894 {
4895 	ImGuiContext& g = *GImGui;
4896 	ImGuiWindow* parent_window = g.CurrentWindow;
4897 	int current_stack_size = g.CurrentPopupStack.Size;
4898 	ImGuiPopupRef popup_ref;  // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
4899 	popup_ref.PopupId = id;
4900 	popup_ref.Window = NULL;
4901 	popup_ref.ParentWindow = parent_window;
4902 	popup_ref.OpenFrameCount = g.FrameCount;
4903 	popup_ref.OpenParentId = parent_window->IDStack.back();
4904 	popup_ref.OpenMousePos = g.IO.MousePos;
4905 	popup_ref.OpenPopupPos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : g.IO.MousePos;
4906 
4907 	if (g.OpenPopupStack.Size < current_stack_size + 1)
4908 	{
4909 		g.OpenPopupStack.push_back(popup_ref);
4910 	}
4911 	else
4912 	{
4913 		// Close child popups if any
4914 		g.OpenPopupStack.resize(current_stack_size + 1);
4915 
4916 		// 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
4917 		// 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
4918 		// 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.
4919 		if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
4920 			g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
4921 		else
4922 			g.OpenPopupStack[current_stack_size] = popup_ref;
4923 
4924 		// When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
4925 		// This is equivalent to what ClosePopupToLevel() does.
4926 		//if (g.OpenPopupStack[current_stack_size].PopupId == id)
4927 		//    FocusWindow(parent_window);
4928 	}
4929 }
4930 
OpenPopup(const char * str_id)4931 void ImGui::OpenPopup(const char* str_id)
4932 {
4933 	ImGuiContext& g = *GImGui;
4934 	OpenPopupEx(g.CurrentWindow->GetID(str_id));
4935 }
4936 
ClosePopupsOverWindow(ImGuiWindow * ref_window)4937 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window)
4938 {
4939 	ImGuiContext& g = *GImGui;
4940 	if (g.OpenPopupStack.empty())
4941 		return;
4942 
4943 	// When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
4944 	// Don't close our own child popup windows.
4945 	int n = 0;
4946 	if (ref_window)
4947 	{
4948 		for (n = 0; n < g.OpenPopupStack.Size; n++)
4949 		{
4950 			ImGuiPopupRef& popup = g.OpenPopupStack[n];
4951 			if (!popup.Window)
4952 				continue;
4953 			IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
4954 			if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
4955 				continue;
4956 
4957 			// Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow)
4958 			bool has_focus = false;
4959 			for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++)
4960 				has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow);
4961 			if (!has_focus)
4962 				break;
4963 		}
4964 	}
4965 	if (n < g.OpenPopupStack.Size)  // This test is not required but it allows to set a convenient breakpoint on the block below
4966 		ClosePopupToLevel(n);
4967 }
4968 
GetFrontMostModalRootWindow()4969 static ImGuiWindow* GetFrontMostModalRootWindow()
4970 {
4971 	ImGuiContext& g = *GImGui;
4972 	for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
4973 		if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
4974 			if (popup->Flags & ImGuiWindowFlags_Modal)
4975 				return popup;
4976 	return NULL;
4977 }
4978 
ClosePopupToLevel(int remaining)4979 static void ClosePopupToLevel(int remaining)
4980 {
4981 	IM_ASSERT(remaining >= 0);
4982 	ImGuiContext& g = *GImGui;
4983 	ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining - 1].Window : g.OpenPopupStack[0].ParentWindow;
4984 	if (g.NavLayer == 0)
4985 		focus_window = NavRestoreLastChildNavWindow(focus_window);
4986 	ImGui::FocusWindow(focus_window);
4987 	focus_window->DC.NavHideHighlightOneFrame = true;
4988 	g.OpenPopupStack.resize(remaining);
4989 }
4990 
ClosePopup(ImGuiID id)4991 void ImGui::ClosePopup(ImGuiID id)
4992 {
4993 	if (!IsPopupOpen(id))
4994 		return;
4995 	ImGuiContext& g = *GImGui;
4996 	ClosePopupToLevel(g.OpenPopupStack.Size - 1);
4997 }
4998 
4999 // Close the popup we have begin-ed into.
CloseCurrentPopup()5000 void ImGui::CloseCurrentPopup()
5001 {
5002 	ImGuiContext& g = *GImGui;
5003 	int popup_idx = g.CurrentPopupStack.Size - 1;
5004 	if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
5005 		return;
5006 	while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu))
5007 		popup_idx--;
5008 	ClosePopupToLevel(popup_idx);
5009 }
5010 
BeginPopupEx(ImGuiID id,ImGuiWindowFlags extra_flags)5011 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
5012 {
5013 	ImGuiContext& g = *GImGui;
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 	char name[20];
5021 	if (extra_flags & ImGuiWindowFlags_ChildMenu)
5022 		ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.CurrentPopupStack.Size);  // Recycle windows based on depth
5023 	else
5024 		ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id);  // Not recycling, so we can close/open during the same frame
5025 
5026 	bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup);
5027 	if (!is_open)  // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
5028 		EndPopup();
5029 
5030 	return is_open;
5031 }
5032 
BeginPopup(const char * str_id,ImGuiWindowFlags flags)5033 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
5034 {
5035 	ImGuiContext& g = *GImGui;
5036 	if (g.OpenPopupStack.Size <= g.CurrentPopupStack.Size)  // Early out for performance
5037 	{
5038 		g.NextWindowData.Clear();  // We behave like Begin() and need to consume those values
5039 		return false;
5040 	}
5041 	return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
5042 }
5043 
IsPopupOpen(ImGuiID id)5044 bool ImGui::IsPopupOpen(ImGuiID id)
5045 {
5046 	ImGuiContext& g = *GImGui;
5047 	return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id;
5048 }
5049 
IsPopupOpen(const char * str_id)5050 bool ImGui::IsPopupOpen(const char* str_id)
5051 {
5052 	ImGuiContext& g = *GImGui;
5053 	return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
5054 }
5055 
BeginPopupModal(const char * name,bool * p_open,ImGuiWindowFlags flags)5056 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
5057 {
5058 	ImGuiContext& g = *GImGui;
5059 	ImGuiWindow* window = g.CurrentWindow;
5060 	const ImGuiID id = window->GetID(name);
5061 	if (!IsPopupOpen(id))
5062 	{
5063 		g.NextWindowData.Clear();  // We behave like Begin() and need to consume those values
5064 		return false;
5065 	}
5066 
5067 	// Center modal windows by default
5068 	// FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
5069 	if (g.NextWindowData.PosCond == 0)
5070 		SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
5071 
5072 	bool is_open = Begin(name, p_open, flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings);
5073 	if (!is_open || (p_open && !*p_open))  // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
5074 	{
5075 		EndPopup();
5076 		if (is_open)
5077 			ClosePopup(id);
5078 		return false;
5079 	}
5080 
5081 	return is_open;
5082 }
5083 
NavProcessMoveRequestWrapAround(ImGuiWindow * window)5084 static void NavProcessMoveRequestWrapAround(ImGuiWindow* window)
5085 {
5086 	ImGuiContext& g = *GImGui;
5087 	if (g.NavWindow == window && NavMoveRequestButNoResultYet())
5088 		if ((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == 0)
5089 		{
5090 			g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
5091 			ImGui::NavMoveRequestCancel();
5092 			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;
5093 		}
5094 }
5095 
EndPopup()5096 void ImGui::EndPopup()
5097 {
5098 	ImGuiContext& g = *GImGui;
5099 	(void)g;
5100 	IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
5101 	IM_ASSERT(g.CurrentPopupStack.Size > 0);
5102 
5103 	// Make all menus and popups wrap around for now, may need to expose that policy.
5104 	NavProcessMoveRequestWrapAround(g.CurrentWindow);
5105 
5106 	End();
5107 }
5108 
OpenPopupOnItemClick(const char * str_id,int mouse_button)5109 bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
5110 {
5111 	ImGuiWindow* window = GImGui->CurrentWindow;
5112 	if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
5113 	{
5114 		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!
5115 		IM_ASSERT(id != 0);                                                   // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
5116 		OpenPopupEx(id);
5117 		return true;
5118 	}
5119 	return false;
5120 }
5121 
5122 // This is a helper to handle the simplest case of associating one named popup to one given widget.
5123 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
5124 // You can pass a NULL str_id to use the identifier of the last item.
BeginPopupContextItem(const char * str_id,int mouse_button)5125 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
5126 {
5127 	ImGuiWindow* window = GImGui->CurrentWindow;
5128 	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!
5129 	IM_ASSERT(id != 0);                                                   // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
5130 	if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
5131 		OpenPopupEx(id);
5132 	return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
5133 }
5134 
BeginPopupContextWindow(const char * str_id,int mouse_button,bool also_over_items)5135 bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
5136 {
5137 	if (!str_id)
5138 		str_id = "window_context";
5139 	ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
5140 	if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
5141 		if (also_over_items || !IsAnyItemHovered())
5142 			OpenPopupEx(id);
5143 	return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
5144 }
5145 
BeginPopupContextVoid(const char * str_id,int mouse_button)5146 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
5147 {
5148 	if (!str_id)
5149 		str_id = "void_context";
5150 	ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
5151 	if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
5152 		OpenPopupEx(id);
5153 	return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
5154 }
5155 
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)5156 static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5157 {
5158 	ImGuiContext& g = *GImGui;
5159 	ImGuiWindow* parent_window = ImGui::GetCurrentWindow();
5160 	ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow;
5161 	flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove);  // Inherit the NoMove flag
5162 
5163 	const ImVec2 content_avail = ImGui::GetContentRegionAvail();
5164 	ImVec2 size = ImFloor(size_arg);
5165 	const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
5166 	if (size.x <= 0.0f)
5167 		size.x = ImMax(content_avail.x + size.x, 4.0f);  // Arbitrary minimum child size (0.0f causing too much issues)
5168 	if (size.y <= 0.0f)
5169 		size.y = ImMax(content_avail.y + size.y, 4.0f);
5170 
5171 	const float backup_border_size = g.Style.ChildBorderSize;
5172 	if (!border)
5173 		g.Style.ChildBorderSize = 0.0f;
5174 	flags |= extra_flags;
5175 
5176 	char title[256];
5177 	if (name)
5178 		ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
5179 	else
5180 		ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
5181 
5182 	ImGui::SetNextWindowSize(size);
5183 	bool ret = ImGui::Begin(title, NULL, flags);
5184 	ImGuiWindow* child_window = ImGui::GetCurrentWindow();
5185 	child_window->ChildId = id;
5186 	child_window->AutoFitChildAxises = auto_fit_axises;
5187 	g.Style.ChildBorderSize = backup_border_size;
5188 
5189 	// Process navigation-in immediately so NavInit can run on first frame
5190 	if (!(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll) && g.NavActivateId == id)
5191 	{
5192 		ImGui::FocusWindow(child_window);
5193 		ImGui::NavInitWindow(child_window, false);
5194 		ImGui::SetActiveID(id + 1, child_window);  // Steal ActiveId with a dummy id so that key-press won't activate child item
5195 		g.ActiveIdSource = ImGuiInputSource_Nav;
5196 	}
5197 
5198 	return ret;
5199 }
5200 
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)5201 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5202 {
5203 	ImGuiWindow* window = GetCurrentWindow();
5204 	return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
5205 }
5206 
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)5207 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5208 {
5209 	IM_ASSERT(id != 0);
5210 	return BeginChildEx(NULL, id, size_arg, border, extra_flags);
5211 }
5212 
EndChild()5213 void ImGui::EndChild()
5214 {
5215 	ImGuiContext& g = *GImGui;
5216 	ImGuiWindow* window = g.CurrentWindow;
5217 
5218 	IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);  // Mismatched BeginChild()/EndChild() callss
5219 	if (window->BeginCount > 1)
5220 	{
5221 		End();
5222 	}
5223 	else
5224 	{
5225 		// 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.
5226 		ImVec2 sz = GetWindowSize();
5227 		if (window->AutoFitChildAxises & (1 << ImGuiAxis_X))  // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
5228 			sz.x = ImMax(4.0f, sz.x);
5229 		if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
5230 			sz.y = ImMax(4.0f, sz.y);
5231 		End();
5232 
5233 		ImGuiWindow* parent_window = g.CurrentWindow;
5234 		ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
5235 		ItemSize(sz);
5236 		if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
5237 		{
5238 			ItemAdd(bb, window->ChildId);
5239 			RenderNavHighlight(bb, window->ChildId);
5240 
5241 			// When browsing a window that has no activable items (scroll only) we keep a highlight on the child
5242 			if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
5243 				RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
5244 		}
5245 		else
5246 		{
5247 			// Not navigable into
5248 			ItemAdd(bb, 0);
5249 		}
5250 	}
5251 }
5252 
5253 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)5254 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
5255 {
5256 	ImGuiContext& g = *GImGui;
5257 	const ImGuiStyle& style = g.Style;
5258 	PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
5259 	PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
5260 	PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
5261 	PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
5262 	return BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
5263 }
5264 
EndChildFrame()5265 void ImGui::EndChildFrame()
5266 {
5267 	EndChild();
5268 	PopStyleVar(3);
5269 	PopStyleColor();
5270 }
5271 
5272 // Save and compare stack sizes on Begin()/End() to detect usage errors
CheckStacksSize(ImGuiWindow * window,bool write)5273 static void CheckStacksSize(ImGuiWindow* window, bool write)
5274 {
5275 	// 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)
5276 	ImGuiContext& g = *GImGui;
5277 	int* p_backup = &window->DC.StackSizesBackup[0];
5278 	{
5279 		int current = window->IDStack.Size;
5280 		if (write)
5281 			*p_backup = current;
5282 		else
5283 			IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!");
5284 		p_backup++;
5285 	}  // Too few or too many PopID()/TreePop()
5286 	{
5287 		int current = window->DC.GroupStack.Size;
5288 		if (write)
5289 			*p_backup = current;
5290 		else
5291 			IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!");
5292 		p_backup++;
5293 	}  // Too few or too many EndGroup()
5294 	{
5295 		int current = g.CurrentPopupStack.Size;
5296 		if (write)
5297 			*p_backup = current;
5298 		else
5299 			IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch");
5300 		p_backup++;
5301 	}  // Too few or too many EndMenu()/EndPopup()
5302 	{
5303 		int current = g.ColorModifiers.Size;
5304 		if (write)
5305 			*p_backup = current;
5306 		else
5307 			IM_ASSERT(*p_backup == current && "PushStyleColor/PopStyleColor Mismatch!");
5308 		p_backup++;
5309 	}  // Too few or too many PopStyleColor()
5310 	{
5311 		int current = g.StyleModifiers.Size;
5312 		if (write)
5313 			*p_backup = current;
5314 		else
5315 			IM_ASSERT(*p_backup == current && "PushStyleVar/PopStyleVar Mismatch!");
5316 		p_backup++;
5317 	}  // Too few or too many PopStyleVar()
5318 	{
5319 		int current = g.FontStack.Size;
5320 		if (write)
5321 			*p_backup = current;
5322 		else
5323 			IM_ASSERT(*p_backup == current && "PushFont/PopFont Mismatch!");
5324 		p_backup++;
5325 	}  // Too few or too many PopFont()
5326 	IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
5327 }
5328 
5329 enum ImGuiPopupPositionPolicy
5330 {
5331 	ImGuiPopupPositionPolicy_Default,
5332 	ImGuiPopupPositionPolicy_ComboBox
5333 };
5334 
FindBestWindowPosForPopup(const ImVec2 & ref_pos,const ImVec2 & size,ImGuiDir * last_dir,const ImRect & r_avoid,ImGuiPopupPositionPolicy policy=ImGuiPopupPositionPolicy_Default)5335 static ImVec2 FindBestWindowPosForPopup(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy = ImGuiPopupPositionPolicy_Default)
5336 {
5337 	const ImGuiStyle& style = GImGui->Style;
5338 
5339 	// 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.)
5340 	// 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.
5341 	ImVec2 safe_padding = style.DisplaySafeAreaPadding;
5342 	ImRect r_outer(GetViewportRect());
5343 	r_outer.Expand(ImVec2((size.x - r_outer.GetWidth() > safe_padding.x * 2) ? -safe_padding.x : 0.0f, (size.y - r_outer.GetHeight() > safe_padding.y * 2) ? -safe_padding.y : 0.0f));
5344 	ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
5345 	//GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
5346 	//GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
5347 
5348 	// Combo Box policy (we want a connecting edge)
5349 	if (policy == ImGuiPopupPositionPolicy_ComboBox)
5350 	{
5351 		const ImGuiDir dir_prefered_order[ImGuiDir_Count_] = {ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up};
5352 		for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_Count_; n++)
5353 		{
5354 			const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
5355 			if (n != -1 && dir == *last_dir)  // Already tried this direction?
5356 				continue;
5357 			ImVec2 pos;
5358 			if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);                  // Below, Toward Right (default)
5359 			if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y);        // Above, Toward Right
5360 			if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y);         // Below, Toward Left
5361 			if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y);  // Above, Toward Left
5362 			if (!r_outer.Contains(ImRect(pos, pos + size)))
5363 				continue;
5364 			*last_dir = dir;
5365 			return pos;
5366 		}
5367 	}
5368 
5369 	// Default popup policy
5370 	const ImGuiDir dir_prefered_order[ImGuiDir_Count_] = {ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left};
5371 	for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_Count_; n++)
5372 	{
5373 		const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
5374 		if (n != -1 && dir == *last_dir)  // Already tried this direction?
5375 			continue;
5376 		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);
5377 		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);
5378 		if (avail_w < size.x || avail_h < size.y)
5379 			continue;
5380 		ImVec2 pos;
5381 		pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
5382 		pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
5383 		*last_dir = dir;
5384 		return pos;
5385 	}
5386 
5387 	// Fallback, try to keep within display
5388 	*last_dir = ImGuiDir_None;
5389 	ImVec2 pos = ref_pos;
5390 	pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
5391 	pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
5392 	return pos;
5393 }
5394 
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)5395 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
5396 {
5397 	window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
5398 	window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
5399 	window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
5400 }
5401 
FindWindowByName(const char * name)5402 ImGuiWindow* ImGui::FindWindowByName(const char* name)
5403 {
5404 	ImGuiContext& g = *GImGui;
5405 	ImGuiID id = ImHash(name, 0);
5406 	return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
5407 }
5408 
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)5409 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
5410 {
5411 	ImGuiContext& g = *GImGui;
5412 
5413 	// Create window the first time
5414 	ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
5415 	window->Flags = flags;
5416 	g.WindowsById.SetVoidPtr(window->ID, window);
5417 
5418 	// User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
5419 	if (!(flags & ImGuiWindowFlags_NoSavedSettings))
5420 	{
5421 		// Retrieve settings from .ini file
5422 		// Use SetWindowPos() or SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
5423 		window->Pos = window->PosFloat = ImVec2(60, 60);
5424 
5425 		if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
5426 		{
5427 			SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
5428 			window->PosFloat = settings->Pos;
5429 			window->Pos = ImFloor(window->PosFloat);
5430 			window->Collapsed = settings->Collapsed;
5431 			if (ImLengthSqr(settings->Size) > 0.00001f)
5432 				size = settings->Size;
5433 		}
5434 	}
5435 	window->Size = window->SizeFull = window->SizeFullAtLastBegin = size;
5436 
5437 	if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
5438 	{
5439 		window->AutoFitFramesX = window->AutoFitFramesY = 2;
5440 		window->AutoFitOnlyGrows = false;
5441 	}
5442 	else
5443 	{
5444 		if (window->Size.x <= 0.0f)
5445 			window->AutoFitFramesX = 2;
5446 		if (window->Size.y <= 0.0f)
5447 			window->AutoFitFramesY = 2;
5448 		window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
5449 	}
5450 
5451 	if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
5452 		g.Windows.insert(g.Windows.begin(), window);  // Quite slow but rare and only once
5453 	else
5454 		g.Windows.push_back(window);
5455 	return window;
5456 }
5457 
CalcSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)5458 static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
5459 {
5460 	ImGuiContext& g = *GImGui;
5461 	if (g.NextWindowData.SizeConstraintCond != 0)
5462 	{
5463 		// Using -1,-1 on either X/Y axis to preserve the current size.
5464 		ImRect cr = g.NextWindowData.SizeConstraintRect;
5465 		new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
5466 		new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
5467 		if (g.NextWindowData.SizeCallback)
5468 		{
5469 			ImGuiSizeCallbackData data;
5470 			data.UserData = g.NextWindowData.SizeCallbackUserData;
5471 			data.Pos = window->Pos;
5472 			data.CurrentSize = window->SizeFull;
5473 			data.DesiredSize = new_size;
5474 			g.NextWindowData.SizeCallback(&data);
5475 			new_size = data.DesiredSize;
5476 		}
5477 	}
5478 
5479 	// Minimum size
5480 	if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
5481 	{
5482 		new_size = ImMax(new_size, g.Style.WindowMinSize);
5483 		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
5484 	}
5485 	return new_size;
5486 }
5487 
CalcSizeContents(ImGuiWindow * window)5488 static ImVec2 CalcSizeContents(ImGuiWindow* window)
5489 {
5490 	ImVec2 sz;
5491 	sz.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : (window->DC.CursorMaxPos.x - window->Pos.x + window->Scroll.x));
5492 	sz.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : (window->DC.CursorMaxPos.y - window->Pos.y + window->Scroll.y));
5493 	return sz + window->WindowPadding;
5494 }
5495 
CalcSizeAutoFit(ImGuiWindow * window,const ImVec2 & size_contents)5496 static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents)
5497 {
5498 	ImGuiContext& g = *GImGui;
5499 	ImGuiStyle& style = g.Style;
5500 	ImGuiWindowFlags flags = window->Flags;
5501 	ImVec2 size_auto_fit;
5502 	if ((flags & ImGuiWindowFlags_Tooltip) != 0)
5503 	{
5504 		// Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose.
5505 		size_auto_fit = size_contents;
5506 	}
5507 	else
5508 	{
5509 		// 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.
5510 		size_auto_fit = ImClamp(size_contents, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding));
5511 		ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit);
5512 		if (size_auto_fit_after_constraint.x < size_contents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar))
5513 			size_auto_fit.y += style.ScrollbarSize;
5514 		if (size_auto_fit_after_constraint.y < size_contents.y && !(flags & ImGuiWindowFlags_NoScrollbar))
5515 			size_auto_fit.x += style.ScrollbarSize;
5516 	}
5517 	return size_auto_fit;
5518 }
5519 
GetScrollMaxX(ImGuiWindow * window)5520 static float GetScrollMaxX(ImGuiWindow* window)
5521 {
5522 	return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x));
5523 }
5524 
GetScrollMaxY(ImGuiWindow * window)5525 static float GetScrollMaxY(ImGuiWindow* window)
5526 {
5527 	return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y));
5528 }
5529 
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window)5530 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
5531 {
5532 	ImVec2 scroll = window->Scroll;
5533 	float cr_x = window->ScrollTargetCenterRatio.x;
5534 	float cr_y = window->ScrollTargetCenterRatio.y;
5535 	if (window->ScrollTarget.x < FLT_MAX)
5536 		scroll.x = window->ScrollTarget.x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
5537 	if (window->ScrollTarget.y < FLT_MAX)
5538 		scroll.y = window->ScrollTarget.y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y);
5539 	scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
5540 	if (!window->Collapsed && !window->SkipItems)
5541 	{
5542 		scroll.x = ImMin(scroll.x, GetScrollMaxX(window));
5543 		scroll.y = ImMin(scroll.y, GetScrollMaxY(window));
5544 	}
5545 	return scroll;
5546 }
5547 
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)5548 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
5549 {
5550 	if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
5551 		return ImGuiCol_PopupBg;
5552 	if (flags & ImGuiWindowFlags_ChildWindow)
5553 		return ImGuiCol_ChildBg;
5554 	return ImGuiCol_WindowBg;
5555 }
5556 
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)5557 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
5558 {
5559 	ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                 // Expected window upper-left
5560 	ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm);  // Expected window lower-right
5561 	ImVec2 size_expected = pos_max - pos_min;
5562 	ImVec2 size_constrained = CalcSizeAfterConstraint(window, size_expected);
5563 	*out_pos = pos_min;
5564 	if (corner_norm.x == 0.0f)
5565 		out_pos->x -= (size_constrained.x - size_expected.x);
5566 	if (corner_norm.y == 0.0f)
5567 		out_pos->y -= (size_constrained.y - size_expected.y);
5568 	*out_size = size_constrained;
5569 }
5570 
5571 struct ImGuiResizeGripDef
5572 {
5573 	ImVec2 CornerPos;
5574 	ImVec2 InnerDir;
5575 	int AngleMin12, AngleMax12;
5576 };
5577 
5578 const ImGuiResizeGripDef resize_grip_def[4] =
5579 	{
5580 		{ImVec2(1, 1), ImVec2(-1, -1), 0, 3},   // Lower right
5581 		{ImVec2(0, 1), ImVec2(+1, -1), 3, 6},   // Lower left
5582 		{ImVec2(0, 0), ImVec2(+1, +1), 6, 9},   // Upper left
5583 		{ImVec2(1, 0), ImVec2(-1, +1), 9, 12},  // Upper right
5584 };
5585 
GetBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)5586 static ImRect GetBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
5587 {
5588 	ImRect rect = window->Rect();
5589 	if (thickness == 0.0f) rect.Max -= ImVec2(1, 1);
5590 	if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y, rect.Max.x - perp_padding, rect.Min.y + thickness);
5591 	if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x, rect.Max.y - perp_padding);
5592 	if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y);
5593 	if (border_n == 3) return ImRect(rect.Min.x, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding);
5594 	IM_ASSERT(0);
5595 	return ImRect();
5596 }
5597 
5598 // 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])5599 static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4])
5600 {
5601 	ImGuiContext& g = *GImGui;
5602 	ImGuiWindowFlags flags = window->Flags;
5603 	if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5604 		return;
5605 
5606 	const int resize_border_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 4 : 0;
5607 	const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
5608 	const float grip_hover_size = (float)(int)(grip_draw_size * 0.75f);
5609 
5610 	ImVec2 pos_target(FLT_MAX, FLT_MAX);
5611 	ImVec2 size_target(FLT_MAX, FLT_MAX);
5612 
5613 	// Manual resize grips
5614 	PushID("#RESIZE");
5615 	for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5616 	{
5617 		const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5618 		const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos);
5619 
5620 		// Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
5621 		ImRect resize_rect(corner, corner + grip.InnerDir * grip_hover_size);
5622 		resize_rect.FixInverted();
5623 		bool hovered, held;
5624 		ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
5625 		if (hovered || held)
5626 			g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
5627 
5628 		if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
5629 		{
5630 			// Manual auto-fit when double-clicking
5631 			size_target = CalcSizeAfterConstraint(window, size_auto_fit);
5632 			ClearActiveID();
5633 		}
5634 		else if (held)
5635 		{
5636 			// Resize from any of the four corners
5637 			// We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
5638 			ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerPos;  // Corner of the window corresponding to our corner grip
5639 			CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target);
5640 		}
5641 		if (resize_grip_n == 0 || held || hovered)
5642 			resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
5643 	}
5644 	for (int border_n = 0; border_n < resize_border_count; border_n++)
5645 	{
5646 		const float BORDER_SIZE = 5.0f;           // FIXME: Only works _inside_ window because of HoveredWindow check.
5647 		const float BORDER_APPEAR_TIMER = 0.05f;  // Reduce visual noise
5648 		bool hovered, held;
5649 		ImRect border_rect = GetBorderRect(window, border_n, grip_hover_size, BORDER_SIZE);
5650 		ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
5651 		if ((hovered && g.HoveredIdTimer > BORDER_APPEAR_TIMER) || held)
5652 		{
5653 			g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
5654 			if (held) *border_held = border_n;
5655 		}
5656 		if (held)
5657 		{
5658 			ImVec2 border_target = window->Pos;
5659 			ImVec2 border_posn;
5660 			if (border_n == 0)
5661 			{
5662 				border_posn = ImVec2(0, 0);
5663 				border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y);
5664 			}
5665 			if (border_n == 1)
5666 			{
5667 				border_posn = ImVec2(1, 0);
5668 				border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + BORDER_SIZE);
5669 			}
5670 			if (border_n == 2)
5671 			{
5672 				border_posn = ImVec2(0, 1);
5673 				border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + BORDER_SIZE);
5674 			}
5675 			if (border_n == 3)
5676 			{
5677 				border_posn = ImVec2(0, 0);
5678 				border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x);
5679 			}
5680 			CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
5681 		}
5682 	}
5683 	PopID();
5684 
5685 	// Navigation/gamepad resize
5686 	if (g.NavWindowingTarget == window)
5687 	{
5688 		ImVec2 nav_resize_delta;
5689 		if (g.NavWindowingInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
5690 			nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
5691 		if (g.NavWindowingInputSource == ImGuiInputSource_NavGamepad)
5692 			nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
5693 		if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
5694 		{
5695 			const float NAV_RESIZE_SPEED = 600.0f;
5696 			nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
5697 			g.NavWindowingToggleLayer = false;
5698 			g.NavDisableMouseHover = true;
5699 			resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
5700 			// FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
5701 			size_target = CalcSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
5702 		}
5703 	}
5704 
5705 	// Apply back modified position/size to window
5706 	if (size_target.x != FLT_MAX)
5707 	{
5708 		window->SizeFull = size_target;
5709 		MarkIniSettingsDirty(window);
5710 	}
5711 	if (pos_target.x != FLT_MAX)
5712 	{
5713 		window->Pos = window->PosFloat = ImFloor(pos_target);
5714 		MarkIniSettingsDirty(window);
5715 	}
5716 
5717 	window->Size = window->SizeFull;
5718 }
5719 
5720 // Push a new ImGui window to add widgets to.
5721 // - 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.
5722 // - Begin/End can be called multiple times during the frame with the same window name to append content.
5723 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
5724 //   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.
5725 // - 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.
5726 // - 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)5727 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
5728 {
5729 	ImGuiContext& g = *GImGui;
5730 	const ImGuiStyle& style = g.Style;
5731 	IM_ASSERT(name != NULL);                       // Window name required
5732 	IM_ASSERT(g.Initialized);                      // Forgot to call ImGui::NewFrame()
5733 	IM_ASSERT(g.FrameCountEnded != g.FrameCount);  // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
5734 
5735 	// Find or create
5736 	ImGuiWindow* window = FindWindowByName(name);
5737 	if (!window)
5738 	{
5739 		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.
5740 		window = CreateNewWindow(name, size_on_first_use, flags);
5741 	}
5742 
5743 	// Automatically disable manual moving/resizing when NoInputs is set
5744 	if (flags & ImGuiWindowFlags_NoInputs)
5745 		flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
5746 
5747 	if (flags & ImGuiWindowFlags_NavFlattened)
5748 		IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
5749 
5750 	const int current_frame = g.FrameCount;
5751 	const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
5752 	if (first_begin_of_the_frame)
5753 		window->Flags = (ImGuiWindowFlags)flags;
5754 	else
5755 		flags = window->Flags;
5756 
5757 	// Update the Appearing flag
5758 	bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1);  // Not using !WasActive because the implicit "Debug" window would always toggle off->on
5759 	const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames == 1);
5760 	if (flags & ImGuiWindowFlags_Popup)
5761 	{
5762 		ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
5763 		window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId);  // We recycle popups so treat window as activated if popup id changed
5764 		window_just_activated_by_user |= (window != popup_ref.Window);
5765 	}
5766 	window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
5767 	window->CloseButton = (p_open != NULL);
5768 	if (window->Appearing)
5769 		SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
5770 
5771 	// 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
5772 	ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
5773 	ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
5774 	IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
5775 
5776 	// Add to stack
5777 	g.CurrentWindowStack.push_back(window);
5778 	SetCurrentWindow(window);
5779 	CheckStacksSize(window, true);
5780 	if (flags & ImGuiWindowFlags_Popup)
5781 	{
5782 		ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
5783 		popup_ref.Window = window;
5784 		g.CurrentPopupStack.push_back(popup_ref);
5785 		window->PopupId = popup_ref.PopupId;
5786 	}
5787 
5788 	if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
5789 		window->NavLastIds[0] = 0;
5790 
5791 	// Process SetNextWindow***() calls
5792 	bool window_pos_set_by_api = false;
5793 	bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
5794 	if (g.NextWindowData.PosCond)
5795 	{
5796 		window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
5797 		if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
5798 		{
5799 			// May be processed on the next frame if this is our first frame and we are measuring size
5800 			// FIXME: Look into removing the branch so everything can go through this same code path for consistency.
5801 			window->SetWindowPosVal = g.NextWindowData.PosVal;
5802 			window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
5803 			window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5804 		}
5805 		else
5806 		{
5807 			SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
5808 		}
5809 		g.NextWindowData.PosCond = 0;
5810 	}
5811 	if (g.NextWindowData.SizeCond)
5812 	{
5813 		window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
5814 		window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
5815 		SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
5816 		g.NextWindowData.SizeCond = 0;
5817 	}
5818 	if (g.NextWindowData.ContentSizeCond)
5819 	{
5820 		// Adjust passed "client size" to become a "window size"
5821 		window->SizeContentsExplicit = g.NextWindowData.ContentSizeVal;
5822 		if (window->SizeContentsExplicit.y != 0.0f)
5823 			window->SizeContentsExplicit.y += window->TitleBarHeight() + window->MenuBarHeight();
5824 		g.NextWindowData.ContentSizeCond = 0;
5825 	}
5826 	else if (first_begin_of_the_frame)
5827 	{
5828 		window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
5829 	}
5830 	if (g.NextWindowData.CollapsedCond)
5831 	{
5832 		SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
5833 		g.NextWindowData.CollapsedCond = 0;
5834 	}
5835 	if (g.NextWindowData.FocusCond)
5836 	{
5837 		SetWindowFocus();
5838 		g.NextWindowData.FocusCond = 0;
5839 	}
5840 	if (window->Appearing)
5841 		SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
5842 
5843 	// When reusing window again multiple times a frame, just append content (don't need to setup again)
5844 	if (first_begin_of_the_frame)
5845 	{
5846 		const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip);  // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
5847 
5848 		// Initialize
5849 		window->ParentWindow = parent_window;
5850 		window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForTabbing = window->RootWindowForNav = window;
5851 		if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !window_is_child_tooltip)
5852 			window->RootWindow = parent_window->RootWindow;
5853 		if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
5854 			window->RootWindowForTitleBarHighlight = window->RootWindowForTabbing = parent_window->RootWindowForTitleBarHighlight;  // Same value in master branch, will differ for docking
5855 		while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
5856 			window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
5857 
5858 		window->Active = true;
5859 		window->BeginOrderWithinParent = 0;
5860 		window->BeginOrderWithinContext = g.WindowsActiveCount++;
5861 		window->BeginCount = 0;
5862 		window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
5863 		window->LastFrameActive = current_frame;
5864 		window->IDStack.resize(1);
5865 
5866 		// Lock window rounding, border size and rounding so that altering the border sizes for children doesn't have side-effects.
5867 		window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
5868 		window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
5869 		window->WindowPadding = style.WindowPadding;
5870 		if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
5871 			window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
5872 
5873 		// Collapse window by double-clicking on title bar
5874 		// 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
5875 		if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
5876 		{
5877 			ImRect title_bar_rect = window->TitleBarRect();
5878 			if (window->CollapseToggleWanted || (g.HoveredWindow == window && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0]))
5879 			{
5880 				window->Collapsed = !window->Collapsed;
5881 				MarkIniSettingsDirty(window);
5882 				FocusWindow(window);
5883 			}
5884 		}
5885 		else
5886 		{
5887 			window->Collapsed = false;
5888 		}
5889 		window->CollapseToggleWanted = false;
5890 
5891 		// SIZE
5892 
5893 		// Update contents size from last frame for auto-fitting (unless explicitly specified)
5894 		window->SizeContents = CalcSizeContents(window);
5895 
5896 		// Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
5897 		if (window->HiddenFrames > 0)
5898 			window->HiddenFrames--;
5899 		if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && window_just_activated_by_user)
5900 		{
5901 			window->HiddenFrames = 1;
5902 			if (flags & ImGuiWindowFlags_AlwaysAutoResize)
5903 			{
5904 				if (!window_size_x_set_by_api)
5905 					window->Size.x = window->SizeFull.x = 0.f;
5906 				if (!window_size_y_set_by_api)
5907 					window->Size.y = window->SizeFull.y = 0.f;
5908 				window->SizeContents = ImVec2(0.f, 0.f);
5909 			}
5910 		}
5911 
5912 		// Calculate auto-fit size, handle automatic resize
5913 		const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->SizeContents);
5914 		ImVec2 size_full_modified(FLT_MAX, FLT_MAX);
5915 		if (flags & ImGuiWindowFlags_AlwaysAutoResize && !window->Collapsed)
5916 		{
5917 			// Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
5918 			if (!window_size_x_set_by_api)
5919 				window->SizeFull.x = size_full_modified.x = size_auto_fit.x;
5920 			if (!window_size_y_set_by_api)
5921 				window->SizeFull.y = size_full_modified.y = size_auto_fit.y;
5922 		}
5923 		else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5924 		{
5925 			// Auto-fit only grows during the first few frames
5926 			// We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
5927 			if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
5928 				window->SizeFull.x = size_full_modified.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
5929 			if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
5930 				window->SizeFull.y = size_full_modified.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
5931 			if (!window->Collapsed)
5932 				MarkIniSettingsDirty(window);
5933 		}
5934 
5935 		// Apply minimum/maximum window size constraints and final size
5936 		window->SizeFull = CalcSizeAfterConstraint(window, window->SizeFull);
5937 		window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
5938 
5939 		// SCROLLBAR STATUS
5940 
5941 		// Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size).
5942 		if (!window->Collapsed)
5943 		{
5944 			// When reading the current size we need to read it after size constraints have been applied
5945 			float size_x_for_scrollbars = size_full_modified.x != FLT_MAX ? window->SizeFull.x : window->SizeFullAtLastBegin.x;
5946 			float size_y_for_scrollbars = size_full_modified.y != FLT_MAX ? window->SizeFull.y : window->SizeFullAtLastBegin.y;
5947 			window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
5948 			window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
5949 			if (window->ScrollbarX && !window->ScrollbarY)
5950 				window->ScrollbarY = (window->SizeContents.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
5951 			window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
5952 		}
5953 
5954 		// POSITION
5955 
5956 		// Popup latch its initial position, will position itself when it appears next frame
5957 		if (window_just_activated_by_user)
5958 		{
5959 			window->AutoPosLastDirection = ImGuiDir_None;
5960 			if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
5961 				window->Pos = window->PosFloat = g.CurrentPopupStack.back().OpenPopupPos;
5962 		}
5963 
5964 		// Position child window
5965 		if (flags & ImGuiWindowFlags_ChildWindow)
5966 		{
5967 			window->BeginOrderWithinParent = parent_window->DC.ChildWindows.Size;
5968 			parent_window->DC.ChildWindows.push_back(window);
5969 			if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
5970 				window->Pos = window->PosFloat = parent_window->DC.CursorPos;
5971 		}
5972 
5973 		const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFrames == 0);
5974 		if (window_pos_with_pivot)
5975 		{
5976 			// Position given a pivot (e.g. for centering)
5977 			SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0);
5978 		}
5979 		else if (flags & ImGuiWindowFlags_ChildMenu)
5980 		{
5981 			// 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.
5982 			// This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
5983 			IM_ASSERT(window_pos_set_by_api);
5984 			float horizontal_overlap = style.ItemSpacing.x;  // We want some overlap to convey the relative depth of each popup (currently the amount of overlap it is hard-coded to style.ItemSpacing.x, may need to introduce another style value).
5985 			ImGuiWindow* parent_menu = parent_window_in_stack;
5986 			ImRect rect_to_avoid;
5987 			if (parent_menu->DC.MenuBarAppending)
5988 				rect_to_avoid = ImRect(-FLT_MAX, parent_menu->Pos.y + parent_menu->TitleBarHeight(), FLT_MAX, parent_menu->Pos.y + parent_menu->TitleBarHeight() + parent_menu->MenuBarHeight());
5989 			else
5990 				rect_to_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);
5991 			window->PosFloat = FindBestWindowPosForPopup(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
5992 		}
5993 		else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
5994 		{
5995 			ImRect rect_to_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1);
5996 			window->PosFloat = FindBestWindowPosForPopup(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
5997 		}
5998 
5999 		// Position tooltip (always follows mouse)
6000 		if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
6001 		{
6002 			float sc = g.Style.MouseCursorScale;
6003 			ImVec2 ref_pos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : g.IO.MousePos;
6004 			ImRect rect_to_avoid;
6005 			if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.NavFlags & ImGuiNavFlags_MoveMouse))
6006 				rect_to_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
6007 			else
6008 				rect_to_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.
6009 			window->PosFloat = FindBestWindowPosForPopup(ref_pos, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
6010 			if (window->AutoPosLastDirection == ImGuiDir_None)
6011 				window->PosFloat = ref_pos + ImVec2(2, 2);  // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
6012 		}
6013 
6014 		// Clamp position so it stays visible
6015 		if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
6016 		{
6017 			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.
6018 			{
6019 				ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
6020 				window->PosFloat = ImMax(window->PosFloat + window->Size, padding) - window->Size;
6021 				window->PosFloat = ImMin(window->PosFloat, g.IO.DisplaySize - padding);
6022 			}
6023 		}
6024 		window->Pos = ImFloor(window->PosFloat);
6025 
6026 		// Default item width. Make it proportional to window size if window manually resizes
6027 		if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
6028 			window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
6029 		else
6030 			window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
6031 
6032 		// Prepare for focus requests
6033 		window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter + 1)) % (window->FocusIdxAllCounter + 1);
6034 		window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter + 1)) % (window->FocusIdxTabCounter + 1);
6035 		window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1;
6036 		window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX;
6037 
6038 		// Apply scrolling
6039 		window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
6040 		window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
6041 
6042 		// Apply focus, new windows appears in front
6043 		bool want_focus = false;
6044 		if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
6045 			if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
6046 				want_focus = true;
6047 
6048 		// Handle manual resize: Resize Grips, Borders, Gamepad
6049 		int border_held = -1;
6050 		ImU32 resize_grip_col[4] = {0};
6051 		const int resize_grip_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 2 : 1;  // 4
6052 		const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
6053 		if (!window->Collapsed)
6054 			UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]);
6055 
6056 		// DRAWING
6057 
6058 		// Setup draw list and outer clipping rectangle
6059 		window->DrawList->Clear();
6060 		window->DrawList->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
6061 		window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
6062 		ImRect viewport_rect(GetViewportRect());
6063 		if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
6064 			PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
6065 		else
6066 			PushClipRect(viewport_rect.Min, viewport_rect.Max, true);
6067 
6068 		// Draw modal window background (darkens what is behind them)
6069 		if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow())
6070 			window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio));
6071 
6072 		// Draw navigation selection/windowing rectangle background
6073 		if (g.NavWindowingTarget == window)
6074 		{
6075 			ImRect bb = window->Rect();
6076 			bb.Expand(g.FontSize);
6077 			if (!bb.Contains(viewport_rect))  // Avoid drawing if the window covers all the viewport anyway
6078 				window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
6079 		}
6080 
6081 		// Draw window + handle manual resize
6082 		const float window_rounding = window->WindowRounding;
6083 		const float window_border_size = window->WindowBorderSize;
6084 		const bool title_bar_is_highlight = want_focus || (g.NavWindow && window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
6085 		const ImRect title_bar_rect = window->TitleBarRect();
6086 		if (window->Collapsed)
6087 		{
6088 			// Title bar only
6089 			float backup_border_size = style.FrameBorderSize;
6090 			g.Style.FrameBorderSize = window->WindowBorderSize;
6091 			ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
6092 			RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
6093 			g.Style.FrameBorderSize = backup_border_size;
6094 		}
6095 		else
6096 		{
6097 			// Window background
6098 			ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
6099 			if (g.NextWindowData.BgAlphaCond != 0)
6100 			{
6101 				bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT);
6102 				g.NextWindowData.BgAlphaCond = 0;
6103 			}
6104 			window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
6105 
6106 			// Title bar
6107 			ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
6108 			if (!(flags & ImGuiWindowFlags_NoTitleBar))
6109 				window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
6110 
6111 			// Menu bar
6112 			if (flags & ImGuiWindowFlags_MenuBar)
6113 			{
6114 				ImRect menu_bar_rect = window->MenuBarRect();
6115 				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.
6116 				window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top);
6117 				if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
6118 					window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
6119 			}
6120 
6121 			// Scrollbars
6122 			if (window->ScrollbarX)
6123 				Scrollbar(ImGuiLayoutType_Horizontal);
6124 			if (window->ScrollbarY)
6125 				Scrollbar(ImGuiLayoutType_Vertical);
6126 
6127 			// Render resize grips (after their input handling so we don't have a frame of latency)
6128 			if (!(flags & ImGuiWindowFlags_NoResize))
6129 			{
6130 				for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
6131 				{
6132 					const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
6133 					const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos);
6134 					window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, grip_draw_size) : ImVec2(grip_draw_size, window_border_size)));
6135 					window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(grip_draw_size, window_border_size) : ImVec2(window_border_size, grip_draw_size)));
6136 					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);
6137 					window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
6138 				}
6139 			}
6140 
6141 			// Borders
6142 			if (window_border_size > 0.0f)
6143 				window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size);
6144 			if (border_held != -1)
6145 			{
6146 				ImRect border = GetBorderRect(window, border_held, grip_draw_size, 0.0f);
6147 				window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size));
6148 			}
6149 			if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar))
6150 				window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize, -1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
6151 		}
6152 
6153 		// Draw navigation selection/windowing rectangle border
6154 		if (g.NavWindowingTarget == window)
6155 		{
6156 			float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
6157 			ImRect bb = window->Rect();
6158 			bb.Expand(g.FontSize);
6159 			if (bb.Contains(viewport_rect))  // If a window fits the entire viewport, adjust its highlight inward
6160 			{
6161 				bb.Expand(-g.FontSize - 1.0f);
6162 				rounding = window->WindowRounding;
6163 			}
6164 			window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
6165 		}
6166 
6167 		// Store a backup of SizeFull which we will use next frame to decide if we need scrollbars.
6168 		window->SizeFullAtLastBegin = window->SizeFull;
6169 
6170 		// Update ContentsRegionMax. All the variable it depends on are set above in this function.
6171 		window->ContentsRegionRect.Min.x = -window->Scroll.x + window->WindowPadding.x;
6172 		window->ContentsRegionRect.Min.y = -window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
6173 		window->ContentsRegionRect.Max.x = -window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x));
6174 		window->ContentsRegionRect.Max.y = -window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y));
6175 
6176 		// Setup drawing context
6177 		// (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.)
6178 		window->DC.IndentX = 0.0f + window->WindowPadding.x - window->Scroll.x;
6179 		window->DC.GroupOffsetX = 0.0f;
6180 		window->DC.ColumnsOffsetX = 0.0f;
6181 		window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.IndentX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y);
6182 		window->DC.CursorPos = window->DC.CursorStartPos;
6183 		window->DC.CursorPosPrevLine = window->DC.CursorPos;
6184 		window->DC.CursorMaxPos = window->DC.CursorStartPos;
6185 		window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f;
6186 		window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
6187 		window->DC.NavHideHighlightOneFrame = false;
6188 		window->DC.NavHasScroll = (GetScrollMaxY() > 0.0f);
6189 		window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
6190 		window->DC.NavLayerActiveMaskNext = 0x00;
6191 		window->DC.MenuBarAppending = false;
6192 		window->DC.MenuBarOffsetX = ImMax(window->WindowPadding.x, style.ItemSpacing.x);
6193 		window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
6194 		window->DC.ChildWindows.resize(0);
6195 		window->DC.LayoutType = ImGuiLayoutType_Vertical;
6196 		window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
6197 		window->DC.ItemFlags = ImGuiItemFlags_Default_;
6198 		window->DC.ItemWidth = window->ItemWidthDefault;
6199 		window->DC.TextWrapPos = -1.0f;  // disabled
6200 		window->DC.ItemFlagsStack.resize(0);
6201 		window->DC.ItemWidthStack.resize(0);
6202 		window->DC.TextWrapPosStack.resize(0);
6203 		window->DC.ColumnsSet = NULL;
6204 		window->DC.TreeDepth = 0;
6205 		window->DC.TreeDepthMayCloseOnPop = 0x00;
6206 		window->DC.StateStorage = &window->StateStorage;
6207 		window->DC.GroupStack.resize(0);
6208 		window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
6209 
6210 		if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
6211 		{
6212 			window->DC.ItemFlags = parent_window->DC.ItemFlags;
6213 			window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
6214 		}
6215 
6216 		if (window->AutoFitFramesX > 0)
6217 			window->AutoFitFramesX--;
6218 		if (window->AutoFitFramesY > 0)
6219 			window->AutoFitFramesY--;
6220 
6221 		// Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
6222 		if (want_focus)
6223 		{
6224 			FocusWindow(window);
6225 			NavInitWindow(window, false);
6226 		}
6227 
6228 		// Title bar
6229 		if (!(flags & ImGuiWindowFlags_NoTitleBar))
6230 		{
6231 			// Close & collapse button are on layer 1 (same as menus) and don't default focus
6232 			const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
6233 			window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
6234 			window->DC.NavLayerCurrent++;
6235 			window->DC.NavLayerCurrentMask <<= 1;
6236 
6237 			// Collapse button
6238 			if (!(flags & ImGuiWindowFlags_NoCollapse))
6239 			{
6240 				ImGuiID id = window->GetID("#COLLAPSE");
6241 				ImRect bb(window->Pos + style.FramePadding + ImVec2(1, 1), window->Pos + style.FramePadding + ImVec2(g.FontSize, g.FontSize) - ImVec2(1, 1));
6242 				ItemAdd(bb, id);  // To allow navigation
6243 				if (ButtonBehavior(bb, id, NULL, NULL))
6244 					window->CollapseToggleWanted = true;  // Defer collapsing to next frame as we are too far in the Begin() function
6245 				RenderNavHighlight(bb, id);
6246 				RenderTriangle(window->Pos + style.FramePadding, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f);
6247 			}
6248 
6249 			// Close button
6250 			if (p_open != NULL)
6251 			{
6252 				const float PAD = 2.0f;
6253 				const float rad = (window->TitleBarHeight() - PAD * 2.0f) * 0.5f;
6254 				if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-PAD - rad, PAD + rad), rad))
6255 					*p_open = false;
6256 			}
6257 
6258 			window->DC.NavLayerCurrent--;
6259 			window->DC.NavLayerCurrentMask >>= 1;
6260 			window->DC.ItemFlags = item_flags_backup;
6261 
6262 			// Title text (FIXME: refactor text alignment facilities along with RenderText helpers)
6263 			ImVec2 text_size = CalcTextSize(name, NULL, true);
6264 			ImRect text_r = title_bar_rect;
6265 			float pad_left = (flags & ImGuiWindowFlags_NoCollapse) == 0 ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x;
6266 			float pad_right = (p_open != NULL) ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x;
6267 			if (style.WindowTitleAlign.x > 0.0f) pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x);
6268 			text_r.Min.x += pad_left;
6269 			text_r.Max.x -= pad_right;
6270 			ImRect clip_rect = text_r;
6271 			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()
6272 			RenderTextClipped(text_r.Min, text_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect);
6273 		}
6274 
6275 		// Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
6276 		window->WindowRectClipped = window->Rect();
6277 		window->WindowRectClipped.ClipWith(window->ClipRect);
6278 
6279 		// Pressing CTRL+C while holding on a window copy its content to the clipboard
6280 		// 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.
6281 		// Maybe we can support CTRL+C on every element?
6282 		/*
6283         if (g.ActiveId == move_id)
6284             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
6285                 ImGui::LogToClipboard();
6286         */
6287 
6288 		// Inner rectangle
6289 		// We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
6290 		// Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior.
6291 		window->InnerRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize;
6292 		window->InnerRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
6293 		window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - window->WindowBorderSize;
6294 		window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y - window->WindowBorderSize;
6295 		//window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE);
6296 
6297 		// 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.).
6298 		window->DC.LastItemId = window->MoveId;
6299 		window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
6300 		window->DC.LastItemRect = title_bar_rect;
6301 	}
6302 
6303 	// Inner clipping rectangle
6304 	// Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
6305 	const float border_size = window->WindowBorderSize;
6306 	ImRect clip_rect;
6307 	clip_rect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x * 0.5f - border_size)));
6308 	clip_rect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y);
6309 	clip_rect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x * 0.5f - border_size)));
6310 	clip_rect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y);
6311 	PushClipRect(clip_rect.Min, clip_rect.Max, true);
6312 
6313 	// 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)
6314 	if (first_begin_of_the_frame)
6315 		window->WriteAccessed = false;
6316 
6317 	window->BeginCount++;
6318 	g.NextWindowData.SizeConstraintCond = 0;
6319 
6320 	// Child window can be out of sight and have "negative" clip windows.
6321 	// Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar).
6322 	if (flags & ImGuiWindowFlags_ChildWindow)
6323 	{
6324 		IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
6325 		window->Collapsed = parent_window && parent_window->Collapsed;
6326 
6327 		if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
6328 			window->Collapsed |= (window->WindowRectClipped.Min.x >= window->WindowRectClipped.Max.x || window->WindowRectClipped.Min.y >= window->WindowRectClipped.Max.y);
6329 
6330 		// We also hide the window from rendering because we've already added its border to the command list.
6331 		// (we could perform the check earlier in the function but it is simpler at this point)
6332 		if (window->Collapsed)
6333 			window->Active = false;
6334 	}
6335 	if (style.Alpha <= 0.0f)
6336 		window->Active = false;
6337 
6338 	// Return false if we don't intend to display anything to allow user to perform an early out optimization
6339 	window->SkipItems = (window->Collapsed || !window->Active) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0;
6340 	return !window->SkipItems;
6341 }
6342 
6343 // Old Begin() API with 5 parameters, avoid calling this version directly! Use SetNextWindowSize()/SetNextWindowBgAlpha() + Begin() instead.
6344 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
Begin(const char * name,bool * p_open,const ImVec2 & size_first_use,float bg_alpha_override,ImGuiWindowFlags flags)6345 bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_first_use, float bg_alpha_override, ImGuiWindowFlags flags)
6346 {
6347 	// 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.
6348 	if (size_first_use.x != 0.0f || size_first_use.y != 0.0f)
6349 		ImGui::SetNextWindowSize(size_first_use, ImGuiCond_FirstUseEver);
6350 
6351 	// Old API feature: override the window background alpha with a parameter.
6352 	if (bg_alpha_override >= 0.0f)
6353 		ImGui::SetNextWindowBgAlpha(bg_alpha_override);
6354 
6355 	return ImGui::Begin(name, p_open, flags);
6356 }
6357 #endif  // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
6358 
End()6359 void ImGui::End()
6360 {
6361 	ImGuiContext& g = *GImGui;
6362 	ImGuiWindow* window = g.CurrentWindow;
6363 
6364 	if (window->DC.ColumnsSet != NULL)
6365 		EndColumns();
6366 	PopClipRect();  // Inner window clip rectangle
6367 
6368 	// Stop logging
6369 	if (!(window->Flags & ImGuiWindowFlags_ChildWindow))  // FIXME: add more options for scope of logging
6370 		LogFinish();
6371 
6372 	// Pop from window stack
6373 	g.CurrentWindowStack.pop_back();
6374 	if (window->Flags & ImGuiWindowFlags_Popup)
6375 		g.CurrentPopupStack.pop_back();
6376 	CheckStacksSize(window, false);
6377 	SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
6378 }
6379 
6380 // Vertical scrollbar
6381 // The entire piece of code below is rather confusing because:
6382 // - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
6383 // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar
6384 // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.
Scrollbar(ImGuiLayoutType direction)6385 void ImGui::Scrollbar(ImGuiLayoutType direction)
6386 {
6387 	ImGuiContext& g = *GImGui;
6388 	ImGuiWindow* window = g.CurrentWindow;
6389 
6390 	const bool horizontal = (direction == ImGuiLayoutType_Horizontal);
6391 	const ImGuiStyle& style = g.Style;
6392 	const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY");
6393 
6394 	// Render background
6395 	bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX);
6396 	float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f;
6397 	const ImRect window_rect = window->Rect();
6398 	const float border_size = window->WindowBorderSize;
6399 	ImRect bb = horizontal
6400 					? 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)
6401 					: 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);
6402 	if (!horizontal)
6403 		bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f);
6404 	if (bb.GetWidth() <= 0.0f || bb.GetHeight() <= 0.0f)
6405 		return;
6406 
6407 	int window_rounding_corners;
6408 	if (horizontal)
6409 		window_rounding_corners = ImDrawCornerFlags_BotLeft | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight);
6410 	else
6411 		window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0) | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight);
6412 	window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, window_rounding_corners);
6413 	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)));
6414 
6415 	// V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
6416 	float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight();
6417 	float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y;
6418 	float win_size_avail_v = (horizontal ? window->SizeFull.x : window->SizeFull.y) - other_scrollbar_size_w;
6419 	float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y;
6420 
6421 	// Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount)
6422 	// But we maintain a minimum size in pixel to allow for the user to still aim inside.
6423 	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.
6424 	const float win_size_v = ImMax(ImMax(win_size_contents_v, win_size_avail_v), 1.0f);
6425 	const float grab_h_pixels = ImClamp(scrollbar_size_v * (win_size_avail_v / win_size_v), style.GrabMinSize, scrollbar_size_v);
6426 	const float grab_h_norm = grab_h_pixels / scrollbar_size_v;
6427 
6428 	// Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().
6429 	bool held = false;
6430 	bool hovered = false;
6431 	const bool previously_held = (g.ActiveId == id);
6432 	ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus);
6433 
6434 	float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v);
6435 	float scroll_ratio = ImSaturate(scroll_v / scroll_max);
6436 	float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
6437 	if (held && grab_h_norm < 1.0f)
6438 	{
6439 		float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y;
6440 		float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
6441 		float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y;
6442 
6443 		// Click position in scrollbar normalized space (0.0f->1.0f)
6444 		const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
6445 		SetHoveredID(id);
6446 
6447 		bool seek_absolute = false;
6448 		if (!previously_held)
6449 		{
6450 			// On initial click calculate the distance between mouse and the center of the grab
6451 			if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm)
6452 			{
6453 				*click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f;
6454 			}
6455 			else
6456 			{
6457 				seek_absolute = true;
6458 				*click_delta_to_grab_center_v = 0.0f;
6459 			}
6460 		}
6461 
6462 		// Apply scroll
6463 		// 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
6464 		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));
6465 		scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);  //(win_size_contents_v - win_size_v));
6466 		if (horizontal)
6467 			window->Scroll.x = scroll_v;
6468 		else
6469 			window->Scroll.y = scroll_v;
6470 
6471 		// Update values for rendering
6472 		scroll_ratio = ImSaturate(scroll_v / scroll_max);
6473 		grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
6474 
6475 		// Update distance to grab now that we have seeked and saturated
6476 		if (seek_absolute)
6477 			*click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f;
6478 	}
6479 
6480 	// Render
6481 	const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab);
6482 	ImRect grab_rect;
6483 	if (horizontal)
6484 		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);
6485 	else
6486 		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));
6487 	window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding);
6488 }
6489 
BringWindowToFront(ImGuiWindow * window)6490 void ImGui::BringWindowToFront(ImGuiWindow* window)
6491 {
6492 	ImGuiContext& g = *GImGui;
6493 	ImGuiWindow* current_front_window = g.Windows.back();
6494 	if (current_front_window == window || current_front_window->RootWindow == window)
6495 		return;
6496 	for (int i = g.Windows.Size - 2; i >= 0; i--)  // We can ignore the front most window
6497 		if (g.Windows[i] == window)
6498 		{
6499 			g.Windows.erase(g.Windows.Data + i);
6500 			g.Windows.push_back(window);
6501 			break;
6502 		}
6503 }
6504 
BringWindowToBack(ImGuiWindow * window)6505 void ImGui::BringWindowToBack(ImGuiWindow* window)
6506 {
6507 	ImGuiContext& g = *GImGui;
6508 	if (g.Windows[0] == window)
6509 		return;
6510 	for (int i = 0; i < g.Windows.Size; i++)
6511 		if (g.Windows[i] == window)
6512 		{
6513 			memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
6514 			g.Windows[0] = window;
6515 			break;
6516 		}
6517 }
6518 
6519 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)6520 void ImGui::FocusWindow(ImGuiWindow* window)
6521 {
6522 	ImGuiContext& g = *GImGui;
6523 
6524 	if (g.NavWindow != window)
6525 	{
6526 		g.NavWindow = window;
6527 		if (window && g.NavDisableMouseHover)
6528 			g.NavMousePosDirty = true;
6529 		g.NavInitRequest = false;
6530 		g.NavId = window ? window->NavLastIds[0] : 0;  // Restore NavId
6531 		g.NavIdIsAlive = false;
6532 		g.NavLayer = 0;
6533 	}
6534 
6535 	// Passing NULL allow to disable keyboard focus
6536 	if (!window)
6537 		return;
6538 
6539 	// Move the root window to the top of the pile
6540 	if (window->RootWindow)
6541 		window = window->RootWindow;
6542 
6543 	// Steal focus on active widgets
6544 	if (window->Flags & ImGuiWindowFlags_Popup)  // FIXME: This statement should be unnecessary. Need further testing before removing it..
6545 		if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
6546 			ClearActiveID();
6547 
6548 	// Bring to front
6549 	if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
6550 		BringWindowToFront(window);
6551 }
6552 
FocusFrontMostActiveWindow(ImGuiWindow * ignore_window)6553 void ImGui::FocusFrontMostActiveWindow(ImGuiWindow* ignore_window)
6554 {
6555 	ImGuiContext& g = *GImGui;
6556 	for (int i = g.Windows.Size - 1; i >= 0; i--)
6557 		if (g.Windows[i] != ignore_window && g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow))
6558 		{
6559 			ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(g.Windows[i]);
6560 			FocusWindow(focus_window);
6561 			return;
6562 		}
6563 }
6564 
PushItemWidth(float item_width)6565 void ImGui::PushItemWidth(float item_width)
6566 {
6567 	ImGuiWindow* window = GetCurrentWindow();
6568 	window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
6569 	window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
6570 }
6571 
PushMultiItemsWidths(int components,float w_full)6572 void ImGui::PushMultiItemsWidths(int components, float w_full)
6573 {
6574 	ImGuiWindow* window = GetCurrentWindow();
6575 	const ImGuiStyle& style = GImGui->Style;
6576 	if (w_full <= 0.0f)
6577 		w_full = CalcItemWidth();
6578 	const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));
6579 	const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));
6580 	window->DC.ItemWidthStack.push_back(w_item_last);
6581 	for (int i = 0; i < components - 1; i++)
6582 		window->DC.ItemWidthStack.push_back(w_item_one);
6583 	window->DC.ItemWidth = window->DC.ItemWidthStack.back();
6584 }
6585 
PopItemWidth()6586 void ImGui::PopItemWidth()
6587 {
6588 	ImGuiWindow* window = GetCurrentWindow();
6589 	window->DC.ItemWidthStack.pop_back();
6590 	window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
6591 }
6592 
CalcItemWidth()6593 float ImGui::CalcItemWidth()
6594 {
6595 	ImGuiWindow* window = GetCurrentWindowRead();
6596 	float w = window->DC.ItemWidth;
6597 	if (w < 0.0f)
6598 	{
6599 		// 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.
6600 		float width_to_right_edge = GetContentRegionAvail().x;
6601 		w = ImMax(1.0f, width_to_right_edge + w);
6602 	}
6603 	w = (float)(int)w;
6604 	return w;
6605 }
6606 
GetDefaultFont()6607 static ImFont* GetDefaultFont()
6608 {
6609 	ImGuiContext& g = *GImGui;
6610 	return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0];
6611 }
6612 
SetCurrentFont(ImFont * font)6613 void ImGui::SetCurrentFont(ImFont* font)
6614 {
6615 	ImGuiContext& g = *GImGui;
6616 	IM_ASSERT(font && font->IsLoaded());  // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
6617 	IM_ASSERT(font->Scale > 0.0f);
6618 	g.Font = font;
6619 	g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale;
6620 	g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
6621 
6622 	ImFontAtlas* atlas = g.Font->ContainerAtlas;
6623 	g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
6624 	g.DrawListSharedData.Font = g.Font;
6625 	g.DrawListSharedData.FontSize = g.FontSize;
6626 }
6627 
PushFont(ImFont * font)6628 void ImGui::PushFont(ImFont* font)
6629 {
6630 	ImGuiContext& g = *GImGui;
6631 	if (!font)
6632 		font = GetDefaultFont();
6633 	SetCurrentFont(font);
6634 	g.FontStack.push_back(font);
6635 	g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
6636 }
6637 
PopFont()6638 void ImGui::PopFont()
6639 {
6640 	ImGuiContext& g = *GImGui;
6641 	g.CurrentWindow->DrawList->PopTextureID();
6642 	g.FontStack.pop_back();
6643 	SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
6644 }
6645 
PushItemFlag(ImGuiItemFlags option,bool enabled)6646 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
6647 {
6648 	ImGuiWindow* window = GetCurrentWindow();
6649 	if (enabled)
6650 		window->DC.ItemFlags |= option;
6651 	else
6652 		window->DC.ItemFlags &= ~option;
6653 	window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
6654 }
6655 
PopItemFlag()6656 void ImGui::PopItemFlag()
6657 {
6658 	ImGuiWindow* window = GetCurrentWindow();
6659 	window->DC.ItemFlagsStack.pop_back();
6660 	window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
6661 }
6662 
PushAllowKeyboardFocus(bool allow_keyboard_focus)6663 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
6664 {
6665 	PushItemFlag(ImGuiItemFlags_AllowKeyboardFocus, allow_keyboard_focus);
6666 }
6667 
PopAllowKeyboardFocus()6668 void ImGui::PopAllowKeyboardFocus()
6669 {
6670 	PopItemFlag();
6671 }
6672 
PushButtonRepeat(bool repeat)6673 void ImGui::PushButtonRepeat(bool repeat)
6674 {
6675 	PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
6676 }
6677 
PopButtonRepeat()6678 void ImGui::PopButtonRepeat()
6679 {
6680 	PopItemFlag();
6681 }
6682 
PushTextWrapPos(float wrap_pos_x)6683 void ImGui::PushTextWrapPos(float wrap_pos_x)
6684 {
6685 	ImGuiWindow* window = GetCurrentWindow();
6686 	window->DC.TextWrapPos = wrap_pos_x;
6687 	window->DC.TextWrapPosStack.push_back(wrap_pos_x);
6688 }
6689 
PopTextWrapPos()6690 void ImGui::PopTextWrapPos()
6691 {
6692 	ImGuiWindow* window = GetCurrentWindow();
6693 	window->DC.TextWrapPosStack.pop_back();
6694 	window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
6695 }
6696 
6697 // 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)6698 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
6699 {
6700 	ImGuiContext& g = *GImGui;
6701 	ImGuiColMod backup;
6702 	backup.Col = idx;
6703 	backup.BackupValue = g.Style.Colors[idx];
6704 	g.ColorModifiers.push_back(backup);
6705 	g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
6706 }
6707 
PushStyleColor(ImGuiCol idx,const ImVec4 & col)6708 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
6709 {
6710 	ImGuiContext& g = *GImGui;
6711 	ImGuiColMod backup;
6712 	backup.Col = idx;
6713 	backup.BackupValue = g.Style.Colors[idx];
6714 	g.ColorModifiers.push_back(backup);
6715 	g.Style.Colors[idx] = col;
6716 }
6717 
PopStyleColor(int count)6718 void ImGui::PopStyleColor(int count)
6719 {
6720 	ImGuiContext& g = *GImGui;
6721 	while (count > 0)
6722 	{
6723 		ImGuiColMod& backup = g.ColorModifiers.back();
6724 		g.Style.Colors[backup.Col] = backup.BackupValue;
6725 		g.ColorModifiers.pop_back();
6726 		count--;
6727 	}
6728 }
6729 
6730 struct ImGuiStyleVarInfo
6731 {
6732 	ImGuiDataType Type;
6733 	ImU32 Offset;
GetVarPtrImGuiStyleVarInfo6734 	void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
6735 };
6736 
6737 static const ImGuiStyleVarInfo GStyleVarInfo[] =
6738 	{
6739 		{ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha)},              // ImGuiStyleVar_Alpha
6740 		{ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding)},     // ImGuiStyleVar_WindowPadding
6741 		{ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding)},     // ImGuiStyleVar_WindowRounding
6742 		{ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize)},   // ImGuiStyleVar_WindowBorderSize
6743 		{ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize)},     // ImGuiStyleVar_WindowMinSize
6744 		{ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign)},  // ImGuiStyleVar_WindowTitleAlign
6745 		{ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding)},      // ImGuiStyleVar_ChildRounding
6746 		{ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize)},    // ImGuiStyleVar_ChildBorderSize
6747 		{ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding)},      // ImGuiStyleVar_PopupRounding
6748 		{ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize)},    // ImGuiStyleVar_PopupBorderSize
6749 		{ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding)},      // ImGuiStyleVar_FramePadding
6750 		{ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding)},      // ImGuiStyleVar_FrameRounding
6751 		{ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize)},    // ImGuiStyleVar_FrameBorderSize
6752 		{ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing)},       // ImGuiStyleVar_ItemSpacing
6753 		{ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing)},  // ImGuiStyleVar_ItemInnerSpacing
6754 		{ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing)},      // ImGuiStyleVar_IndentSpacing
6755 		{ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize)},      // ImGuiStyleVar_ScrollbarSize
6756 		{ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding)},  // ImGuiStyleVar_ScrollbarRounding
6757 		{ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize)},        // ImGuiStyleVar_GrabMinSize
6758 		{ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding)},       // ImGuiStyleVar_GrabRounding
6759 		{ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign)},   // ImGuiStyleVar_ButtonTextAlign
6760 };
6761 
GetStyleVarInfo(ImGuiStyleVar idx)6762 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
6763 {
6764 	IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_Count_);
6765 	IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_Count_);
6766 	return &GStyleVarInfo[idx];
6767 }
6768 
PushStyleVar(ImGuiStyleVar idx,float val)6769 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
6770 {
6771 	const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
6772 	if (var_info->Type == ImGuiDataType_Float)
6773 	{
6774 		ImGuiContext& g = *GImGui;
6775 		float* pvar = (float*)var_info->GetVarPtr(&g.Style);
6776 		g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
6777 		*pvar = val;
6778 		return;
6779 	}
6780 	IM_ASSERT(0);  // Called function with wrong-type? Variable is not a float.
6781 }
6782 
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)6783 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
6784 {
6785 	const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
6786 	if (var_info->Type == ImGuiDataType_Float2)
6787 	{
6788 		ImGuiContext& g = *GImGui;
6789 		ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
6790 		g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
6791 		*pvar = val;
6792 		return;
6793 	}
6794 	IM_ASSERT(0);  // Called function with wrong-type? Variable is not a ImVec2.
6795 }
6796 
PopStyleVar(int count)6797 void ImGui::PopStyleVar(int count)
6798 {
6799 	ImGuiContext& g = *GImGui;
6800 	while (count > 0)
6801 	{
6802 		ImGuiStyleMod& backup = g.StyleModifiers.back();
6803 		const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
6804 		if (info->Type == ImGuiDataType_Float)
6805 			(*(float*)info->GetVarPtr(&g.Style)) = backup.BackupFloat[0];
6806 		else if (info->Type == ImGuiDataType_Float2)
6807 			(*(ImVec2*)info->GetVarPtr(&g.Style)) = ImVec2(backup.BackupFloat[0], backup.BackupFloat[1]);
6808 		else if (info->Type == ImGuiDataType_Int)
6809 			(*(int*)info->GetVarPtr(&g.Style)) = backup.BackupInt[0];
6810 		g.StyleModifiers.pop_back();
6811 		count--;
6812 	}
6813 }
6814 
GetStyleColorName(ImGuiCol idx)6815 const char* ImGui::GetStyleColorName(ImGuiCol idx)
6816 {
6817 	// Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
6818 	switch (idx)
6819 	{
6820 		case ImGuiCol_Text:
6821 			return "Text";
6822 		case ImGuiCol_TextDisabled:
6823 			return "TextDisabled";
6824 		case ImGuiCol_WindowBg:
6825 			return "WindowBg";
6826 		case ImGuiCol_ChildBg:
6827 			return "ChildBg";
6828 		case ImGuiCol_PopupBg:
6829 			return "PopupBg";
6830 		case ImGuiCol_Border:
6831 			return "Border";
6832 		case ImGuiCol_BorderShadow:
6833 			return "BorderShadow";
6834 		case ImGuiCol_FrameBg:
6835 			return "FrameBg";
6836 		case ImGuiCol_FrameBgHovered:
6837 			return "FrameBgHovered";
6838 		case ImGuiCol_FrameBgActive:
6839 			return "FrameBgActive";
6840 		case ImGuiCol_TitleBg:
6841 			return "TitleBg";
6842 		case ImGuiCol_TitleBgActive:
6843 			return "TitleBgActive";
6844 		case ImGuiCol_TitleBgCollapsed:
6845 			return "TitleBgCollapsed";
6846 		case ImGuiCol_MenuBarBg:
6847 			return "MenuBarBg";
6848 		case ImGuiCol_ScrollbarBg:
6849 			return "ScrollbarBg";
6850 		case ImGuiCol_ScrollbarGrab:
6851 			return "ScrollbarGrab";
6852 		case ImGuiCol_ScrollbarGrabHovered:
6853 			return "ScrollbarGrabHovered";
6854 		case ImGuiCol_ScrollbarGrabActive:
6855 			return "ScrollbarGrabActive";
6856 		case ImGuiCol_CheckMark:
6857 			return "CheckMark";
6858 		case ImGuiCol_SliderGrab:
6859 			return "SliderGrab";
6860 		case ImGuiCol_SliderGrabActive:
6861 			return "SliderGrabActive";
6862 		case ImGuiCol_Button:
6863 			return "Button";
6864 		case ImGuiCol_ButtonHovered:
6865 			return "ButtonHovered";
6866 		case ImGuiCol_ButtonActive:
6867 			return "ButtonActive";
6868 		case ImGuiCol_Header:
6869 			return "Header";
6870 		case ImGuiCol_HeaderHovered:
6871 			return "HeaderHovered";
6872 		case ImGuiCol_HeaderActive:
6873 			return "HeaderActive";
6874 		case ImGuiCol_Separator:
6875 			return "Separator";
6876 		case ImGuiCol_SeparatorHovered:
6877 			return "SeparatorHovered";
6878 		case ImGuiCol_SeparatorActive:
6879 			return "SeparatorActive";
6880 		case ImGuiCol_ResizeGrip:
6881 			return "ResizeGrip";
6882 		case ImGuiCol_ResizeGripHovered:
6883 			return "ResizeGripHovered";
6884 		case ImGuiCol_ResizeGripActive:
6885 			return "ResizeGripActive";
6886 		case ImGuiCol_CloseButton:
6887 			return "CloseButton";
6888 		case ImGuiCol_CloseButtonHovered:
6889 			return "CloseButtonHovered";
6890 		case ImGuiCol_CloseButtonActive:
6891 			return "CloseButtonActive";
6892 		case ImGuiCol_PlotLines:
6893 			return "PlotLines";
6894 		case ImGuiCol_PlotLinesHovered:
6895 			return "PlotLinesHovered";
6896 		case ImGuiCol_PlotHistogram:
6897 			return "PlotHistogram";
6898 		case ImGuiCol_PlotHistogramHovered:
6899 			return "PlotHistogramHovered";
6900 		case ImGuiCol_TextSelectedBg:
6901 			return "TextSelectedBg";
6902 		case ImGuiCol_ModalWindowDarkening:
6903 			return "ModalWindowDarkening";
6904 		case ImGuiCol_DragDropTarget:
6905 			return "DragDropTarget";
6906 		case ImGuiCol_NavHighlight:
6907 			return "NavHighlight";
6908 		case ImGuiCol_NavWindowingHighlight:
6909 			return "NavWindowingHighlight";
6910 	}
6911 	IM_ASSERT(0);
6912 	return "Unknown";
6913 }
6914 
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)6915 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
6916 {
6917 	if (window->RootWindow == potential_parent)
6918 		return true;
6919 	while (window != NULL)
6920 	{
6921 		if (window == potential_parent)
6922 			return true;
6923 		window = window->ParentWindow;
6924 	}
6925 	return false;
6926 }
6927 
IsWindowHovered(ImGuiHoveredFlags flags)6928 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
6929 {
6930 	IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0);  // Flags not supported by this function
6931 	ImGuiContext& g = *GImGui;
6932 
6933 	if (flags & ImGuiHoveredFlags_AnyWindow)
6934 	{
6935 		if (g.HoveredWindow == NULL)
6936 			return false;
6937 	}
6938 	else
6939 	{
6940 		switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
6941 		{
6942 			case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
6943 				if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
6944 					return false;
6945 				break;
6946 			case ImGuiHoveredFlags_RootWindow:
6947 				if (g.HoveredWindow != g.CurrentWindow->RootWindow)
6948 					return false;
6949 				break;
6950 			case ImGuiHoveredFlags_ChildWindows:
6951 				if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
6952 					return false;
6953 				break;
6954 			default:
6955 				if (g.HoveredWindow != g.CurrentWindow)
6956 					return false;
6957 				break;
6958 		}
6959 	}
6960 
6961 	if (!IsWindowContentHoverable(g.HoveredRootWindow, flags))
6962 		return false;
6963 	if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
6964 		if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
6965 			return false;
6966 	return true;
6967 }
6968 
IsWindowFocused(ImGuiFocusedFlags flags)6969 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
6970 {
6971 	ImGuiContext& g = *GImGui;
6972 	IM_ASSERT(g.CurrentWindow);  // Not inside a Begin()/End()
6973 
6974 	if (flags & ImGuiFocusedFlags_AnyWindow)
6975 		return g.NavWindow != NULL;
6976 
6977 	switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
6978 	{
6979 		case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
6980 			return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
6981 		case ImGuiFocusedFlags_RootWindow:
6982 			return g.NavWindow == g.CurrentWindow->RootWindow;
6983 		case ImGuiFocusedFlags_ChildWindows:
6984 			return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
6985 		default:
6986 			return g.NavWindow == g.CurrentWindow;
6987 	}
6988 }
6989 
6990 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
IsWindowNavFocusable(ImGuiWindow * window)6991 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
6992 {
6993 	ImGuiContext& g = *GImGui;
6994 	return window->Active && window == window->RootWindowForTabbing && (!(window->Flags & ImGuiWindowFlags_NoNavFocus) || window == g.NavWindow);
6995 }
6996 
GetWindowWidth()6997 float ImGui::GetWindowWidth()
6998 {
6999 	ImGuiWindow* window = GImGui->CurrentWindow;
7000 	return window->Size.x;
7001 }
7002 
GetWindowHeight()7003 float ImGui::GetWindowHeight()
7004 {
7005 	ImGuiWindow* window = GImGui->CurrentWindow;
7006 	return window->Size.y;
7007 }
7008 
GetWindowPos()7009 ImVec2 ImGui::GetWindowPos()
7010 {
7011 	ImGuiContext& g = *GImGui;
7012 	ImGuiWindow* window = g.CurrentWindow;
7013 	return window->Pos;
7014 }
7015 
SetWindowScrollX(ImGuiWindow * window,float new_scroll_x)7016 static void SetWindowScrollX(ImGuiWindow* window, float new_scroll_x)
7017 {
7018 	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.
7019 	window->Scroll.x = new_scroll_x;
7020 	window->DC.CursorMaxPos.x -= window->Scroll.x;
7021 }
7022 
SetWindowScrollY(ImGuiWindow * window,float new_scroll_y)7023 static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
7024 {
7025 	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.
7026 	window->Scroll.y = new_scroll_y;
7027 	window->DC.CursorMaxPos.y -= window->Scroll.y;
7028 }
7029 
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)7030 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
7031 {
7032 	// Test condition (NB: bit 0 is always true) and clear flags for next time
7033 	if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
7034 		return;
7035 	window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7036 	window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
7037 
7038 	// Set
7039 	const ImVec2 old_pos = window->Pos;
7040 	window->PosFloat = pos;
7041 	window->Pos = ImFloor(pos);
7042 	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
7043 	window->DC.CursorMaxPos += (window->Pos - old_pos);  // And more importantly we need to adjust this so size calculation doesn't get affected.
7044 }
7045 
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)7046 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
7047 {
7048 	ImGuiWindow* window = GetCurrentWindowRead();
7049 	SetWindowPos(window, pos, cond);
7050 }
7051 
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)7052 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
7053 {
7054 	if (ImGuiWindow* window = FindWindowByName(name))
7055 		SetWindowPos(window, pos, cond);
7056 }
7057 
GetWindowSize()7058 ImVec2 ImGui::GetWindowSize()
7059 {
7060 	ImGuiWindow* window = GetCurrentWindowRead();
7061 	return window->Size;
7062 }
7063 
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)7064 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
7065 {
7066 	// Test condition (NB: bit 0 is always true) and clear flags for next time
7067 	if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
7068 		return;
7069 	window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7070 
7071 	// Set
7072 	if (size.x > 0.0f)
7073 	{
7074 		window->AutoFitFramesX = 0;
7075 		window->SizeFull.x = size.x;
7076 	}
7077 	else
7078 	{
7079 		window->AutoFitFramesX = 2;
7080 		window->AutoFitOnlyGrows = false;
7081 	}
7082 	if (size.y > 0.0f)
7083 	{
7084 		window->AutoFitFramesY = 0;
7085 		window->SizeFull.y = size.y;
7086 	}
7087 	else
7088 	{
7089 		window->AutoFitFramesY = 2;
7090 		window->AutoFitOnlyGrows = false;
7091 	}
7092 }
7093 
SetWindowSize(const ImVec2 & size,ImGuiCond cond)7094 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
7095 {
7096 	SetWindowSize(GImGui->CurrentWindow, size, cond);
7097 }
7098 
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)7099 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
7100 {
7101 	if (ImGuiWindow* window = FindWindowByName(name))
7102 		SetWindowSize(window, size, cond);
7103 }
7104 
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)7105 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
7106 {
7107 	// Test condition (NB: bit 0 is always true) and clear flags for next time
7108 	if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
7109 		return;
7110 	window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7111 
7112 	// Set
7113 	window->Collapsed = collapsed;
7114 }
7115 
SetWindowCollapsed(bool collapsed,ImGuiCond cond)7116 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
7117 {
7118 	SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
7119 }
7120 
IsWindowCollapsed()7121 bool ImGui::IsWindowCollapsed()
7122 {
7123 	ImGuiWindow* window = GetCurrentWindowRead();
7124 	return window->Collapsed;
7125 }
7126 
IsWindowAppearing()7127 bool ImGui::IsWindowAppearing()
7128 {
7129 	ImGuiWindow* window = GetCurrentWindowRead();
7130 	return window->Appearing;
7131 }
7132 
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)7133 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
7134 {
7135 	if (ImGuiWindow* window = FindWindowByName(name))
7136 		SetWindowCollapsed(window, collapsed, cond);
7137 }
7138 
SetWindowFocus()7139 void ImGui::SetWindowFocus()
7140 {
7141 	FocusWindow(GImGui->CurrentWindow);
7142 }
7143 
SetWindowFocus(const char * name)7144 void ImGui::SetWindowFocus(const char* name)
7145 {
7146 	if (name)
7147 	{
7148 		if (ImGuiWindow* window = FindWindowByName(name))
7149 			FocusWindow(window);
7150 	}
7151 	else
7152 	{
7153 		FocusWindow(NULL);
7154 	}
7155 }
7156 
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)7157 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
7158 {
7159 	ImGuiContext& g = *GImGui;
7160 	g.NextWindowData.PosVal = pos;
7161 	g.NextWindowData.PosPivotVal = pivot;
7162 	g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
7163 }
7164 
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)7165 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
7166 {
7167 	ImGuiContext& g = *GImGui;
7168 	g.NextWindowData.SizeVal = size;
7169 	g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
7170 }
7171 
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)7172 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
7173 {
7174 	ImGuiContext& g = *GImGui;
7175 	g.NextWindowData.SizeConstraintCond = ImGuiCond_Always;
7176 	g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
7177 	g.NextWindowData.SizeCallback = custom_callback;
7178 	g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
7179 }
7180 
SetNextWindowContentSize(const ImVec2 & size)7181 void ImGui::SetNextWindowContentSize(const ImVec2& size)
7182 {
7183 	ImGuiContext& g = *GImGui;
7184 	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.
7185 	g.NextWindowData.ContentSizeCond = ImGuiCond_Always;
7186 }
7187 
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)7188 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
7189 {
7190 	ImGuiContext& g = *GImGui;
7191 	g.NextWindowData.CollapsedVal = collapsed;
7192 	g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
7193 }
7194 
SetNextWindowFocus()7195 void ImGui::SetNextWindowFocus()
7196 {
7197 	ImGuiContext& g = *GImGui;
7198 	g.NextWindowData.FocusCond = ImGuiCond_Always;  // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
7199 }
7200 
SetNextWindowBgAlpha(float alpha)7201 void ImGui::SetNextWindowBgAlpha(float alpha)
7202 {
7203 	ImGuiContext& g = *GImGui;
7204 	g.NextWindowData.BgAlphaVal = alpha;
7205 	g.NextWindowData.BgAlphaCond = ImGuiCond_Always;  // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
7206 }
7207 
7208 // In window space (not screen space!)
GetContentRegionMax()7209 ImVec2 ImGui::GetContentRegionMax()
7210 {
7211 	ImGuiWindow* window = GetCurrentWindowRead();
7212 	ImVec2 mx = window->ContentsRegionRect.Max;
7213 	if (window->DC.ColumnsSet)
7214 		mx.x = GetColumnOffset(window->DC.ColumnsSet->Current + 1) - window->WindowPadding.x;
7215 	return mx;
7216 }
7217 
GetContentRegionAvail()7218 ImVec2 ImGui::GetContentRegionAvail()
7219 {
7220 	ImGuiWindow* window = GetCurrentWindowRead();
7221 	return GetContentRegionMax() - (window->DC.CursorPos - window->Pos);
7222 }
7223 
GetContentRegionAvailWidth()7224 float ImGui::GetContentRegionAvailWidth()
7225 {
7226 	return GetContentRegionAvail().x;
7227 }
7228 
7229 // In window space (not screen space!)
GetWindowContentRegionMin()7230 ImVec2 ImGui::GetWindowContentRegionMin()
7231 {
7232 	ImGuiWindow* window = GetCurrentWindowRead();
7233 	return window->ContentsRegionRect.Min;
7234 }
7235 
GetWindowContentRegionMax()7236 ImVec2 ImGui::GetWindowContentRegionMax()
7237 {
7238 	ImGuiWindow* window = GetCurrentWindowRead();
7239 	return window->ContentsRegionRect.Max;
7240 }
7241 
GetWindowContentRegionWidth()7242 float ImGui::GetWindowContentRegionWidth()
7243 {
7244 	ImGuiWindow* window = GetCurrentWindowRead();
7245 	return window->ContentsRegionRect.Max.x - window->ContentsRegionRect.Min.x;
7246 }
7247 
GetTextLineHeight()7248 float ImGui::GetTextLineHeight()
7249 {
7250 	ImGuiContext& g = *GImGui;
7251 	return g.FontSize;
7252 }
7253 
GetTextLineHeightWithSpacing()7254 float ImGui::GetTextLineHeightWithSpacing()
7255 {
7256 	ImGuiContext& g = *GImGui;
7257 	return g.FontSize + g.Style.ItemSpacing.y;
7258 }
7259 
GetFrameHeight()7260 float ImGui::GetFrameHeight()
7261 {
7262 	ImGuiContext& g = *GImGui;
7263 	return g.FontSize + g.Style.FramePadding.y * 2.0f;
7264 }
7265 
GetFrameHeightWithSpacing()7266 float ImGui::GetFrameHeightWithSpacing()
7267 {
7268 	ImGuiContext& g = *GImGui;
7269 	return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
7270 }
7271 
GetWindowDrawList()7272 ImDrawList* ImGui::GetWindowDrawList()
7273 {
7274 	ImGuiWindow* window = GetCurrentWindow();
7275 	return window->DrawList;
7276 }
7277 
GetFont()7278 ImFont* ImGui::GetFont()
7279 {
7280 	return GImGui->Font;
7281 }
7282 
GetFontSize()7283 float ImGui::GetFontSize()
7284 {
7285 	return GImGui->FontSize;
7286 }
7287 
GetFontTexUvWhitePixel()7288 ImVec2 ImGui::GetFontTexUvWhitePixel()
7289 {
7290 	return GImGui->DrawListSharedData.TexUvWhitePixel;
7291 }
7292 
SetWindowFontScale(float scale)7293 void ImGui::SetWindowFontScale(float scale)
7294 {
7295 	ImGuiContext& g = *GImGui;
7296 	ImGuiWindow* window = GetCurrentWindow();
7297 	window->FontWindowScale = scale;
7298 	g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
7299 }
7300 
7301 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
7302 // 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()7303 ImVec2 ImGui::GetCursorPos()
7304 {
7305 	ImGuiWindow* window = GetCurrentWindowRead();
7306 	return window->DC.CursorPos - window->Pos + window->Scroll;
7307 }
7308 
GetCursorPosX()7309 float ImGui::GetCursorPosX()
7310 {
7311 	ImGuiWindow* window = GetCurrentWindowRead();
7312 	return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
7313 }
7314 
GetCursorPosY()7315 float ImGui::GetCursorPosY()
7316 {
7317 	ImGuiWindow* window = GetCurrentWindowRead();
7318 	return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
7319 }
7320 
SetCursorPos(const ImVec2 & local_pos)7321 void ImGui::SetCursorPos(const ImVec2& local_pos)
7322 {
7323 	ImGuiWindow* window = GetCurrentWindow();
7324 	window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
7325 	window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7326 }
7327 
SetCursorPosX(float x)7328 void ImGui::SetCursorPosX(float x)
7329 {
7330 	ImGuiWindow* window = GetCurrentWindow();
7331 	window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
7332 	window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
7333 }
7334 
SetCursorPosY(float y)7335 void ImGui::SetCursorPosY(float y)
7336 {
7337 	ImGuiWindow* window = GetCurrentWindow();
7338 	window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
7339 	window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
7340 }
7341 
GetCursorStartPos()7342 ImVec2 ImGui::GetCursorStartPos()
7343 {
7344 	ImGuiWindow* window = GetCurrentWindowRead();
7345 	return window->DC.CursorStartPos - window->Pos;
7346 }
7347 
GetCursorScreenPos()7348 ImVec2 ImGui::GetCursorScreenPos()
7349 {
7350 	ImGuiWindow* window = GetCurrentWindowRead();
7351 	return window->DC.CursorPos;
7352 }
7353 
SetCursorScreenPos(const ImVec2 & screen_pos)7354 void ImGui::SetCursorScreenPos(const ImVec2& screen_pos)
7355 {
7356 	ImGuiWindow* window = GetCurrentWindow();
7357 	window->DC.CursorPos = screen_pos;
7358 	window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7359 }
7360 
GetScrollX()7361 float ImGui::GetScrollX()
7362 {
7363 	return GImGui->CurrentWindow->Scroll.x;
7364 }
7365 
GetScrollY()7366 float ImGui::GetScrollY()
7367 {
7368 	return GImGui->CurrentWindow->Scroll.y;
7369 }
7370 
GetScrollMaxX()7371 float ImGui::GetScrollMaxX()
7372 {
7373 	return GetScrollMaxX(GImGui->CurrentWindow);
7374 }
7375 
GetScrollMaxY()7376 float ImGui::GetScrollMaxY()
7377 {
7378 	return GetScrollMaxY(GImGui->CurrentWindow);
7379 }
7380 
SetScrollX(float scroll_x)7381 void ImGui::SetScrollX(float scroll_x)
7382 {
7383 	ImGuiWindow* window = GetCurrentWindow();
7384 	window->ScrollTarget.x = scroll_x;
7385 	window->ScrollTargetCenterRatio.x = 0.0f;
7386 }
7387 
SetScrollY(float scroll_y)7388 void ImGui::SetScrollY(float scroll_y)
7389 {
7390 	ImGuiWindow* window = GetCurrentWindow();
7391 	window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight();  // title bar height canceled out when using ScrollTargetRelY
7392 	window->ScrollTargetCenterRatio.y = 0.0f;
7393 }
7394 
SetScrollFromPosY(float pos_y,float center_y_ratio)7395 void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio)
7396 {
7397 	// We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
7398 	ImGuiWindow* window = GetCurrentWindow();
7399 	IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
7400 	window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y);
7401 	window->ScrollTargetCenterRatio.y = center_y_ratio;
7402 
7403 	// Minor hack to to make scrolling to top/bottom of window take account of WindowPadding, it looks more right to the user this way
7404 	if (center_y_ratio <= 0.0f && window->ScrollTarget.y <= window->WindowPadding.y)
7405 		window->ScrollTarget.y = 0.0f;
7406 	else if (center_y_ratio >= 1.0f && window->ScrollTarget.y >= window->SizeContents.y - window->WindowPadding.y + GImGui->Style.ItemSpacing.y)
7407 		window->ScrollTarget.y = window->SizeContents.y;
7408 }
7409 
7410 // 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)7411 void ImGui::SetScrollHere(float center_y_ratio)
7412 {
7413 	ImGuiWindow* window = GetCurrentWindow();
7414 	float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y;                                                            // Top of last item, in window space
7415 	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.
7416 	SetScrollFromPosY(target_y, center_y_ratio);
7417 }
7418 
ActivateItem(ImGuiID id)7419 void ImGui::ActivateItem(ImGuiID id)
7420 {
7421 	ImGuiContext& g = *GImGui;
7422 	g.NavNextActivateId = id;
7423 }
7424 
SetKeyboardFocusHere(int offset)7425 void ImGui::SetKeyboardFocusHere(int offset)
7426 {
7427 	IM_ASSERT(offset >= -1);  // -1 is allowed but not below
7428 	ImGuiWindow* window = GetCurrentWindow();
7429 	window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
7430 	window->FocusIdxTabRequestNext = INT_MAX;
7431 }
7432 
SetItemDefaultFocus()7433 void ImGui::SetItemDefaultFocus()
7434 {
7435 	ImGuiContext& g = *GImGui;
7436 	ImGuiWindow* window = g.CurrentWindow;
7437 	if (!window->Appearing)
7438 		return;
7439 	if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
7440 	{
7441 		g.NavInitRequest = false;
7442 		g.NavInitResultId = g.NavWindow->DC.LastItemId;
7443 		g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
7444 		NavUpdateAnyRequestFlag();
7445 		if (!IsItemVisible())
7446 			SetScrollHere();
7447 	}
7448 }
7449 
SetStateStorage(ImGuiStorage * tree)7450 void ImGui::SetStateStorage(ImGuiStorage* tree)
7451 {
7452 	ImGuiWindow* window = GetCurrentWindow();
7453 	window->DC.StateStorage = tree ? tree : &window->StateStorage;
7454 }
7455 
GetStateStorage()7456 ImGuiStorage* ImGui::GetStateStorage()
7457 {
7458 	ImGuiWindow* window = GetCurrentWindowRead();
7459 	return window->DC.StateStorage;
7460 }
7461 
TextV(const char * fmt,va_list args)7462 void ImGui::TextV(const char* fmt, va_list args)
7463 {
7464 	ImGuiWindow* window = GetCurrentWindow();
7465 	if (window->SkipItems)
7466 		return;
7467 
7468 	ImGuiContext& g = *GImGui;
7469 	const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
7470 	TextUnformatted(g.TempBuffer, text_end);
7471 }
7472 
Text(const char * fmt,...)7473 void ImGui::Text(const char* fmt, ...)
7474 {
7475 	va_list args;
7476 	va_start(args, fmt);
7477 	TextV(fmt, args);
7478 	va_end(args);
7479 }
7480 
TextColoredV(const ImVec4 & col,const char * fmt,va_list args)7481 void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args)
7482 {
7483 	PushStyleColor(ImGuiCol_Text, col);
7484 	TextV(fmt, args);
7485 	PopStyleColor();
7486 }
7487 
TextColored(const ImVec4 & col,const char * fmt,...)7488 void ImGui::TextColored(const ImVec4& col, const char* fmt, ...)
7489 {
7490 	va_list args;
7491 	va_start(args, fmt);
7492 	TextColoredV(col, fmt, args);
7493 	va_end(args);
7494 }
7495 
TextDisabledV(const char * fmt,va_list args)7496 void ImGui::TextDisabledV(const char* fmt, va_list args)
7497 {
7498 	PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]);
7499 	TextV(fmt, args);
7500 	PopStyleColor();
7501 }
7502 
TextDisabled(const char * fmt,...)7503 void ImGui::TextDisabled(const char* fmt, ...)
7504 {
7505 	va_list args;
7506 	va_start(args, fmt);
7507 	TextDisabledV(fmt, args);
7508 	va_end(args);
7509 }
7510 
TextWrappedV(const char * fmt,va_list args)7511 void ImGui::TextWrappedV(const char* fmt, va_list args)
7512 {
7513 	bool need_wrap = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f);  // Keep existing wrap position is one ia already set
7514 	if (need_wrap) PushTextWrapPos(0.0f);
7515 	TextV(fmt, args);
7516 	if (need_wrap) PopTextWrapPos();
7517 }
7518 
TextWrapped(const char * fmt,...)7519 void ImGui::TextWrapped(const char* fmt, ...)
7520 {
7521 	va_list args;
7522 	va_start(args, fmt);
7523 	TextWrappedV(fmt, args);
7524 	va_end(args);
7525 }
7526 
TextUnformatted(const char * text,const char * text_end)7527 void ImGui::TextUnformatted(const char* text, const char* text_end)
7528 {
7529 	ImGuiWindow* window = GetCurrentWindow();
7530 	if (window->SkipItems)
7531 		return;
7532 
7533 	ImGuiContext& g = *GImGui;
7534 	IM_ASSERT(text != NULL);
7535 	const char* text_begin = text;
7536 	if (text_end == NULL)
7537 		text_end = text + strlen(text);  // FIXME-OPT
7538 
7539 	const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset);
7540 	const float wrap_pos_x = window->DC.TextWrapPos;
7541 	const bool wrap_enabled = wrap_pos_x >= 0.0f;
7542 	if (text_end - text > 2000 && !wrap_enabled)
7543 	{
7544 		// Long text!
7545 		// Perform manual coarse clipping to optimize for long multi-line text
7546 		// From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.
7547 		// 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.
7548 		const char* line = text;
7549 		const float line_height = GetTextLineHeight();
7550 		const ImRect clip_rect = window->ClipRect;
7551 		ImVec2 text_size(0, 0);
7552 
7553 		if (text_pos.y <= clip_rect.Max.y)
7554 		{
7555 			ImVec2 pos = text_pos;
7556 
7557 			// Lines to skip (can't skip when logging text)
7558 			if (!g.LogEnabled)
7559 			{
7560 				int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height);
7561 				if (lines_skippable > 0)
7562 				{
7563 					int lines_skipped = 0;
7564 					while (line < text_end && lines_skipped < lines_skippable)
7565 					{
7566 						const char* line_end = strchr(line, '\n');
7567 						if (!line_end)
7568 							line_end = text_end;
7569 						line = line_end + 1;
7570 						lines_skipped++;
7571 					}
7572 					pos.y += lines_skipped * line_height;
7573 				}
7574 			}
7575 
7576 			// Lines to render
7577 			if (line < text_end)
7578 			{
7579 				ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height));
7580 				while (line < text_end)
7581 				{
7582 					const char* line_end = strchr(line, '\n');
7583 					if (IsClippedEx(line_rect, 0, false))
7584 						break;
7585 
7586 					const ImVec2 line_size = CalcTextSize(line, line_end, false);
7587 					text_size.x = ImMax(text_size.x, line_size.x);
7588 					RenderText(pos, line, line_end, false);
7589 					if (!line_end)
7590 						line_end = text_end;
7591 					line = line_end + 1;
7592 					line_rect.Min.y += line_height;
7593 					line_rect.Max.y += line_height;
7594 					pos.y += line_height;
7595 				}
7596 
7597 				// Count remaining lines
7598 				int lines_skipped = 0;
7599 				while (line < text_end)
7600 				{
7601 					const char* line_end = strchr(line, '\n');
7602 					if (!line_end)
7603 						line_end = text_end;
7604 					line = line_end + 1;
7605 					lines_skipped++;
7606 				}
7607 				pos.y += lines_skipped * line_height;
7608 			}
7609 
7610 			text_size.y += (pos - text_pos).y;
7611 		}
7612 
7613 		ImRect bb(text_pos, text_pos + text_size);
7614 		ItemSize(bb);
7615 		ItemAdd(bb, 0);
7616 	}
7617 	else
7618 	{
7619 		const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;
7620 		const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);
7621 
7622 		// Account of baseline offset
7623 		ImRect bb(text_pos, text_pos + text_size);
7624 		ItemSize(text_size);
7625 		if (!ItemAdd(bb, 0))
7626 			return;
7627 
7628 		// Render (we don't hide text after ## in this end-user function)
7629 		RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width);
7630 	}
7631 }
7632 
AlignTextToFramePadding()7633 void ImGui::AlignTextToFramePadding()
7634 {
7635 	ImGuiWindow* window = GetCurrentWindow();
7636 	if (window->SkipItems)
7637 		return;
7638 
7639 	ImGuiContext& g = *GImGui;
7640 	window->DC.CurrentLineHeight = ImMax(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y * 2);
7641 	window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
7642 }
7643 
7644 // Add a label+text combo aligned to other label+value widgets
LabelTextV(const char * label,const char * fmt,va_list args)7645 void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
7646 {
7647 	ImGuiWindow* window = GetCurrentWindow();
7648 	if (window->SkipItems)
7649 		return;
7650 
7651 	ImGuiContext& g = *GImGui;
7652 	const ImGuiStyle& style = g.Style;
7653 	const float w = CalcItemWidth();
7654 
7655 	const ImVec2 label_size = CalcTextSize(label, NULL, true);
7656 	const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2));
7657 	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);
7658 	ItemSize(total_bb, style.FramePadding.y);
7659 	if (!ItemAdd(total_bb, 0))
7660 		return;
7661 
7662 	// Render
7663 	const char* value_text_begin = &g.TempBuffer[0];
7664 	const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
7665 	RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f, 0.5f));
7666 	if (label_size.x > 0.0f)
7667 		RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
7668 }
7669 
LabelText(const char * label,const char * fmt,...)7670 void ImGui::LabelText(const char* label, const char* fmt, ...)
7671 {
7672 	va_list args;
7673 	va_start(args, fmt);
7674 	LabelTextV(label, fmt, args);
7675 	va_end(args);
7676 }
7677 
ButtonBehavior(const ImRect & bb,ImGuiID id,bool * out_hovered,bool * out_held,ImGuiButtonFlags flags)7678 bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)
7679 {
7680 	ImGuiContext& g = *GImGui;
7681 	ImGuiWindow* window = GetCurrentWindow();
7682 
7683 	if (flags & ImGuiButtonFlags_Disabled)
7684 	{
7685 		if (out_hovered) *out_hovered = false;
7686 		if (out_held) *out_held = false;
7687 		if (g.ActiveId == id) ClearActiveID();
7688 		return false;
7689 	}
7690 
7691 	// Default behavior requires click+release on same spot
7692 	if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0)
7693 		flags |= ImGuiButtonFlags_PressedOnClickRelease;
7694 
7695 	ImGuiWindow* backup_hovered_window = g.HoveredWindow;
7696 	if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window)
7697 		g.HoveredWindow = window;
7698 
7699 	bool pressed = false;
7700 	bool hovered = ItemHoverable(bb, id);
7701 
7702 	// Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button
7703 	if ((flags & ImGuiButtonFlags_PressedOnDragDropHold) && g.DragDropActive && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers))
7704 		if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
7705 		{
7706 			hovered = true;
7707 			SetHoveredID(id);
7708 			if (CalcTypematicPressedRepeatAmount(g.HoveredIdTimer + 0.0001f, g.HoveredIdTimer + 0.0001f - g.IO.DeltaTime, 0.01f, 0.70f))  // FIXME: Our formula for CalcTypematicPressedRepeatAmount() is fishy
7709 			{
7710 				pressed = true;
7711 				FocusWindow(window);
7712 			}
7713 		}
7714 
7715 	if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window)
7716 		g.HoveredWindow = backup_hovered_window;
7717 
7718 	// 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.
7719 	if (hovered && (flags & ImGuiButtonFlags_AllowItemOverlap) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0))
7720 		hovered = false;
7721 
7722 	// Mouse
7723 	if (hovered)
7724 	{
7725 		if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
7726 		{
7727 			//                        | CLICKING        | HOLDING with ImGuiButtonFlags_Repeat
7728 			// PressedOnClickRelease  |  <on release>*  |  <on repeat> <on repeat> .. (NOT on release)  <-- MOST COMMON! (*) only if both click/release were over bounds
7729 			// PressedOnClick         |  <on click>     |  <on click> <on repeat> <on repeat> ..
7730 			// PressedOnRelease       |  <on release>   |  <on repeat> <on repeat> .. (NOT on release)
7731 			// PressedOnDoubleClick   |  <on dclick>    |  <on dclick> <on repeat> <on repeat> ..
7732 			// FIXME-NAV: We don't honor those different behaviors.
7733 			if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0])
7734 			{
7735 				SetActiveID(id, window);
7736 				if (!(flags & ImGuiButtonFlags_NoNavFocus))
7737 					SetFocusID(id, window);
7738 				FocusWindow(window);
7739 			}
7740 			if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0]))
7741 			{
7742 				pressed = true;
7743 				if (flags & ImGuiButtonFlags_NoHoldingActiveID)
7744 					ClearActiveID();
7745 				else
7746 					SetActiveID(id, window);  // Hold on ID
7747 				FocusWindow(window);
7748 			}
7749 			if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0])
7750 			{
7751 				if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay))  // Repeat mode trumps <on release>
7752 					pressed = true;
7753 				ClearActiveID();
7754 			}
7755 
7756 			// 'Repeat' mode acts when held regardless of _PressedOn flags (see table above).
7757 			// Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings.
7758 			if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true))
7759 				pressed = true;
7760 		}
7761 
7762 		if (pressed)
7763 			g.NavDisableHighlight = true;
7764 	}
7765 
7766 	// Gamepad/Keyboard navigation
7767 	// We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse.
7768 	if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId))
7769 		hovered = true;
7770 
7771 	if (g.NavActivateDownId == id)
7772 	{
7773 		bool nav_activated_by_code = (g.NavActivateId == id);
7774 		bool nav_activated_by_inputs = IsNavInputPressed(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiInputReadMode_Repeat : ImGuiInputReadMode_Pressed);
7775 		if (nav_activated_by_code || nav_activated_by_inputs)
7776 			pressed = true;
7777 		if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id)
7778 		{
7779 			// Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button.
7780 			g.NavActivateId = id;  // This is so SetActiveId assign a Nav source
7781 			SetActiveID(id, window);
7782 			if (!(flags & ImGuiButtonFlags_NoNavFocus))
7783 				SetFocusID(id, window);
7784 			g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
7785 		}
7786 	}
7787 
7788 	bool held = false;
7789 	if (g.ActiveId == id)
7790 	{
7791 		if (g.ActiveIdSource == ImGuiInputSource_Mouse)
7792 		{
7793 			if (g.ActiveIdIsJustActivated)
7794 				g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;
7795 			if (g.IO.MouseDown[0])
7796 			{
7797 				held = true;
7798 			}
7799 			else
7800 			{
7801 				if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease))
7802 					if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay))  // Repeat mode trumps <on release>
7803 						if (!g.DragDropActive)
7804 							pressed = true;
7805 				ClearActiveID();
7806 			}
7807 			if (!(flags & ImGuiButtonFlags_NoNavFocus))
7808 				g.NavDisableHighlight = true;
7809 		}
7810 		else if (g.ActiveIdSource == ImGuiInputSource_Nav)
7811 		{
7812 			if (g.NavActivateDownId != id)
7813 				ClearActiveID();
7814 		}
7815 	}
7816 
7817 	if (out_hovered) *out_hovered = hovered;
7818 	if (out_held) *out_held = held;
7819 
7820 	return pressed;
7821 }
7822 
ButtonEx(const char * label,const ImVec2 & size_arg,ImGuiButtonFlags flags)7823 bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags)
7824 {
7825 	ImGuiWindow* window = GetCurrentWindow();
7826 	if (window->SkipItems)
7827 		return false;
7828 
7829 	ImGuiContext& g = *GImGui;
7830 	const ImGuiStyle& style = g.Style;
7831 	const ImGuiID id = window->GetID(label);
7832 	const ImVec2 label_size = CalcTextSize(label, NULL, true);
7833 
7834 	ImVec2 pos = window->DC.CursorPos;
7835 	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)
7836 		pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y;
7837 	ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);
7838 
7839 	const ImRect bb(pos, pos + size);
7840 	ItemSize(bb, style.FramePadding.y);
7841 	if (!ItemAdd(bb, id))
7842 		return false;
7843 
7844 	if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat)
7845 		flags |= ImGuiButtonFlags_Repeat;
7846 	bool hovered, held;
7847 	bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
7848 
7849 	// Render
7850 	const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
7851 	RenderNavHighlight(bb, id);
7852 	RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
7853 	RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb);
7854 
7855 	// Automatically close popups
7856 	//if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
7857 	//    CloseCurrentPopup();
7858 
7859 	return pressed;
7860 }
7861 
Button(const char * label,const ImVec2 & size_arg)7862 bool ImGui::Button(const char* label, const ImVec2& size_arg)
7863 {
7864 	return ButtonEx(label, size_arg, 0);
7865 }
7866 
7867 // Small buttons fits within text without additional vertical spacing.
SmallButton(const char * label)7868 bool ImGui::SmallButton(const char* label)
7869 {
7870 	ImGuiContext& g = *GImGui;
7871 	float backup_padding_y = g.Style.FramePadding.y;
7872 	g.Style.FramePadding.y = 0.0f;
7873 	bool pressed = ButtonEx(label, ImVec2(0, 0), ImGuiButtonFlags_AlignTextBaseLine);
7874 	g.Style.FramePadding.y = backup_padding_y;
7875 	return pressed;
7876 }
7877 
7878 // Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack.
7879 // 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)7880 bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg)
7881 {
7882 	ImGuiWindow* window = GetCurrentWindow();
7883 	if (window->SkipItems)
7884 		return false;
7885 
7886 	const ImGuiID id = window->GetID(str_id);
7887 	ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f);
7888 	const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
7889 	ItemSize(bb);
7890 	if (!ItemAdd(bb, id))
7891 		return false;
7892 
7893 	bool hovered, held;
7894 	bool pressed = ButtonBehavior(bb, id, &hovered, &held);
7895 
7896 	return pressed;
7897 }
7898 
7899 // Button to close a window
CloseButton(ImGuiID id,const ImVec2 & pos,float radius)7900 bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius)
7901 {
7902 	ImGuiContext& g = *GImGui;
7903 	ImGuiWindow* window = g.CurrentWindow;
7904 
7905 	// We intentionally allow interaction when clipped so that a mechanical Alt,Right,Validate sequence close a window.
7906 	// (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible).
7907 	const ImRect bb(pos - ImVec2(radius, radius), pos + ImVec2(radius, radius));
7908 	bool is_clipped = !ItemAdd(bb, id);
7909 
7910 	bool hovered, held;
7911 	bool pressed = ButtonBehavior(bb, id, &hovered, &held);
7912 	if (is_clipped)
7913 		return pressed;
7914 
7915 	// Render
7916 	const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton);
7917 	ImVec2 center = bb.GetCenter();
7918 	window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), col, 12);
7919 
7920 	const float cross_extent = (radius * 0.7071f) - 1.0f;
7921 	if (hovered)
7922 	{
7923 		center -= ImVec2(0.5f, 0.5f);
7924 		window->DrawList->AddLine(center + ImVec2(+cross_extent, +cross_extent), center + ImVec2(-cross_extent, -cross_extent), GetColorU32(ImGuiCol_Text));
7925 		window->DrawList->AddLine(center + ImVec2(+cross_extent, -cross_extent), center + ImVec2(-cross_extent, +cross_extent), GetColorU32(ImGuiCol_Text));
7926 	}
7927 	return pressed;
7928 }
7929 
7930 // [Internal]
ArrowButton(ImGuiID id,ImGuiDir dir,ImVec2 padding,ImGuiButtonFlags flags)7931 bool ImGui::ArrowButton(ImGuiID id, ImGuiDir dir, ImVec2 padding, ImGuiButtonFlags flags)
7932 {
7933 	ImGuiContext& g = *GImGui;
7934 	ImGuiWindow* window = g.CurrentWindow;
7935 	if (window->SkipItems)
7936 		return false;
7937 
7938 	const ImGuiStyle& style = g.Style;
7939 
7940 	const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + padding.x * 2.0f, g.FontSize + padding.y * 2.0f));
7941 	ItemSize(bb, style.FramePadding.y);
7942 	if (!ItemAdd(bb, id))
7943 		return false;
7944 
7945 	bool hovered, held;
7946 	bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
7947 
7948 	const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
7949 	RenderNavHighlight(bb, id);
7950 	RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
7951 	RenderTriangle(bb.Min + padding, dir, 1.0f);
7952 
7953 	return pressed;
7954 }
7955 
Image(ImTextureID user_texture_id,const ImVec2 & size,const ImVec2 & uv0,const ImVec2 & uv1,const ImVec4 & tint_col,const ImVec4 & border_col)7956 void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
7957 {
7958 	ImGuiWindow* window = GetCurrentWindow();
7959 	if (window->SkipItems)
7960 		return;
7961 
7962 	ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
7963 	if (border_col.w > 0.0f)
7964 		bb.Max += ImVec2(2, 2);
7965 	ItemSize(bb);
7966 	if (!ItemAdd(bb, 0))
7967 		return;
7968 
7969 	if (border_col.w > 0.0f)
7970 	{
7971 		window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f);
7972 		window->DrawList->AddImage(user_texture_id, bb.Min + ImVec2(1, 1), bb.Max - ImVec2(1, 1), uv0, uv1, GetColorU32(tint_col));
7973 	}
7974 	else
7975 	{
7976 		window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col));
7977 	}
7978 }
7979 
7980 // frame_padding < 0: uses FramePadding from style (default)
7981 // frame_padding = 0: no framing
7982 // frame_padding > 0: set framing size
7983 // 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)7984 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)
7985 {
7986 	ImGuiWindow* window = GetCurrentWindow();
7987 	if (window->SkipItems)
7988 		return false;
7989 
7990 	ImGuiContext& g = *GImGui;
7991 	const ImGuiStyle& style = g.Style;
7992 
7993 	// Default to using texture ID as ID. User can still push string/integer prefixes.
7994 	// We could hash the size/uv to create a unique ID but that would prevent the user from animating UV.
7995 	PushID((void*)user_texture_id);
7996 	const ImGuiID id = window->GetID("#image");
7997 	PopID();
7998 
7999 	const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding;
8000 	const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2);
8001 	const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size);
8002 	ItemSize(bb);
8003 	if (!ItemAdd(bb, id))
8004 		return false;
8005 
8006 	bool hovered, held;
8007 	bool pressed = ButtonBehavior(bb, id, &hovered, &held);
8008 
8009 	// Render
8010 	const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
8011 	RenderNavHighlight(bb, id);
8012 	RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding));
8013 	if (bg_col.w > 0.0f)
8014 		window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col));
8015 	window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col));
8016 
8017 	return pressed;
8018 }
8019 
8020 // Start logging ImGui output to TTY
LogToTTY(int max_depth)8021 void ImGui::LogToTTY(int max_depth)
8022 {
8023 	ImGuiContext& g = *GImGui;
8024 	if (g.LogEnabled)
8025 		return;
8026 	ImGuiWindow* window = g.CurrentWindow;
8027 
8028 	IM_ASSERT(g.LogFile == NULL);
8029 	g.LogFile = stdout;
8030 	g.LogEnabled = true;
8031 	g.LogStartDepth = window->DC.TreeDepth;
8032 	if (max_depth >= 0)
8033 		g.LogAutoExpandMaxDepth = max_depth;
8034 }
8035 
8036 // Start logging ImGui output to given file
LogToFile(int max_depth,const char * filename)8037 void ImGui::LogToFile(int max_depth, const char* filename)
8038 {
8039 	ImGuiContext& g = *GImGui;
8040 	if (g.LogEnabled)
8041 		return;
8042 	ImGuiWindow* window = g.CurrentWindow;
8043 
8044 	if (!filename)
8045 	{
8046 		filename = g.IO.LogFilename;
8047 		if (!filename)
8048 			return;
8049 	}
8050 
8051 	IM_ASSERT(g.LogFile == NULL);
8052 	g.LogFile = ImFileOpen(filename, "ab");
8053 	if (!g.LogFile)
8054 	{
8055 		IM_ASSERT(g.LogFile != NULL);  // Consider this an error
8056 		return;
8057 	}
8058 	g.LogEnabled = true;
8059 	g.LogStartDepth = window->DC.TreeDepth;
8060 	if (max_depth >= 0)
8061 		g.LogAutoExpandMaxDepth = max_depth;
8062 }
8063 
8064 // Start logging ImGui output to clipboard
LogToClipboard(int max_depth)8065 void ImGui::LogToClipboard(int max_depth)
8066 {
8067 	ImGuiContext& g = *GImGui;
8068 	if (g.LogEnabled)
8069 		return;
8070 	ImGuiWindow* window = g.CurrentWindow;
8071 
8072 	IM_ASSERT(g.LogFile == NULL);
8073 	g.LogFile = NULL;
8074 	g.LogEnabled = true;
8075 	g.LogStartDepth = window->DC.TreeDepth;
8076 	if (max_depth >= 0)
8077 		g.LogAutoExpandMaxDepth = max_depth;
8078 }
8079 
LogFinish()8080 void ImGui::LogFinish()
8081 {
8082 	ImGuiContext& g = *GImGui;
8083 	if (!g.LogEnabled)
8084 		return;
8085 
8086 	LogText(IM_NEWLINE);
8087 	if (g.LogFile != NULL)
8088 	{
8089 		if (g.LogFile == stdout)
8090 			fflush(g.LogFile);
8091 		else
8092 			fclose(g.LogFile);
8093 		g.LogFile = NULL;
8094 	}
8095 	if (g.LogClipboard->size() > 1)
8096 	{
8097 		SetClipboardText(g.LogClipboard->begin());
8098 		g.LogClipboard->clear();
8099 	}
8100 	g.LogEnabled = false;
8101 }
8102 
8103 // Helper to display logging buttons
LogButtons()8104 void ImGui::LogButtons()
8105 {
8106 	ImGuiContext& g = *GImGui;
8107 
8108 	PushID("LogButtons");
8109 	const bool log_to_tty = Button("Log To TTY");
8110 	SameLine();
8111 	const bool log_to_file = Button("Log To File");
8112 	SameLine();
8113 	const bool log_to_clipboard = Button("Log To Clipboard");
8114 	SameLine();
8115 	PushItemWidth(80.0f);
8116 	PushAllowKeyboardFocus(false);
8117 	SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL);
8118 	PopAllowKeyboardFocus();
8119 	PopItemWidth();
8120 	PopID();
8121 
8122 	// Start logging at the end of the function so that the buttons don't appear in the log
8123 	if (log_to_tty)
8124 		LogToTTY(g.LogAutoExpandMaxDepth);
8125 	if (log_to_file)
8126 		LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename);
8127 	if (log_to_clipboard)
8128 		LogToClipboard(g.LogAutoExpandMaxDepth);
8129 }
8130 
TreeNodeBehaviorIsOpen(ImGuiID id,ImGuiTreeNodeFlags flags)8131 bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags)
8132 {
8133 	if (flags & ImGuiTreeNodeFlags_Leaf)
8134 		return true;
8135 
8136 	// We only write to the tree storage if the user clicks (or explicitely use SetNextTreeNode*** functions)
8137 	ImGuiContext& g = *GImGui;
8138 	ImGuiWindow* window = g.CurrentWindow;
8139 	ImGuiStorage* storage = window->DC.StateStorage;
8140 
8141 	bool is_open;
8142 	if (g.NextTreeNodeOpenCond != 0)
8143 	{
8144 		if (g.NextTreeNodeOpenCond & ImGuiCond_Always)
8145 		{
8146 			is_open = g.NextTreeNodeOpenVal;
8147 			storage->SetInt(id, is_open);
8148 		}
8149 		else
8150 		{
8151 			// We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently.
8152 			const int stored_value = storage->GetInt(id, -1);
8153 			if (stored_value == -1)
8154 			{
8155 				is_open = g.NextTreeNodeOpenVal;
8156 				storage->SetInt(id, is_open);
8157 			}
8158 			else
8159 			{
8160 				is_open = stored_value != 0;
8161 			}
8162 		}
8163 		g.NextTreeNodeOpenCond = 0;
8164 	}
8165 	else
8166 	{
8167 		is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;
8168 	}
8169 
8170 	// When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior).
8171 	// NB- If we are above max depth we still allow manually opened nodes to be logged.
8172 	if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth)
8173 		is_open = true;
8174 
8175 	return is_open;
8176 }
8177 
TreeNodeBehavior(ImGuiID id,ImGuiTreeNodeFlags flags,const char * label,const char * label_end)8178 bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end)
8179 {
8180 	ImGuiWindow* window = GetCurrentWindow();
8181 	if (window->SkipItems)
8182 		return false;
8183 
8184 	ImGuiContext& g = *GImGui;
8185 	const ImGuiStyle& style = g.Style;
8186 	const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;
8187 	const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f);
8188 
8189 	if (!label_end)
8190 		label_end = FindRenderedTextEnd(label);
8191 	const ImVec2 label_size = CalcTextSize(label, label_end, false);
8192 
8193 	// We vertically grow up to current line height up the typical widget height.
8194 	const float text_base_offset_y = ImMax(padding.y, window->DC.CurrentLineTextBaseOffset);  // Latch before ItemSize changes it
8195 	const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + style.FramePadding.y * 2), label_size.y + padding.y * 2);
8196 	ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height));
8197 	if (display_frame)
8198 	{
8199 		// Framed header expand a little outside the default padding
8200 		frame_bb.Min.x -= (float)(int)(window->WindowPadding.x * 0.5f) - 1;
8201 		frame_bb.Max.x += (float)(int)(window->WindowPadding.x * 0.5f) - 1;
8202 	}
8203 
8204 	const float text_offset_x = (g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2));         // Collapser arrow width + Spacing
8205 	const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x * 2 : 0.0f);  // Include collapser
8206 	ItemSize(ImVec2(text_width, frame_height), text_base_offset_y);
8207 
8208 	// For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
8209 	// (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)
8210 	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);
8211 	bool is_open = TreeNodeBehaviorIsOpen(id, flags);
8212 
8213 	// Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child.
8214 	// For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop().
8215 	// This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero.
8216 	if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavCloseFromChild) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
8217 		window->DC.TreeDepthMayCloseOnPop |= (1 << window->DC.TreeDepth);
8218 
8219 	bool item_add = ItemAdd(interact_bb, id);
8220 	window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDisplayRect;
8221 	window->DC.LastItemDisplayRect = frame_bb;
8222 
8223 	if (!item_add)
8224 	{
8225 		if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
8226 			TreePushRawID(id);
8227 		return is_open;
8228 	}
8229 
8230 	// Flags that affects opening behavior:
8231 	// - 0(default) ..................... single-click anywhere to open
8232 	// - OpenOnDoubleClick .............. double-click anywhere to open
8233 	// - OpenOnArrow .................... single-click on arrow to open
8234 	// - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open
8235 	ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowItemOverlap) ? ImGuiButtonFlags_AllowItemOverlap : 0);
8236 	if (!(flags & ImGuiTreeNodeFlags_Leaf))
8237 		button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
8238 	if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
8239 		button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0);
8240 
8241 	bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags);
8242 	if (!(flags & ImGuiTreeNodeFlags_Leaf))
8243 	{
8244 		bool toggled = false;
8245 		if (pressed)
8246 		{
8247 			toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) || (g.NavActivateId == id);
8248 			if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
8249 				toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)) && (!g.NavDisableMouseHover);
8250 			if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
8251 				toggled |= g.IO.MouseDoubleClicked[0];
8252 			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.
8253 				toggled = false;
8254 		}
8255 
8256 		if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open)
8257 		{
8258 			toggled = true;
8259 			NavMoveRequestCancel();
8260 		}
8261 		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?
8262 		{
8263 			toggled = true;
8264 			NavMoveRequestCancel();
8265 		}
8266 
8267 		if (toggled)
8268 		{
8269 			is_open = !is_open;
8270 			window->DC.StateStorage->SetInt(id, is_open);
8271 		}
8272 	}
8273 	if (flags & ImGuiTreeNodeFlags_AllowItemOverlap)
8274 		SetItemAllowOverlap();
8275 
8276 	// Render
8277 	const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
8278 	const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y);
8279 	if (display_frame)
8280 	{
8281 		// Framed type
8282 		RenderFrame(frame_bb.Min, frame_bb.Max, col, true, style.FrameRounding);
8283 		RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin);
8284 		RenderTriangle(frame_bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f);
8285 		if (g.LogEnabled)
8286 		{
8287 			// 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.
8288 			const char log_prefix[] = "\n##";
8289 			const char log_suffix[] = "##";
8290 			LogRenderedText(&text_pos, log_prefix, log_prefix + 3);
8291 			RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
8292 			LogRenderedText(&text_pos, log_suffix + 1, log_suffix + 3);
8293 		}
8294 		else
8295 		{
8296 			RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
8297 		}
8298 	}
8299 	else
8300 	{
8301 		// Unframed typed for tree nodes
8302 		if (hovered || (flags & ImGuiTreeNodeFlags_Selected))
8303 		{
8304 			RenderFrame(frame_bb.Min, frame_bb.Max, col, false);
8305 			RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin);
8306 		}
8307 
8308 		if (flags & ImGuiTreeNodeFlags_Bullet)
8309 			RenderBullet(frame_bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize * 0.50f + text_base_offset_y));
8310 		else if (!(flags & ImGuiTreeNodeFlags_Leaf))
8311 			RenderTriangle(frame_bb.Min + ImVec2(padding.x, g.FontSize * 0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f);
8312 		if (g.LogEnabled)
8313 			LogRenderedText(&text_pos, ">");
8314 		RenderText(text_pos, label, label_end, false);
8315 	}
8316 
8317 	if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
8318 		TreePushRawID(id);
8319 	return is_open;
8320 }
8321 
8322 // CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag).
8323 // 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)8324 bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags)
8325 {
8326 	ImGuiWindow* window = GetCurrentWindow();
8327 	if (window->SkipItems)
8328 		return false;
8329 
8330 	return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen, label);
8331 }
8332 
CollapsingHeader(const char * label,bool * p_open,ImGuiTreeNodeFlags flags)8333 bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags)
8334 {
8335 	ImGuiWindow* window = GetCurrentWindow();
8336 	if (window->SkipItems)
8337 		return false;
8338 
8339 	if (p_open && !*p_open)
8340 		return false;
8341 
8342 	ImGuiID id = window->GetID(label);
8343 	bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen | (p_open ? ImGuiTreeNodeFlags_AllowItemOverlap : 0), label);
8344 	if (p_open)
8345 	{
8346 		// Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.
8347 		ImGuiContext& g = *GImGui;
8348 		float button_sz = g.FontSize * 0.5f;
8349 		ImGuiItemHoveredDataBackup last_item_backup;
8350 		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))
8351 			*p_open = false;
8352 		last_item_backup.Restore();
8353 	}
8354 
8355 	return is_open;
8356 }
8357 
TreeNodeEx(const char * label,ImGuiTreeNodeFlags flags)8358 bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags)
8359 {
8360 	ImGuiWindow* window = GetCurrentWindow();
8361 	if (window->SkipItems)
8362 		return false;
8363 
8364 	return TreeNodeBehavior(window->GetID(label), flags, label, NULL);
8365 }
8366 
TreeNodeExV(const char * str_id,ImGuiTreeNodeFlags flags,const char * fmt,va_list args)8367 bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
8368 {
8369 	ImGuiWindow* window = GetCurrentWindow();
8370 	if (window->SkipItems)
8371 		return false;
8372 
8373 	ImGuiContext& g = *GImGui;
8374 	const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
8375 	return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end);
8376 }
8377 
TreeNodeExV(const void * ptr_id,ImGuiTreeNodeFlags flags,const char * fmt,va_list args)8378 bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
8379 {
8380 	ImGuiWindow* window = GetCurrentWindow();
8381 	if (window->SkipItems)
8382 		return false;
8383 
8384 	ImGuiContext& g = *GImGui;
8385 	const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
8386 	return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end);
8387 }
8388 
TreeNodeV(const char * str_id,const char * fmt,va_list args)8389 bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)
8390 {
8391 	return TreeNodeExV(str_id, 0, fmt, args);
8392 }
8393 
TreeNodeV(const void * ptr_id,const char * fmt,va_list args)8394 bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)
8395 {
8396 	return TreeNodeExV(ptr_id, 0, fmt, args);
8397 }
8398 
TreeNodeEx(const char * str_id,ImGuiTreeNodeFlags flags,const char * fmt,...)8399 bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
8400 {
8401 	va_list args;
8402 	va_start(args, fmt);
8403 	bool is_open = TreeNodeExV(str_id, flags, fmt, args);
8404 	va_end(args);
8405 	return is_open;
8406 }
8407 
TreeNodeEx(const void * ptr_id,ImGuiTreeNodeFlags flags,const char * fmt,...)8408 bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
8409 {
8410 	va_list args;
8411 	va_start(args, fmt);
8412 	bool is_open = TreeNodeExV(ptr_id, flags, fmt, args);
8413 	va_end(args);
8414 	return is_open;
8415 }
8416 
TreeNode(const char * str_id,const char * fmt,...)8417 bool ImGui::TreeNode(const char* str_id, const char* fmt, ...)
8418 {
8419 	va_list args;
8420 	va_start(args, fmt);
8421 	bool is_open = TreeNodeExV(str_id, 0, fmt, args);
8422 	va_end(args);
8423 	return is_open;
8424 }
8425 
TreeNode(const void * ptr_id,const char * fmt,...)8426 bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)
8427 {
8428 	va_list args;
8429 	va_start(args, fmt);
8430 	bool is_open = TreeNodeExV(ptr_id, 0, fmt, args);
8431 	va_end(args);
8432 	return is_open;
8433 }
8434 
TreeNode(const char * label)8435 bool ImGui::TreeNode(const char* label)
8436 {
8437 	ImGuiWindow* window = GetCurrentWindow();
8438 	if (window->SkipItems)
8439 		return false;
8440 	return TreeNodeBehavior(window->GetID(label), 0, label, NULL);
8441 }
8442 
TreeAdvanceToLabelPos()8443 void ImGui::TreeAdvanceToLabelPos()
8444 {
8445 	ImGuiContext& g = *GImGui;
8446 	g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing();
8447 }
8448 
8449 // Horizontal distance preceding label when using TreeNode() or Bullet()
GetTreeNodeToLabelSpacing()8450 float ImGui::GetTreeNodeToLabelSpacing()
8451 {
8452 	ImGuiContext& g = *GImGui;
8453 	return g.FontSize + (g.Style.FramePadding.x * 2.0f);
8454 }
8455 
SetNextTreeNodeOpen(bool is_open,ImGuiCond cond)8456 void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiCond cond)
8457 {
8458 	ImGuiContext& g = *GImGui;
8459 	if (g.CurrentWindow->SkipItems)
8460 		return;
8461 	g.NextTreeNodeOpenVal = is_open;
8462 	g.NextTreeNodeOpenCond = cond ? cond : ImGuiCond_Always;
8463 }
8464 
PushID(const char * str_id)8465 void ImGui::PushID(const char* str_id)
8466 {
8467 	ImGuiWindow* window = GetCurrentWindowRead();
8468 	window->IDStack.push_back(window->GetID(str_id));
8469 }
8470 
PushID(const char * str_id_begin,const char * str_id_end)8471 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
8472 {
8473 	ImGuiWindow* window = GetCurrentWindowRead();
8474 	window->IDStack.push_back(window->GetID(str_id_begin, str_id_end));
8475 }
8476 
PushID(const void * ptr_id)8477 void ImGui::PushID(const void* ptr_id)
8478 {
8479 	ImGuiWindow* window = GetCurrentWindowRead();
8480 	window->IDStack.push_back(window->GetID(ptr_id));
8481 }
8482 
PushID(int int_id)8483 void ImGui::PushID(int int_id)
8484 {
8485 	const void* ptr_id = (void*)(intptr_t)int_id;
8486 	ImGuiWindow* window = GetCurrentWindowRead();
8487 	window->IDStack.push_back(window->GetID(ptr_id));
8488 }
8489 
PopID()8490 void ImGui::PopID()
8491 {
8492 	ImGuiWindow* window = GetCurrentWindowRead();
8493 	window->IDStack.pop_back();
8494 }
8495 
GetID(const char * str_id)8496 ImGuiID ImGui::GetID(const char* str_id)
8497 {
8498 	return GImGui->CurrentWindow->GetID(str_id);
8499 }
8500 
GetID(const char * str_id_begin,const char * str_id_end)8501 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
8502 {
8503 	return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end);
8504 }
8505 
GetID(const void * ptr_id)8506 ImGuiID ImGui::GetID(const void* ptr_id)
8507 {
8508 	return GImGui->CurrentWindow->GetID(ptr_id);
8509 }
8510 
Bullet()8511 void ImGui::Bullet()
8512 {
8513 	ImGuiWindow* window = GetCurrentWindow();
8514 	if (window->SkipItems)
8515 		return;
8516 
8517 	ImGuiContext& g = *GImGui;
8518 	const ImGuiStyle& style = g.Style;
8519 	const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y * 2), g.FontSize);
8520 	const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height));
8521 	ItemSize(bb);
8522 	if (!ItemAdd(bb, 0))
8523 	{
8524 		SameLine(0, style.FramePadding.x * 2);
8525 		return;
8526 	}
8527 
8528 	// Render and stay on same line
8529 	RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, line_height * 0.5f));
8530 	SameLine(0, style.FramePadding.x * 2);
8531 }
8532 
8533 // Text with a little bullet aligned to the typical tree node.
BulletTextV(const char * fmt,va_list args)8534 void ImGui::BulletTextV(const char* fmt, va_list args)
8535 {
8536 	ImGuiWindow* window = GetCurrentWindow();
8537 	if (window->SkipItems)
8538 		return;
8539 
8540 	ImGuiContext& g = *GImGui;
8541 	const ImGuiStyle& style = g.Style;
8542 
8543 	const char* text_begin = g.TempBuffer;
8544 	const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
8545 	const ImVec2 label_size = CalcTextSize(text_begin, text_end, false);
8546 	const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset);  // Latch before ItemSize changes it
8547 	const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y * 2), g.FontSize);
8548 	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
8549 	ItemSize(bb);
8550 	if (!ItemAdd(bb, 0))
8551 		return;
8552 
8553 	// Render
8554 	RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, line_height * 0.5f));
8555 	RenderText(bb.Min + ImVec2(g.FontSize + style.FramePadding.x * 2, text_base_offset_y), text_begin, text_end, false);
8556 }
8557 
BulletText(const char * fmt,...)8558 void ImGui::BulletText(const char* fmt, ...)
8559 {
8560 	va_list args;
8561 	va_start(args, fmt);
8562 	BulletTextV(fmt, args);
8563 	va_end(args);
8564 }
8565 
DataTypeFormatString(ImGuiDataType data_type,void * data_ptr,const char * display_format,char * buf,int buf_size)8566 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size)
8567 {
8568 	if (data_type == ImGuiDataType_Int)
8569 		ImFormatString(buf, buf_size, display_format, *(int*)data_ptr);
8570 	else if (data_type == ImGuiDataType_Float)
8571 		ImFormatString(buf, buf_size, display_format, *(float*)data_ptr);
8572 }
8573 
DataTypeFormatString(ImGuiDataType data_type,void * data_ptr,int decimal_precision,char * buf,int buf_size)8574 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size)
8575 {
8576 	if (data_type == ImGuiDataType_Int)
8577 	{
8578 		if (decimal_precision < 0)
8579 			ImFormatString(buf, buf_size, "%d", *(int*)data_ptr);
8580 		else
8581 			ImFormatString(buf, buf_size, "%.*d", decimal_precision, *(int*)data_ptr);
8582 	}
8583 	else if (data_type == ImGuiDataType_Float)
8584 	{
8585 		if (decimal_precision < 0)
8586 			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?
8587 		else
8588 			ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(float*)data_ptr);
8589 	}
8590 }
8591 
DataTypeApplyOp(ImGuiDataType data_type,int op,void * value1,const void * value2)8592 static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2)  // Store into value1
8593 {
8594 	if (data_type == ImGuiDataType_Int)
8595 	{
8596 		if (op == '+')
8597 			*(int*)value1 = *(int*)value1 + *(const int*)value2;
8598 		else if (op == '-')
8599 			*(int*)value1 = *(int*)value1 - *(const int*)value2;
8600 	}
8601 	else if (data_type == ImGuiDataType_Float)
8602 	{
8603 		if (op == '+')
8604 			*(float*)value1 = *(float*)value1 + *(const float*)value2;
8605 		else if (op == '-')
8606 			*(float*)value1 = *(float*)value1 - *(const float*)value2;
8607 	}
8608 }
8609 
8610 // User can input math operators (e.g. +100) to edit a numerical values.
DataTypeApplyOpFromText(const char * buf,const char * initial_value_buf,ImGuiDataType data_type,void * data_ptr,const char * scalar_format)8611 static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format)
8612 {
8613 	while (ImCharIsSpace(*buf))
8614 		buf++;
8615 
8616 	// We don't support '-' op because it would conflict with inputing negative value.
8617 	// Instead you can use +-100 to subtract from an existing value
8618 	char op = buf[0];
8619 	if (op == '+' || op == '*' || op == '/')
8620 	{
8621 		buf++;
8622 		while (ImCharIsSpace(*buf))
8623 			buf++;
8624 	}
8625 	else
8626 	{
8627 		op = 0;
8628 	}
8629 	if (!buf[0])
8630 		return false;
8631 
8632 	if (data_type == ImGuiDataType_Int)
8633 	{
8634 		if (!scalar_format)
8635 			scalar_format = "%d";
8636 		int* v = (int*)data_ptr;
8637 		const int old_v = *v;
8638 		int arg0i = *v;
8639 		if (op && sscanf(initial_value_buf, scalar_format, &arg0i) < 1)
8640 			return false;
8641 
8642 		// 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
8643 		float arg1f = 0.0f;
8644 		if (op == '+')
8645 		{
8646 			if (sscanf(buf, "%f", &arg1f) == 1) *v = (int)(arg0i + arg1f);
8647 		}  // Add (use "+-" to subtract)
8648 		else if (op == '*')
8649 		{
8650 			if (sscanf(buf, "%f", &arg1f) == 1) *v = (int)(arg0i * arg1f);
8651 		}  // Multiply
8652 		else if (op == '/')
8653 		{
8654 			if (sscanf(buf, "%f", &arg1f) == 1 && arg1f != 0.0f) *v = (int)(arg0i / arg1f);
8655 		}  // Divide
8656 		else
8657 		{
8658 			if (sscanf(buf, scalar_format, &arg0i) == 1) *v = arg0i;
8659 		}  // Assign constant (read as integer so big values are not lossy)
8660 		return (old_v != *v);
8661 	}
8662 	else if (data_type == ImGuiDataType_Float)
8663 	{
8664 		// For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in
8665 		scalar_format = "%f";
8666 		float* v = (float*)data_ptr;
8667 		const float old_v = *v;
8668 		float arg0f = *v;
8669 		if (op && sscanf(initial_value_buf, scalar_format, &arg0f) < 1)
8670 			return false;
8671 
8672 		float arg1f = 0.0f;
8673 		if (sscanf(buf, scalar_format, &arg1f) < 1)
8674 			return false;
8675 		if (op == '+')
8676 		{
8677 			*v = arg0f + arg1f;
8678 		}  // Add (use "+-" to subtract)
8679 		else if (op == '*')
8680 		{
8681 			*v = arg0f * arg1f;
8682 		}  // Multiply
8683 		else if (op == '/')
8684 		{
8685 			if (arg1f != 0.0f) *v = arg0f / arg1f;
8686 		}  // Divide
8687 		else
8688 		{
8689 			*v = arg1f;
8690 		}  // Assign constant
8691 		return (old_v != *v);
8692 	}
8693 
8694 	return false;
8695 }
8696 
8697 // Create text input in place of a slider (when CTRL+Clicking on slider)
8698 // FIXME: Logic is messy and confusing.
InputScalarAsWidgetReplacement(const ImRect & aabb,const char * label,ImGuiDataType data_type,void * data_ptr,ImGuiID id,int decimal_precision)8699 bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision)
8700 {
8701 	ImGuiContext& g = *GImGui;
8702 	ImGuiWindow* window = GetCurrentWindow();
8703 
8704 	// Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen)
8705 	// On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id
8706 	SetActiveID(g.ScalarAsInputTextId, window);
8707 	g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
8708 	SetHoveredID(0);
8709 	FocusableItemUnregister(window);
8710 
8711 	char buf[32];
8712 	DataTypeFormatString(data_type, data_ptr, decimal_precision, buf, IM_ARRAYSIZE(buf));
8713 	bool text_value_changed = InputTextEx(label, buf, IM_ARRAYSIZE(buf), aabb.GetSize(), ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll);
8714 	if (g.ScalarAsInputTextId == 0)  // First frame we started displaying the InputText widget
8715 	{
8716 		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)
8717 		g.ScalarAsInputTextId = g.ActiveId;
8718 		SetHoveredID(id);
8719 	}
8720 	if (text_value_changed)
8721 		return DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, NULL);
8722 	return false;
8723 }
8724 
8725 // Parse display precision back from the display format string
ParseFormatPrecision(const char * fmt,int default_precision)8726 int ImGui::ParseFormatPrecision(const char* fmt, int default_precision)
8727 {
8728 	int precision = default_precision;
8729 	while ((fmt = strchr(fmt, '%')) != NULL)
8730 	{
8731 		fmt++;
8732 		if (fmt[0] == '%')
8733 		{
8734 			fmt++;
8735 			continue;
8736 		}  // Ignore "%%"
8737 		while (*fmt >= '0' && *fmt <= '9')
8738 			fmt++;
8739 		if (*fmt == '.')
8740 		{
8741 			fmt = ImAtoi(fmt + 1, &precision);
8742 			if (precision < 0 || precision > 10)
8743 				precision = default_precision;
8744 		}
8745 		if (*fmt == 'e' || *fmt == 'E')  // Maximum precision with scientific notation
8746 			precision = -1;
8747 		break;
8748 	}
8749 	return precision;
8750 }
8751 
GetMinimumStepAtDecimalPrecision(int decimal_precision)8752 static float GetMinimumStepAtDecimalPrecision(int decimal_precision)
8753 {
8754 	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};
8755 	return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision);
8756 }
8757 
RoundScalar(float value,int decimal_precision)8758 float ImGui::RoundScalar(float value, int decimal_precision)
8759 {
8760 	// Round past decimal precision
8761 	// So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0
8762 	// FIXME: Investigate better rounding methods
8763 	if (decimal_precision < 0)
8764 		return value;
8765 	const float min_step = GetMinimumStepAtDecimalPrecision(decimal_precision);
8766 	bool negative = value < 0.0f;
8767 	value = fabsf(value);
8768 	float remainder = fmodf(value, min_step);
8769 	if (remainder <= min_step * 0.5f)
8770 		value -= remainder;
8771 	else
8772 		value += (min_step - remainder);
8773 	return negative ? -value : value;
8774 }
8775 
SliderBehaviorCalcRatioFromValue(float v,float v_min,float v_max,float power,float linear_zero_pos)8776 static inline float SliderBehaviorCalcRatioFromValue(float v, float v_min, float v_max, float power, float linear_zero_pos)
8777 {
8778 	if (v_min == v_max)
8779 		return 0.0f;
8780 
8781 	const bool is_non_linear = (power < 1.0f - 0.00001f) || (power > 1.0f + 0.00001f);
8782 	const float v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min);
8783 	if (is_non_linear)
8784 	{
8785 		if (v_clamped < 0.0f)
8786 		{
8787 			const float f = 1.0f - (v_clamped - v_min) / (ImMin(0.0f, v_max) - v_min);
8788 			return (1.0f - powf(f, 1.0f / power)) * linear_zero_pos;
8789 		}
8790 		else
8791 		{
8792 			const float f = (v_clamped - ImMax(0.0f, v_min)) / (v_max - ImMax(0.0f, v_min));
8793 			return linear_zero_pos + powf(f, 1.0f / power) * (1.0f - linear_zero_pos);
8794 		}
8795 	}
8796 
8797 	// Linear slider
8798 	return (v_clamped - v_min) / (v_max - v_min);
8799 }
8800 
SliderBehavior(const ImRect & frame_bb,ImGuiID id,float * v,float v_min,float v_max,float power,int decimal_precision,ImGuiSliderFlags flags)8801 bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags)
8802 {
8803 	ImGuiContext& g = *GImGui;
8804 	ImGuiWindow* window = GetCurrentWindow();
8805 	const ImGuiStyle& style = g.Style;
8806 
8807 	// Draw frame
8808 	const ImU32 frame_col = GetColorU32((g.ActiveId == id && g.ActiveIdSource == ImGuiInputSource_Nav) ? ImGuiCol_FrameBgActive : ImGuiCol_FrameBg);
8809 	RenderNavHighlight(frame_bb, id);
8810 	RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
8811 
8812 	const bool is_non_linear = (power < 1.0f - 0.00001f) || (power > 1.0f + 0.00001f);
8813 	const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0;
8814 
8815 	const float grab_padding = 2.0f;
8816 	const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f);
8817 	float grab_sz;
8818 	if (decimal_precision != 0)
8819 		grab_sz = ImMin(style.GrabMinSize, slider_sz);
8820 	else
8821 		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
8822 	const float slider_usable_sz = slider_sz - grab_sz;
8823 	const float slider_usable_pos_min = (is_horizontal ? frame_bb.Min.x : frame_bb.Min.y) + grab_padding + grab_sz * 0.5f;
8824 	const float slider_usable_pos_max = (is_horizontal ? frame_bb.Max.x : frame_bb.Max.y) - grab_padding - grab_sz * 0.5f;
8825 
8826 	// For logarithmic sliders that cross over sign boundary we want the exponential increase to be symmetric around 0.0f
8827 	float linear_zero_pos = 0.0f;  // 0.0->1.0f
8828 	if (v_min * v_max < 0.0f)
8829 	{
8830 		// Different sign
8831 		const float linear_dist_min_to_0 = powf(fabsf(0.0f - v_min), 1.0f / power);
8832 		const float linear_dist_max_to_0 = powf(fabsf(v_max - 0.0f), 1.0f / power);
8833 		linear_zero_pos = linear_dist_min_to_0 / (linear_dist_min_to_0 + linear_dist_max_to_0);
8834 	}
8835 	else
8836 	{
8837 		// Same sign
8838 		linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f;
8839 	}
8840 
8841 	// Process interacting with the slider
8842 	bool value_changed = false;
8843 	if (g.ActiveId == id)
8844 	{
8845 		bool set_new_value = false;
8846 		float clicked_t = 0.0f;
8847 		if (g.ActiveIdSource == ImGuiInputSource_Mouse)
8848 		{
8849 			if (!g.IO.MouseDown[0])
8850 			{
8851 				ClearActiveID();
8852 			}
8853 			else
8854 			{
8855 				const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
8856 				clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f;
8857 				if (!is_horizontal)
8858 					clicked_t = 1.0f - clicked_t;
8859 				set_new_value = true;
8860 			}
8861 		}
8862 		else if (g.ActiveIdSource == ImGuiInputSource_Nav)
8863 		{
8864 			const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f);
8865 			float delta = is_horizontal ? delta2.x : -delta2.y;
8866 			if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)
8867 			{
8868 				ClearActiveID();
8869 			}
8870 			else if (delta != 0.0f)
8871 			{
8872 				clicked_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos);
8873 				if (decimal_precision == 0 && !is_non_linear)
8874 				{
8875 					if (fabsf(v_max - v_min) <= 100.0f || IsNavInputDown(ImGuiNavInput_TweakSlow))
8876 						delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (v_max - v_min);  // Gamepad/keyboard tweak speeds in integer steps
8877 					else
8878 						delta /= 100.0f;
8879 				}
8880 				else
8881 				{
8882 					delta /= 100.0f;  // Gamepad/keyboard tweak speeds in % of slider bounds
8883 					if (IsNavInputDown(ImGuiNavInput_TweakSlow))
8884 						delta /= 10.0f;
8885 				}
8886 				if (IsNavInputDown(ImGuiNavInput_TweakFast))
8887 					delta *= 10.0f;
8888 				set_new_value = true;
8889 				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
8890 					set_new_value = false;
8891 				else
8892 					clicked_t = ImSaturate(clicked_t + delta);
8893 			}
8894 		}
8895 
8896 		if (set_new_value)
8897 		{
8898 			float new_value;
8899 			if (is_non_linear)
8900 			{
8901 				// Account for logarithmic scale on both sides of the zero
8902 				if (clicked_t < linear_zero_pos)
8903 				{
8904 					// Negative: rescale to the negative range before powering
8905 					float a = 1.0f - (clicked_t / linear_zero_pos);
8906 					a = powf(a, power);
8907 					new_value = ImLerp(ImMin(v_max, 0.0f), v_min, a);
8908 				}
8909 				else
8910 				{
8911 					// Positive: rescale to the positive range before powering
8912 					float a;
8913 					if (fabsf(linear_zero_pos - 1.0f) > 1.e-6f)
8914 						a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos);
8915 					else
8916 						a = clicked_t;
8917 					a = powf(a, power);
8918 					new_value = ImLerp(ImMax(v_min, 0.0f), v_max, a);
8919 				}
8920 			}
8921 			else
8922 			{
8923 				// Linear slider
8924 				new_value = ImLerp(v_min, v_max, clicked_t);
8925 			}
8926 
8927 			// Round past decimal precision
8928 			new_value = RoundScalar(new_value, decimal_precision);
8929 			if (*v != new_value)
8930 			{
8931 				*v = new_value;
8932 				value_changed = true;
8933 			}
8934 		}
8935 	}
8936 
8937 	// Draw
8938 	float grab_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos);
8939 	if (!is_horizontal)
8940 		grab_t = 1.0f - grab_t;
8941 	const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
8942 	ImRect grab_bb;
8943 	if (is_horizontal)
8944 		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));
8945 	else
8946 		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));
8947 	window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
8948 
8949 	return value_changed;
8950 }
8951 
8952 // Use power!=1.0 for logarithmic sliders.
8953 // Adjust display_format to decorate the value with a prefix or a suffix.
8954 //   "%.3f"         1.234
8955 //   "%5.2f secs"   01.23 secs
8956 //   "Gold: %.0f"   Gold: 1
SliderFloat(const char * label,float * v,float v_min,float v_max,const char * display_format,float power)8957 bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format, float power)
8958 {
8959 	ImGuiWindow* window = GetCurrentWindow();
8960 	if (window->SkipItems)
8961 		return false;
8962 
8963 	ImGuiContext& g = *GImGui;
8964 	const ImGuiStyle& style = g.Style;
8965 	const ImGuiID id = window->GetID(label);
8966 	const float w = CalcItemWidth();
8967 
8968 	const ImVec2 label_size = CalcTextSize(label, NULL, true);
8969 	const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
8970 	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));
8971 
8972 	// NB- we don't call ItemSize() yet because we may turn into a text edit box below
8973 	if (!ItemAdd(total_bb, id, &frame_bb))
8974 	{
8975 		ItemSize(total_bb, style.FramePadding.y);
8976 		return false;
8977 	}
8978 	const bool hovered = ItemHoverable(frame_bb, id);
8979 
8980 	if (!display_format)
8981 		display_format = "%.3f";
8982 	int decimal_precision = ParseFormatPrecision(display_format, 3);
8983 
8984 	// Tabbing or CTRL-clicking on Slider turns it into an input box
8985 	bool start_text_input = false;
8986 	const bool tab_focus_requested = FocusableItemRegister(window, id);
8987 	if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id))
8988 	{
8989 		SetActiveID(id, window);
8990 		SetFocusID(id, window);
8991 		FocusWindow(window);
8992 		g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
8993 		if (tab_focus_requested || g.IO.KeyCtrl || g.NavInputId == id)
8994 		{
8995 			start_text_input = true;
8996 			g.ScalarAsInputTextId = 0;
8997 		}
8998 	}
8999 	if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
9000 		return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
9001 
9002 	// Actual slider behavior + render grab
9003 	ItemSize(total_bb, style.FramePadding.y);
9004 	const bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision);
9005 
9006 	// Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
9007 	char value_buf[64];
9008 	const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
9009 	RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));
9010 
9011 	if (label_size.x > 0.0f)
9012 		RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
9013 
9014 	return value_changed;
9015 }
9016 
VSliderFloat(const char * label,const ImVec2 & size,float * v,float v_min,float v_max,const char * display_format,float power)9017 bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format, float power)
9018 {
9019 	ImGuiWindow* window = GetCurrentWindow();
9020 	if (window->SkipItems)
9021 		return false;
9022 
9023 	ImGuiContext& g = *GImGui;
9024 	const ImGuiStyle& style = g.Style;
9025 	const ImGuiID id = window->GetID(label);
9026 
9027 	const ImVec2 label_size = CalcTextSize(label, NULL, true);
9028 	const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
9029 	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));
9030 
9031 	ItemSize(bb, style.FramePadding.y);
9032 	if (!ItemAdd(frame_bb, id))
9033 		return false;
9034 	const bool hovered = ItemHoverable(frame_bb, id);
9035 
9036 	if (!display_format)
9037 		display_format = "%.3f";
9038 	int decimal_precision = ParseFormatPrecision(display_format, 3);
9039 
9040 	if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id)
9041 	{
9042 		SetActiveID(id, window);
9043 		SetFocusID(id, window);
9044 		FocusWindow(window);
9045 		g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
9046 	}
9047 
9048 	// Actual slider behavior + render grab
9049 	bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision, ImGuiSliderFlags_Vertical);
9050 
9051 	// Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
9052 	// For the vertical slider we allow centered text to overlap the frame padding
9053 	char value_buf[64];
9054 	char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
9055 	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));
9056 	if (label_size.x > 0.0f)
9057 		RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
9058 
9059 	return value_changed;
9060 }
9061 
SliderAngle(const char * label,float * v_rad,float v_degrees_min,float v_degrees_max)9062 bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max)
9063 {
9064 	float v_deg = (*v_rad) * 360.0f / (2 * IM_PI);
9065 	bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f);
9066 	*v_rad = v_deg * (2 * IM_PI) / 360.0f;
9067 	return value_changed;
9068 }
9069 
SliderInt(const char * label,int * v,int v_min,int v_max,const char * display_format)9070 bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format)
9071 {
9072 	if (!display_format)
9073 		display_format = "%.0f";
9074 	float v_f = (float)*v;
9075 	bool value_changed = SliderFloat(label, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
9076 	*v = (int)v_f;
9077 	return value_changed;
9078 }
9079 
VSliderInt(const char * label,const ImVec2 & size,int * v,int v_min,int v_max,const char * display_format)9080 bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format)
9081 {
9082 	if (!display_format)
9083 		display_format = "%.0f";
9084 	float v_f = (float)*v;
9085 	bool value_changed = VSliderFloat(label, size, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
9086 	*v = (int)v_f;
9087 	return value_changed;
9088 }
9089 
9090 // 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)9091 bool ImGui::SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power)
9092 {
9093 	ImGuiWindow* window = GetCurrentWindow();
9094 	if (window->SkipItems)
9095 		return false;
9096 
9097 	ImGuiContext& g = *GImGui;
9098 	bool value_changed = false;
9099 	BeginGroup();
9100 	PushID(label);
9101 	PushMultiItemsWidths(components);
9102 	for (int i = 0; i < components; i++)
9103 	{
9104 		PushID(i);
9105 		value_changed |= SliderFloat("##v", &v[i], v_min, v_max, display_format, power);
9106 		SameLine(0, g.Style.ItemInnerSpacing.x);
9107 		PopID();
9108 		PopItemWidth();
9109 	}
9110 	PopID();
9111 
9112 	TextUnformatted(label, FindRenderedTextEnd(label));
9113 	EndGroup();
9114 
9115 	return value_changed;
9116 }
9117 
SliderFloat2(const char * label,float v[2],float v_min,float v_max,const char * display_format,float power)9118 bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format, float power)
9119 {
9120 	return SliderFloatN(label, v, 2, v_min, v_max, display_format, power);
9121 }
9122 
SliderFloat3(const char * label,float v[3],float v_min,float v_max,const char * display_format,float power)9123 bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format, float power)
9124 {
9125 	return SliderFloatN(label, v, 3, v_min, v_max, display_format, power);
9126 }
9127 
SliderFloat4(const char * label,float v[4],float v_min,float v_max,const char * display_format,float power)9128 bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format, float power)
9129 {
9130 	return SliderFloatN(label, v, 4, v_min, v_max, display_format, power);
9131 }
9132 
SliderIntN(const char * label,int * v,int components,int v_min,int v_max,const char * display_format)9133 bool ImGui::SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format)
9134 {
9135 	ImGuiWindow* window = GetCurrentWindow();
9136 	if (window->SkipItems)
9137 		return false;
9138 
9139 	ImGuiContext& g = *GImGui;
9140 	bool value_changed = false;
9141 	BeginGroup();
9142 	PushID(label);
9143 	PushMultiItemsWidths(components);
9144 	for (int i = 0; i < components; i++)
9145 	{
9146 		PushID(i);
9147 		value_changed |= SliderInt("##v", &v[i], v_min, v_max, display_format);
9148 		SameLine(0, g.Style.ItemInnerSpacing.x);
9149 		PopID();
9150 		PopItemWidth();
9151 	}
9152 	PopID();
9153 
9154 	TextUnformatted(label, FindRenderedTextEnd(label));
9155 	EndGroup();
9156 
9157 	return value_changed;
9158 }
9159 
SliderInt2(const char * label,int v[2],int v_min,int v_max,const char * display_format)9160 bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format)
9161 {
9162 	return SliderIntN(label, v, 2, v_min, v_max, display_format);
9163 }
9164 
SliderInt3(const char * label,int v[3],int v_min,int v_max,const char * display_format)9165 bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format)
9166 {
9167 	return SliderIntN(label, v, 3, v_min, v_max, display_format);
9168 }
9169 
SliderInt4(const char * label,int v[4],int v_min,int v_max,const char * display_format)9170 bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format)
9171 {
9172 	return SliderIntN(label, v, 4, v_min, v_max, display_format);
9173 }
9174 
DragBehavior(const ImRect & frame_bb,ImGuiID id,float * v,float v_speed,float v_min,float v_max,int decimal_precision,float power)9175 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)
9176 {
9177 	ImGuiContext& g = *GImGui;
9178 	const ImGuiStyle& style = g.Style;
9179 
9180 	// Draw frame
9181 	const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
9182 	RenderNavHighlight(frame_bb, id);
9183 	RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
9184 
9185 	bool value_changed = false;
9186 
9187 	// Process interacting with the drag
9188 	if (g.ActiveId == id)
9189 	{
9190 		if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0])
9191 			ClearActiveID();
9192 		else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)
9193 			ClearActiveID();
9194 	}
9195 	if (g.ActiveId == id)
9196 	{
9197 		if (g.ActiveIdIsJustActivated)
9198 		{
9199 			// Lock current value on click
9200 			g.DragCurrentValue = *v;
9201 			g.DragLastMouseDelta = ImVec2(0.f, 0.f);
9202 		}
9203 
9204 		if (v_speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX)
9205 			v_speed = (v_max - v_min) * g.DragSpeedDefaultRatio;
9206 
9207 		float v_cur = g.DragCurrentValue;
9208 		const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f);
9209 		float adjust_delta = 0.0f;
9210 		if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid())
9211 		{
9212 			adjust_delta = mouse_drag_delta.x - g.DragLastMouseDelta.x;
9213 			if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f)
9214 				adjust_delta *= g.DragSpeedScaleFast;
9215 			if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f)
9216 				adjust_delta *= g.DragSpeedScaleSlow;
9217 			g.DragLastMouseDelta.x = mouse_drag_delta.x;
9218 		}
9219 		if (g.ActiveIdSource == ImGuiInputSource_Nav)
9220 		{
9221 			adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f / 10.0f, 10.0f).x;
9222 			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
9223 				adjust_delta = 0.0f;
9224 			v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision));
9225 		}
9226 		adjust_delta *= v_speed;
9227 
9228 		if (fabsf(adjust_delta) > 0.0f)
9229 		{
9230 			if (fabsf(power - 1.0f) > 0.001f)
9231 			{
9232 				// Logarithmic curve on both side of 0.0
9233 				float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur;
9234 				float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f;
9235 				float v1 = powf(v0_abs, 1.0f / power) + (adjust_delta * v0_sign);
9236 				float v1_abs = v1 >= 0.0f ? v1 : -v1;
9237 				float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f;        // Crossed sign line
9238 				v_cur = powf(v1_abs, power) * v0_sign * v1_sign;  // Reapply sign
9239 			}
9240 			else
9241 			{
9242 				v_cur += adjust_delta;
9243 			}
9244 
9245 			// Clamp
9246 			if (v_min < v_max)
9247 				v_cur = ImClamp(v_cur, v_min, v_max);
9248 			g.DragCurrentValue = v_cur;
9249 		}
9250 
9251 		// Round to user desired precision, then apply
9252 		v_cur = RoundScalar(v_cur, decimal_precision);
9253 		if (*v != v_cur)
9254 		{
9255 			*v = v_cur;
9256 			value_changed = true;
9257 		}
9258 	}
9259 
9260 	return value_changed;
9261 }
9262 
DragFloat(const char * label,float * v,float v_speed,float v_min,float v_max,const char * display_format,float power)9263 bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* display_format, float power)
9264 {
9265 	ImGuiWindow* window = GetCurrentWindow();
9266 	if (window->SkipItems)
9267 		return false;
9268 
9269 	ImGuiContext& g = *GImGui;
9270 	const ImGuiStyle& style = g.Style;
9271 	const ImGuiID id = window->GetID(label);
9272 	const float w = CalcItemWidth();
9273 
9274 	const ImVec2 label_size = CalcTextSize(label, NULL, true);
9275 	const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
9276 	const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
9277 	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));
9278 
9279 	// NB- we don't call ItemSize() yet because we may turn into a text edit box below
9280 	if (!ItemAdd(total_bb, id, &frame_bb))
9281 	{
9282 		ItemSize(total_bb, style.FramePadding.y);
9283 		return false;
9284 	}
9285 	const bool hovered = ItemHoverable(frame_bb, id);
9286 
9287 	if (!display_format)
9288 		display_format = "%.3f";
9289 	int decimal_precision = ParseFormatPrecision(display_format, 3);
9290 
9291 	// Tabbing or CTRL-clicking on Drag turns it into an input box
9292 	bool start_text_input = false;
9293 	const bool tab_focus_requested = FocusableItemRegister(window, id);
9294 	if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id))
9295 	{
9296 		SetActiveID(id, window);
9297 		SetFocusID(id, window);
9298 		FocusWindow(window);
9299 		g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
9300 		if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id)
9301 		{
9302 			start_text_input = true;
9303 			g.ScalarAsInputTextId = 0;
9304 		}
9305 	}
9306 	if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
9307 		return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
9308 
9309 	// Actual drag behavior
9310 	ItemSize(total_bb, style.FramePadding.y);
9311 	const bool value_changed = DragBehavior(frame_bb, id, v, v_speed, v_min, v_max, decimal_precision, power);
9312 
9313 	// Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
9314 	char value_buf[64];
9315 	const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
9316 	RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));
9317 
9318 	if (label_size.x > 0.0f)
9319 		RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
9320 
9321 	return value_changed;
9322 }
9323 
DragFloatN(const char * label,float * v,int components,float v_speed,float v_min,float v_max,const char * display_format,float power)9324 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)
9325 {
9326 	ImGuiWindow* window = GetCurrentWindow();
9327 	if (window->SkipItems)
9328 		return false;
9329 
9330 	ImGuiContext& g = *GImGui;
9331 	bool value_changed = false;
9332 	BeginGroup();
9333 	PushID(label);
9334 	PushMultiItemsWidths(components);
9335 	for (int i = 0; i < components; i++)
9336 	{
9337 		PushID(i);
9338 		value_changed |= DragFloat("##v", &v[i], v_speed, v_min, v_max, display_format, power);
9339 		SameLine(0, g.Style.ItemInnerSpacing.x);
9340 		PopID();
9341 		PopItemWidth();
9342 	}
9343 	PopID();
9344 
9345 	TextUnformatted(label, FindRenderedTextEnd(label));
9346 	EndGroup();
9347 
9348 	return value_changed;
9349 }
9350 
DragFloat2(const char * label,float v[2],float v_speed,float v_min,float v_max,const char * display_format,float power)9351 bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power)
9352 {
9353 	return DragFloatN(label, v, 2, v_speed, v_min, v_max, display_format, power);
9354 }
9355 
DragFloat3(const char * label,float v[3],float v_speed,float v_min,float v_max,const char * display_format,float power)9356 bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* display_format, float power)
9357 {
9358 	return DragFloatN(label, v, 3, v_speed, v_min, v_max, display_format, power);
9359 }
9360 
DragFloat4(const char * label,float v[4],float v_speed,float v_min,float v_max,const char * display_format,float power)9361 bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* display_format, float power)
9362 {
9363 	return DragFloatN(label, v, 4, v_speed, v_min, v_max, display_format, power);
9364 }
9365 
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)9366 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)
9367 {
9368 	ImGuiWindow* window = GetCurrentWindow();
9369 	if (window->SkipItems)
9370 		return false;
9371 
9372 	ImGuiContext& g = *GImGui;
9373 	PushID(label);
9374 	BeginGroup();
9375 	PushMultiItemsWidths(2);
9376 
9377 	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);
9378 	PopItemWidth();
9379 	SameLine(0, g.Style.ItemInnerSpacing.x);
9380 	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);
9381 	PopItemWidth();
9382 	SameLine(0, g.Style.ItemInnerSpacing.x);
9383 
9384 	TextUnformatted(label, FindRenderedTextEnd(label));
9385 	EndGroup();
9386 	PopID();
9387 
9388 	return value_changed;
9389 }
9390 
9391 // 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)9392 bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* display_format)
9393 {
9394 	if (!display_format)
9395 		display_format = "%.0f";
9396 	float v_f = (float)*v;
9397 	bool value_changed = DragFloat(label, &v_f, v_speed, (float)v_min, (float)v_max, display_format);
9398 	*v = (int)v_f;
9399 	return value_changed;
9400 }
9401 
DragIntN(const char * label,int * v,int components,float v_speed,int v_min,int v_max,const char * display_format)9402 bool ImGui::DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format)
9403 {
9404 	ImGuiWindow* window = GetCurrentWindow();
9405 	if (window->SkipItems)
9406 		return false;
9407 
9408 	ImGuiContext& g = *GImGui;
9409 	bool value_changed = false;
9410 	BeginGroup();
9411 	PushID(label);
9412 	PushMultiItemsWidths(components);
9413 	for (int i = 0; i < components; i++)
9414 	{
9415 		PushID(i);
9416 		value_changed |= DragInt("##v", &v[i], v_speed, v_min, v_max, display_format);
9417 		SameLine(0, g.Style.ItemInnerSpacing.x);
9418 		PopID();
9419 		PopItemWidth();
9420 	}
9421 	PopID();
9422 
9423 	TextUnformatted(label, FindRenderedTextEnd(label));
9424 	EndGroup();
9425 
9426 	return value_changed;
9427 }
9428 
DragInt2(const char * label,int v[2],float v_speed,int v_min,int v_max,const char * display_format)9429 bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* display_format)
9430 {
9431 	return DragIntN(label, v, 2, v_speed, v_min, v_max, display_format);
9432 }
9433 
DragInt3(const char * label,int v[3],float v_speed,int v_min,int v_max,const char * display_format)9434 bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* display_format)
9435 {
9436 	return DragIntN(label, v, 3, v_speed, v_min, v_max, display_format);
9437 }
9438 
DragInt4(const char * label,int v[4],float v_speed,int v_min,int v_max,const char * display_format)9439 bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* display_format)
9440 {
9441 	return DragIntN(label, v, 4, v_speed, v_min, v_max, display_format);
9442 }
9443 
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)9444 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)
9445 {
9446 	ImGuiWindow* window = GetCurrentWindow();
9447 	if (window->SkipItems)
9448 		return false;
9449 
9450 	ImGuiContext& g = *GImGui;
9451 	PushID(label);
9452 	BeginGroup();
9453 	PushMultiItemsWidths(2);
9454 
9455 	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);
9456 	PopItemWidth();
9457 	SameLine(0, g.Style.ItemInnerSpacing.x);
9458 	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);
9459 	PopItemWidth();
9460 	SameLine(0, g.Style.ItemInnerSpacing.x);
9461 
9462 	TextUnformatted(label, FindRenderedTextEnd(label));
9463 	EndGroup();
9464 	PopID();
9465 
9466 	return value_changed;
9467 }
9468 
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)9469 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)
9470 {
9471 	ImGuiWindow* window = GetCurrentWindow();
9472 	if (window->SkipItems)
9473 		return;
9474 
9475 	ImGuiContext& g = *GImGui;
9476 	const ImGuiStyle& style = g.Style;
9477 
9478 	const ImVec2 label_size = CalcTextSize(label, NULL, true);
9479 	if (graph_size.x == 0.0f)
9480 		graph_size.x = CalcItemWidth();
9481 	if (graph_size.y == 0.0f)
9482 		graph_size.y = label_size.y + (style.FramePadding.y * 2);
9483 
9484 	const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y));
9485 	const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
9486 	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));
9487 	ItemSize(total_bb, style.FramePadding.y);
9488 	if (!ItemAdd(total_bb, 0, &frame_bb))
9489 		return;
9490 	const bool hovered = ItemHoverable(inner_bb, 0);
9491 
9492 	// Determine scale from values if not specified
9493 	if (scale_min == FLT_MAX || scale_max == FLT_MAX)
9494 	{
9495 		float v_min = FLT_MAX;
9496 		float v_max = -FLT_MAX;
9497 		for (int i = 0; i < values_count; i++)
9498 		{
9499 			const float v = values_getter(data, i);
9500 			v_min = ImMin(v_min, v);
9501 			v_max = ImMax(v_max, v);
9502 		}
9503 		if (scale_min == FLT_MAX)
9504 			scale_min = v_min;
9505 		if (scale_max == FLT_MAX)
9506 			scale_max = v_max;
9507 	}
9508 
9509 	RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
9510 
9511 	if (values_count > 0)
9512 	{
9513 		int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
9514 		int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
9515 
9516 		// Tooltip on hover
9517 		int v_hovered = -1;
9518 		if (hovered)
9519 		{
9520 			const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);
9521 			const int v_idx = (int)(t * item_count);
9522 			IM_ASSERT(v_idx >= 0 && v_idx < values_count);
9523 
9524 			const float v0 = values_getter(data, (v_idx + values_offset) % values_count);
9525 			const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count);
9526 			if (plot_type == ImGuiPlotType_Lines)
9527 				SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx + 1, v1);
9528 			else if (plot_type == ImGuiPlotType_Histogram)
9529 				SetTooltip("%d: %8.4g", v_idx, v0);
9530 			v_hovered = v_idx;
9531 		}
9532 
9533 		const float t_step = 1.0f / (float)res_w;
9534 		const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min));
9535 
9536 		float v0 = values_getter(data, (0 + values_offset) % values_count);
9537 		float t0 = 0.0f;
9538 		ImVec2 tp0 = ImVec2(t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale));                                                    // Point in the normalized space of our target rectangle
9539 		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
9540 
9541 		const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram);
9542 		const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered);
9543 
9544 		for (int n = 0; n < res_w; n++)
9545 		{
9546 			const float t1 = t0 + t_step;
9547 			const int v1_idx = (int)(t0 * item_count + 0.5f);
9548 			IM_ASSERT(v1_idx >= 0 && v1_idx < values_count);
9549 			const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count);
9550 			const ImVec2 tp1 = ImVec2(t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale));
9551 
9552 			// 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.
9553 			ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0);
9554 			ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t));
9555 			if (plot_type == ImGuiPlotType_Lines)
9556 			{
9557 				window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
9558 			}
9559 			else if (plot_type == ImGuiPlotType_Histogram)
9560 			{
9561 				if (pos1.x >= pos0.x + 2.0f)
9562 					pos1.x -= 1.0f;
9563 				window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
9564 			}
9565 
9566 			t0 = t1;
9567 			tp0 = tp1;
9568 		}
9569 	}
9570 
9571 	// Text overlay
9572 	if (overlay_text)
9573 		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));
9574 
9575 	if (label_size.x > 0.0f)
9576 		RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
9577 }
9578 
9579 struct ImGuiPlotArrayGetterData
9580 {
9581 	const float* Values;
9582 	int Stride;
9583 
ImGuiPlotArrayGetterDataImGuiPlotArrayGetterData9584 	ImGuiPlotArrayGetterData(const float* values, int stride)
9585 	{
9586 		Values = values;
9587 		Stride = stride;
9588 	}
9589 };
9590 
Plot_ArrayGetter(void * data,int idx)9591 static float Plot_ArrayGetter(void* data, int idx)
9592 {
9593 	ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data;
9594 	const float v = *(float*)(void*)((unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);
9595 	return v;
9596 }
9597 
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)9598 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)
9599 {
9600 	ImGuiPlotArrayGetterData data(values, stride);
9601 	PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
9602 }
9603 
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)9604 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)
9605 {
9606 	PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
9607 }
9608 
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)9609 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)
9610 {
9611 	ImGuiPlotArrayGetterData data(values, stride);
9612 	PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
9613 }
9614 
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)9615 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)
9616 {
9617 	PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
9618 }
9619 
9620 // 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)9621 void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay)
9622 {
9623 	ImGuiWindow* window = GetCurrentWindow();
9624 	if (window->SkipItems)
9625 		return;
9626 
9627 	ImGuiContext& g = *GImGui;
9628 	const ImGuiStyle& style = g.Style;
9629 
9630 	ImVec2 pos = window->DC.CursorPos;
9631 	ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y * 2.0f));
9632 	ItemSize(bb, style.FramePadding.y);
9633 	if (!ItemAdd(bb, 0))
9634 		return;
9635 
9636 	// Render
9637 	fraction = ImSaturate(fraction);
9638 	RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
9639 	bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize));
9640 	const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y);
9641 	RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding);
9642 
9643 	// Default displaying the fraction as percentage string, but user can override it
9644 	char overlay_buf[32];
9645 	if (!overlay)
9646 	{
9647 		ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction * 100 + 0.01f);
9648 		overlay = overlay_buf;
9649 	}
9650 
9651 	ImVec2 overlay_size = CalcTextSize(overlay, NULL);
9652 	if (overlay_size.x > 0.0f)
9653 		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);
9654 }
9655 
Checkbox(const char * label,bool * v)9656 bool ImGui::Checkbox(const char* label, bool* v)
9657 {
9658 	ImGuiWindow* window = GetCurrentWindow();
9659 	if (window->SkipItems)
9660 		return false;
9661 
9662 	ImGuiContext& g = *GImGui;
9663 	const ImGuiStyle& style = g.Style;
9664 	const ImGuiID id = window->GetID(label);
9665 	const ImVec2 label_size = CalcTextSize(label, NULL, true);
9666 
9667 	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
9668 	ItemSize(check_bb, style.FramePadding.y);
9669 
9670 	ImRect total_bb = check_bb;
9671 	if (label_size.x > 0)
9672 		SameLine(0, style.ItemInnerSpacing.x);
9673 	const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size);
9674 	if (label_size.x > 0)
9675 	{
9676 		ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
9677 		total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max));
9678 	}
9679 
9680 	if (!ItemAdd(total_bb, id))
9681 		return false;
9682 
9683 	bool hovered, held;
9684 	bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
9685 	if (pressed)
9686 		*v = !(*v);
9687 
9688 	RenderNavHighlight(total_bb, id);
9689 	RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
9690 	if (*v)
9691 	{
9692 		const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
9693 		const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
9694 		RenderCheckMark(check_bb.Min + ImVec2(pad, pad), GetColorU32(ImGuiCol_CheckMark), check_bb.GetWidth() - pad * 2.0f);
9695 	}
9696 
9697 	if (g.LogEnabled)
9698 		LogRenderedText(&text_bb.Min, *v ? "[x]" : "[ ]");
9699 	if (label_size.x > 0.0f)
9700 		RenderText(text_bb.Min, label);
9701 
9702 	return pressed;
9703 }
9704 
CheckboxFlags(const char * label,unsigned int * flags,unsigned int flags_value)9705 bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value)
9706 {
9707 	bool v = ((*flags & flags_value) == flags_value);
9708 	bool pressed = Checkbox(label, &v);
9709 	if (pressed)
9710 	{
9711 		if (v)
9712 			*flags |= flags_value;
9713 		else
9714 			*flags &= ~flags_value;
9715 	}
9716 
9717 	return pressed;
9718 }
9719 
RadioButton(const char * label,bool active)9720 bool ImGui::RadioButton(const char* label, bool active)
9721 {
9722 	ImGuiWindow* window = GetCurrentWindow();
9723 	if (window->SkipItems)
9724 		return false;
9725 
9726 	ImGuiContext& g = *GImGui;
9727 	const ImGuiStyle& style = g.Style;
9728 	const ImGuiID id = window->GetID(label);
9729 	const ImVec2 label_size = CalcTextSize(label, NULL, true);
9730 
9731 	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));
9732 	ItemSize(check_bb, style.FramePadding.y);
9733 
9734 	ImRect total_bb = check_bb;
9735 	if (label_size.x > 0)
9736 		SameLine(0, style.ItemInnerSpacing.x);
9737 	const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size);
9738 	if (label_size.x > 0)
9739 	{
9740 		ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
9741 		total_bb.Add(text_bb);
9742 	}
9743 
9744 	if (!ItemAdd(total_bb, id))
9745 		return false;
9746 
9747 	ImVec2 center = check_bb.GetCenter();
9748 	center.x = (float)(int)center.x + 0.5f;
9749 	center.y = (float)(int)center.y + 0.5f;
9750 	const float radius = check_bb.GetHeight() * 0.5f;
9751 
9752 	bool hovered, held;
9753 	bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
9754 
9755 	RenderNavHighlight(total_bb, id);
9756 	window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16);
9757 	if (active)
9758 	{
9759 		const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
9760 		const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
9761 		window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark), 16);
9762 	}
9763 
9764 	if (style.FrameBorderSize > 0.0f)
9765 	{
9766 		window->DrawList->AddCircle(center + ImVec2(1, 1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize);
9767 		window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize);
9768 	}
9769 
9770 	if (g.LogEnabled)
9771 		LogRenderedText(&text_bb.Min, active ? "(x)" : "( )");
9772 	if (label_size.x > 0.0f)
9773 		RenderText(text_bb.Min, label);
9774 
9775 	return pressed;
9776 }
9777 
RadioButton(const char * label,int * v,int v_button)9778 bool ImGui::RadioButton(const char* label, int* v, int v_button)
9779 {
9780 	const bool pressed = RadioButton(label, *v == v_button);
9781 	if (pressed)
9782 	{
9783 		*v = v_button;
9784 	}
9785 	return pressed;
9786 }
9787 
InputTextCalcTextLenAndLineCount(const char * text_begin,const char ** out_text_end)9788 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
9789 {
9790 	int line_count = 0;
9791 	const char* s = text_begin;
9792 	while (char c = *s++)  // We are only matching for \n so we can ignore UTF-8 decoding
9793 		if (c == '\n')
9794 			line_count++;
9795 	s--;
9796 	if (s[0] != '\n' && s[0] != '\r')
9797 		line_count++;
9798 	*out_text_end = s;
9799 	return line_count;
9800 }
9801 
InputTextCalcTextSizeW(const ImWchar * text_begin,const ImWchar * text_end,const ImWchar ** remaining,ImVec2 * out_offset,bool stop_on_new_line)9802 static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line)
9803 {
9804 	ImFont* font = GImGui->Font;
9805 	const float line_height = GImGui->FontSize;
9806 	const float scale = line_height / font->FontSize;
9807 
9808 	ImVec2 text_size = ImVec2(0, 0);
9809 	float line_width = 0.0f;
9810 
9811 	const ImWchar* s = text_begin;
9812 	while (s < text_end)
9813 	{
9814 		unsigned int c = (unsigned int)(*s++);
9815 		if (c == '\n')
9816 		{
9817 			text_size.x = ImMax(text_size.x, line_width);
9818 			text_size.y += line_height;
9819 			line_width = 0.0f;
9820 			if (stop_on_new_line)
9821 				break;
9822 			continue;
9823 		}
9824 		if (c == '\r')
9825 			continue;
9826 
9827 		const float char_width = font->GetCharAdvance((unsigned short)c) * scale;
9828 		line_width += char_width;
9829 	}
9830 
9831 	if (text_size.x < line_width)
9832 		text_size.x = line_width;
9833 
9834 	if (out_offset)
9835 		*out_offset = ImVec2(line_width, text_size.y + line_height);  // offset allow for the possibility of sitting after a trailing \n
9836 
9837 	if (line_width > 0 || text_size.y == 0.0f)  // whereas size.y will ignore the trailing \n
9838 		text_size.y += line_height;
9839 
9840 	if (remaining)
9841 		*remaining = s;
9842 
9843 	return text_size;
9844 }
9845 
9846 // 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)
9847 namespace ImGuiStb
9848 {
STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING * obj)9849 static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; }
STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING * obj,int idx)9850 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)9851 static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx)
9852 {
9853 	ImWchar c = obj->Text[line_start_idx + char_idx];
9854 	if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE;
9855 	return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize);
9856 }
STB_TEXTEDIT_KEYTOTEXT(int key)9857 static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; }
9858 static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
STB_TEXTEDIT_LAYOUTROW(StbTexteditRow * r,STB_TEXTEDIT_STRING * obj,int line_start_idx)9859 static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
9860 {
9861 	const ImWchar* text = obj->Text.Data;
9862 	const ImWchar* text_remaining = NULL;
9863 	const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true);
9864 	r->x0 = 0.0f;
9865 	r->x1 = size.x;
9866 	r->baseline_y_delta = size.y;
9867 	r->ymin = 0.0f;
9868 	r->ymax = size.y;
9869 	r->num_chars = (int)(text_remaining - (text + line_start_idx));
9870 }
9871 
is_separator(unsigned int c)9872 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)9873 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)9874 static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)
9875 {
9876 	idx--;
9877 	while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--;
9878 	return idx < 0 ? 0 : idx;
9879 }
9880 #ifdef __APPLE__  // FIXME: Move setting to IO structure
is_word_boundary_from_left(STB_TEXTEDIT_STRING * obj,int idx)9881 static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx)
9882 {
9883 	return idx > 0 ? (!is_separator(obj->Text[idx - 1]) && is_separator(obj->Text[idx])) : 1;
9884 }
STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING * obj,int idx)9885 static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)
9886 {
9887 	idx++;
9888 	int len = obj->CurLenW;
9889 	while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++;
9890 	return idx > len ? len : idx;
9891 }
9892 #else
STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING * obj,int idx)9893 static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)
9894 {
9895 	idx++;
9896 	int len = obj->CurLenW;
9897 	while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++;
9898 	return idx > len ? len : idx;
9899 }
9900 #endif
9901 #define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL  // They need to be #define for stb_textedit.h
9902 #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
9903 
STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING * obj,int pos,int n)9904 static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
9905 {
9906 	ImWchar* dst = obj->Text.Data + pos;
9907 
9908 	// We maintain our buffer length in both UTF-8 and wchar formats
9909 	obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);
9910 	obj->CurLenW -= n;
9911 
9912 	// Offset remaining text
9913 	const ImWchar* src = obj->Text.Data + pos + n;
9914 	while (ImWchar c = *src++)
9915 		*dst++ = c;
9916 	*dst = '\0';
9917 }
9918 
STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING * obj,int pos,const ImWchar * new_text,int new_text_len)9919 static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len)
9920 {
9921 	const int text_len = obj->CurLenW;
9922 	IM_ASSERT(pos <= text_len);
9923 	if (new_text_len + text_len + 1 > obj->Text.Size)
9924 		return false;
9925 
9926 	const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
9927 	if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA)
9928 		return false;
9929 
9930 	ImWchar* text = obj->Text.Data;
9931 	if (pos != text_len)
9932 		memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));
9933 	memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
9934 
9935 	obj->CurLenW += new_text_len;
9936 	obj->CurLenA += new_text_len_utf8;
9937 	obj->Text[obj->CurLenW] = '\0';
9938 
9939 	return true;
9940 }
9941 
9942 // 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)
9943 #define STB_TEXTEDIT_K_LEFT 0x10000       // keyboard input to move cursor left
9944 #define STB_TEXTEDIT_K_RIGHT 0x10001      // keyboard input to move cursor right
9945 #define STB_TEXTEDIT_K_UP 0x10002         // keyboard input to move cursor up
9946 #define STB_TEXTEDIT_K_DOWN 0x10003       // keyboard input to move cursor down
9947 #define STB_TEXTEDIT_K_LINESTART 0x10004  // keyboard input to move cursor to start of line
9948 #define STB_TEXTEDIT_K_LINEEND 0x10005    // keyboard input to move cursor to end of line
9949 #define STB_TEXTEDIT_K_TEXTSTART 0x10006  // keyboard input to move cursor to start of text
9950 #define STB_TEXTEDIT_K_TEXTEND 0x10007    // keyboard input to move cursor to end of text
9951 #define STB_TEXTEDIT_K_DELETE 0x10008     // keyboard input to delete selection or character under cursor
9952 #define STB_TEXTEDIT_K_BACKSPACE 0x10009  // keyboard input to delete selection or character left of cursor
9953 #define STB_TEXTEDIT_K_UNDO 0x1000A       // keyboard input to perform undo
9954 #define STB_TEXTEDIT_K_REDO 0x1000B       // keyboard input to perform redo
9955 #define STB_TEXTEDIT_K_WORDLEFT 0x1000C   // keyboard input to move cursor left one word
9956 #define STB_TEXTEDIT_K_WORDRIGHT 0x1000D  // keyboard input to move cursor right one word
9957 #define STB_TEXTEDIT_K_SHIFT 0x20000
9958 
9959 #define STB_TEXTEDIT_IMPLEMENTATION
9960 #include "stb_textedit.h"
9961 
9962 }  // namespace ImGuiStb
9963 
OnKeyPressed(int key)9964 void ImGuiTextEditState::OnKeyPressed(int key)
9965 {
9966 	stb_textedit_key(this, &StbState, key);
9967 	CursorFollow = true;
9968 	CursorAnimReset();
9969 }
9970 
9971 // Public API to manipulate UTF-8 text
9972 // We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar)
9973 // FIXME: The existence of this rarely exercised code path is a bit of a nuisance.
DeleteChars(int pos,int bytes_count)9974 void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count)
9975 {
9976 	IM_ASSERT(pos + bytes_count <= BufTextLen);
9977 	char* dst = Buf + pos;
9978 	const char* src = Buf + pos + bytes_count;
9979 	while (char c = *src++)
9980 		*dst++ = c;
9981 	*dst = '\0';
9982 
9983 	if (CursorPos + bytes_count >= pos)
9984 		CursorPos -= bytes_count;
9985 	else if (CursorPos >= pos)
9986 		CursorPos = pos;
9987 	SelectionStart = SelectionEnd = CursorPos;
9988 	BufDirty = true;
9989 	BufTextLen -= bytes_count;
9990 }
9991 
InsertChars(int pos,const char * new_text,const char * new_text_end)9992 void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)
9993 {
9994 	const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
9995 	if (new_text_len + BufTextLen + 1 >= BufSize)
9996 		return;
9997 
9998 	if (BufTextLen != pos)
9999 		memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos));
10000 	memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char));
10001 	Buf[BufTextLen + new_text_len] = '\0';
10002 
10003 	if (CursorPos >= pos)
10004 		CursorPos += new_text_len;
10005 	SelectionStart = SelectionEnd = CursorPos;
10006 	BufDirty = true;
10007 	BufTextLen += new_text_len;
10008 }
10009 
10010 // Return false to discard a character.
InputTextFilterCharacter(unsigned int * p_char,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)10011 static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
10012 {
10013 	unsigned int c = *p_char;
10014 
10015 	if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF)))
10016 	{
10017 		bool pass = false;
10018 		pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline));
10019 		pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput));
10020 		if (!pass)
10021 			return false;
10022 	}
10023 
10024 	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.
10025 		return false;
10026 
10027 	if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank))
10028 	{
10029 		if (flags & ImGuiInputTextFlags_CharsDecimal)
10030 			if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/'))
10031 				return false;
10032 
10033 		if (flags & ImGuiInputTextFlags_CharsHexadecimal)
10034 			if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F'))
10035 				return false;
10036 
10037 		if (flags & ImGuiInputTextFlags_CharsUppercase)
10038 			if (c >= 'a' && c <= 'z')
10039 				*p_char = (c += (unsigned int)('A' - 'a'));
10040 
10041 		if (flags & ImGuiInputTextFlags_CharsNoBlank)
10042 			if (ImCharIsSpace(c))
10043 				return false;
10044 	}
10045 
10046 	if (flags & ImGuiInputTextFlags_CallbackCharFilter)
10047 	{
10048 		ImGuiTextEditCallbackData callback_data;
10049 		memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
10050 		callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;
10051 		callback_data.EventChar = (ImWchar)c;
10052 		callback_data.Flags = flags;
10053 		callback_data.UserData = user_data;
10054 		if (callback(&callback_data) != 0)
10055 			return false;
10056 		*p_char = callback_data.EventChar;
10057 		if (!callback_data.EventChar)
10058 			return false;
10059 	}
10060 
10061 	return true;
10062 }
10063 
10064 // Edit a string of text
10065 // 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.
10066 // 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)10067 bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
10068 {
10069 	ImGuiWindow* window = GetCurrentWindow();
10070 	if (window->SkipItems)
10071 		return false;
10072 
10073 	IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline)));         // Can't use both together (they both use up/down keys)
10074 	IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput)));  // Can't use both together (they both use tab key)
10075 
10076 	ImGuiContext& g = *GImGui;
10077 	const ImGuiIO& io = g.IO;
10078 	const ImGuiStyle& style = g.Style;
10079 
10080 	const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;
10081 	const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0;
10082 	const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
10083 	const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0;
10084 
10085 	if (is_multiline)  // Open group before calling GetID() because groups tracks id created during their spawn
10086 		BeginGroup();
10087 	const ImGuiID id = window->GetID(label);
10088 	const ImVec2 label_size = CalcTextSize(label, NULL, true);
10089 	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
10090 	const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
10091 	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));
10092 
10093 	ImGuiWindow* draw_window = window;
10094 	if (is_multiline)
10095 	{
10096 		ItemAdd(total_bb, id, &frame_bb);
10097 		if (!BeginChildFrame(id, frame_bb.GetSize()))
10098 		{
10099 			EndChildFrame();
10100 			EndGroup();
10101 			return false;
10102 		}
10103 		draw_window = GetCurrentWindow();
10104 		size.x -= draw_window->ScrollbarSizes.x;
10105 	}
10106 	else
10107 	{
10108 		ItemSize(total_bb, style.FramePadding.y);
10109 		if (!ItemAdd(total_bb, id, &frame_bb))
10110 			return false;
10111 	}
10112 	const bool hovered = ItemHoverable(frame_bb, id);
10113 	if (hovered)
10114 		g.MouseCursor = ImGuiMouseCursor_TextInput;
10115 
10116 	// Password pushes a temporary font with only a fallback glyph
10117 	if (is_password)
10118 	{
10119 		const ImFontGlyph* glyph = g.Font->FindGlyph('*');
10120 		ImFont* password_font = &g.InputTextPasswordFont;
10121 		password_font->FontSize = g.Font->FontSize;
10122 		password_font->Scale = g.Font->Scale;
10123 		password_font->DisplayOffset = g.Font->DisplayOffset;
10124 		password_font->Ascent = g.Font->Ascent;
10125 		password_font->Descent = g.Font->Descent;
10126 		password_font->ContainerAtlas = g.Font->ContainerAtlas;
10127 		password_font->FallbackGlyph = glyph;
10128 		password_font->FallbackAdvanceX = glyph->AdvanceX;
10129 		IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty());
10130 		PushFont(password_font);
10131 	}
10132 
10133 	// NB: we are only allowed to access 'edit_state' if we are the active widget.
10134 	ImGuiTextEditState& edit_state = g.InputTextState;
10135 
10136 	const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) == 0);  // Using completion callback disable keyboard tabbing
10137 	const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent);
10138 	const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code;
10139 
10140 	const bool user_clicked = hovered && io.MouseClicked[0];
10141 	const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY");
10142 
10143 	bool clear_active_id = false;
10144 
10145 	bool select_all = (g.ActiveId != id) && (((flags & ImGuiInputTextFlags_AutoSelectAll) != 0) || (g.NavInputId == id)) && (!is_multiline);
10146 	if (focus_requested || user_clicked || user_scrolled || g.NavInputId == id)
10147 	{
10148 		if (g.ActiveId != id)
10149 		{
10150 			// Start edition
10151 			// Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)
10152 			// From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode)
10153 			const int prev_len_w = edit_state.CurLenW;
10154 			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.
10155 			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.
10156 			ImStrncpy(edit_state.InitialText.Data, buf, edit_state.InitialText.Size);
10157 			const char* buf_end = NULL;
10158 			edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
10159 			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.
10160 			edit_state.CursorAnimReset();
10161 
10162 			// Preserve cursor position and undo/redo stack if we come back to same widget
10163 			// FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar).
10164 			const bool recycle_state = (edit_state.Id == id) && (prev_len_w == edit_state.CurLenW);
10165 			if (recycle_state)
10166 			{
10167 				// Recycle existing cursor/selection/undo stack but clamp position
10168 				// Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
10169 				edit_state.CursorClamp();
10170 			}
10171 			else
10172 			{
10173 				edit_state.Id = id;
10174 				edit_state.ScrollX = 0.0f;
10175 				stb_textedit_initialize_state(&edit_state.StbState, !is_multiline);
10176 				if (!is_multiline && focus_requested_by_code)
10177 					select_all = true;
10178 			}
10179 			if (flags & ImGuiInputTextFlags_AlwaysInsertMode)
10180 				edit_state.StbState.insert_mode = true;
10181 			if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl)))
10182 				select_all = true;
10183 		}
10184 		SetActiveID(id, window);
10185 		SetFocusID(id, window);
10186 		FocusWindow(window);
10187 		if (!is_multiline && !(flags & ImGuiInputTextFlags_CallbackHistory))
10188 			g.ActiveIdAllowNavDirFlags |= ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down));
10189 	}
10190 	else if (io.MouseClicked[0])
10191 	{
10192 		// Release focus when we click outside
10193 		clear_active_id = true;
10194 	}
10195 
10196 	bool value_changed = false;
10197 	bool enter_pressed = false;
10198 
10199 	if (g.ActiveId == id)
10200 	{
10201 		if (!is_editable && !g.ActiveIdIsJustActivated)
10202 		{
10203 			// When read-only we always use the live data passed to the function
10204 			edit_state.Text.resize(buf_size + 1);
10205 			const char* buf_end = NULL;
10206 			edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
10207 			edit_state.CurLenA = (int)(buf_end - buf);
10208 			edit_state.CursorClamp();
10209 		}
10210 
10211 		edit_state.BufSizeA = buf_size;
10212 
10213 		// Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
10214 		// Down the line we should have a cleaner library-wide concept of Selected vs Active.
10215 		g.ActiveIdAllowOverlap = !io.MouseDown[0];
10216 		g.WantTextInputNextFrame = 1;
10217 
10218 		// Edit in progress
10219 		const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX;
10220 		const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize * 0.5f));
10221 
10222 		const bool osx_double_click_selects_words = io.OptMacOSXBehaviors;  // OS X style: Double click selects by word instead of selecting whole text
10223 		if (select_all || (hovered && !osx_double_click_selects_words && io.MouseDoubleClicked[0]))
10224 		{
10225 			edit_state.SelectAll();
10226 			edit_state.SelectedAllMouseLock = true;
10227 		}
10228 		else if (hovered && osx_double_click_selects_words && io.MouseDoubleClicked[0])
10229 		{
10230 			// Select a word only, OS X style (by simulating keystrokes)
10231 			edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT);
10232 			edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);
10233 		}
10234 		else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock)
10235 		{
10236 			if (hovered)
10237 			{
10238 				stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
10239 				edit_state.CursorAnimReset();
10240 			}
10241 		}
10242 		else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f))
10243 		{
10244 			stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
10245 			edit_state.CursorAnimReset();
10246 			edit_state.CursorFollow = true;
10247 		}
10248 		if (edit_state.SelectedAllMouseLock && !io.MouseDown[0])
10249 			edit_state.SelectedAllMouseLock = false;
10250 
10251 		if (io.InputCharacters[0])
10252 		{
10253 			// Process text input (before we check for Return because using some IME will effectively send a Return?)
10254 			// We ignore CTRL inputs, but need to allow CTRL+ALT as some keyboards (e.g. German) use AltGR - which is Alt+Ctrl - to input certain characters.
10255 			if (!(io.KeyCtrl && !io.KeyAlt) && is_editable)
10256 			{
10257 				for (int n = 0; n < IM_ARRAYSIZE(io.InputCharacters) && io.InputCharacters[n]; n++)
10258 					if (unsigned int c = (unsigned int)io.InputCharacters[n])
10259 					{
10260 						// Insert character if they pass filtering
10261 						if (!InputTextFilterCharacter(&c, flags, callback, user_data))
10262 							continue;
10263 						edit_state.OnKeyPressed((int)c);
10264 					}
10265 			}
10266 
10267 			// Consume characters
10268 			memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
10269 		}
10270 	}
10271 
10272 	bool cancel_edit = false;
10273 	if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id)
10274 	{
10275 		// Handle key-presses
10276 		const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
10277 		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
10278 		const bool is_wordmove_key_down = io.OptMacOSXBehaviors ? io.KeyAlt : io.KeyCtrl;                                                                       // OS X style: Text editing cursor movement using Alt instead of Ctrl
10279 		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
10280 		const bool is_ctrl_key_only = io.KeyCtrl && !io.KeyShift && !io.KeyAlt && !io.KeySuper;
10281 		const bool is_shift_key_only = io.KeyShift && !io.KeyCtrl && !io.KeyAlt && !io.KeySuper;
10282 
10283 		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());
10284 		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());
10285 		const bool is_paste = ((is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && is_editable;
10286 
10287 		if (IsKeyPressedMap(ImGuiKey_LeftArrow))
10288 		{
10289 			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);
10290 		}
10291 		else if (IsKeyPressedMap(ImGuiKey_RightArrow))
10292 		{
10293 			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);
10294 		}
10295 		else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline)
10296 		{
10297 			if (io.KeyCtrl)
10298 				SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f));
10299 			else
10300 				edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask);
10301 		}
10302 		else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline)
10303 		{
10304 			if (io.KeyCtrl)
10305 				SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY()));
10306 			else
10307 				edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask);
10308 		}
10309 		else if (IsKeyPressedMap(ImGuiKey_Home))
10310 		{
10311 			edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask);
10312 		}
10313 		else if (IsKeyPressedMap(ImGuiKey_End))
10314 		{
10315 			edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask);
10316 		}
10317 		else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable)
10318 		{
10319 			edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask);
10320 		}
10321 		else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable)
10322 		{
10323 			if (!edit_state.HasSelection())
10324 			{
10325 				if (is_wordmove_key_down)
10326 					edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT);
10327 				else if (io.OptMacOSXBehaviors && io.KeySuper && !io.KeyAlt && !io.KeyCtrl)
10328 					edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT);
10329 			}
10330 			edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);
10331 		}
10332 		else if (IsKeyPressedMap(ImGuiKey_Enter))
10333 		{
10334 			bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;
10335 			if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl))
10336 			{
10337 				enter_pressed = clear_active_id = true;
10338 			}
10339 			else if (is_editable)
10340 			{
10341 				unsigned int c = '\n';  // Insert new line
10342 				if (InputTextFilterCharacter(&c, flags, callback, user_data))
10343 					edit_state.OnKeyPressed((int)c);
10344 			}
10345 		}
10346 		else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable)
10347 		{
10348 			unsigned int c = '\t';  // Insert TAB
10349 			if (InputTextFilterCharacter(&c, flags, callback, user_data))
10350 				edit_state.OnKeyPressed((int)c);
10351 		}
10352 		else if (IsKeyPressedMap(ImGuiKey_Escape))
10353 		{
10354 			clear_active_id = cancel_edit = true;
10355 		}
10356 		else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable && is_undoable)
10357 		{
10358 			edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO);
10359 			edit_state.ClearSelection();
10360 		}
10361 		else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable && is_undoable)
10362 		{
10363 			edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO);
10364 			edit_state.ClearSelection();
10365 		}
10366 		else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_A))
10367 		{
10368 			edit_state.SelectAll();
10369 			edit_state.CursorFollow = true;
10370 		}
10371 		else if (is_cut || is_copy)
10372 		{
10373 			// Cut, Copy
10374 			if (io.SetClipboardTextFn)
10375 			{
10376 				const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0;
10377 				const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW;
10378 				edit_state.TempTextBuffer.resize((ie - ib) * 4 + 1);
10379 				ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data + ib, edit_state.Text.Data + ie);
10380 				SetClipboardText(edit_state.TempTextBuffer.Data);
10381 			}
10382 
10383 			if (is_cut)
10384 			{
10385 				if (!edit_state.HasSelection())
10386 					edit_state.SelectAll();
10387 				edit_state.CursorFollow = true;
10388 				stb_textedit_cut(&edit_state, &edit_state.StbState);
10389 			}
10390 		}
10391 		else if (is_paste)
10392 		{
10393 			// Paste
10394 			if (const char* clipboard = GetClipboardText())
10395 			{
10396 				// Filter pasted buffer
10397 				const int clipboard_len = (int)strlen(clipboard);
10398 				ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len + 1) * sizeof(ImWchar));
10399 				int clipboard_filtered_len = 0;
10400 				for (const char* s = clipboard; *s;)
10401 				{
10402 					unsigned int c;
10403 					s += ImTextCharFromUtf8(&c, s, NULL);
10404 					if (c == 0)
10405 						break;
10406 					if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, user_data))
10407 						continue;
10408 					clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;
10409 				}
10410 				clipboard_filtered[clipboard_filtered_len] = 0;
10411 				if (clipboard_filtered_len > 0)  // If everything was filtered, ignore the pasting operation
10412 				{
10413 					stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len);
10414 					edit_state.CursorFollow = true;
10415 				}
10416 				ImGui::MemFree(clipboard_filtered);
10417 			}
10418 		}
10419 	}
10420 
10421 	if (g.ActiveId == id)
10422 	{
10423 		if (cancel_edit)
10424 		{
10425 			// Restore initial value
10426 			if (is_editable)
10427 			{
10428 				ImStrncpy(buf, edit_state.InitialText.Data, buf_size);
10429 				value_changed = true;
10430 			}
10431 		}
10432 
10433 		// 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.
10434 		// 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.
10435 		bool apply_edit_back_to_user_buffer = !cancel_edit || (enter_pressed && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0);
10436 		if (apply_edit_back_to_user_buffer)
10437 		{
10438 			// Apply new value immediately - copy modified buffer back
10439 			// Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer
10440 			// FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect.
10441 			// FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks.
10442 			if (is_editable)
10443 			{
10444 				edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4);
10445 				ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL);
10446 			}
10447 
10448 			// User callback
10449 			if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0)
10450 			{
10451 				IM_ASSERT(callback != NULL);
10452 
10453 				// The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
10454 				ImGuiInputTextFlags event_flag = 0;
10455 				ImGuiKey event_key = ImGuiKey_COUNT;
10456 				if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab))
10457 				{
10458 					event_flag = ImGuiInputTextFlags_CallbackCompletion;
10459 					event_key = ImGuiKey_Tab;
10460 				}
10461 				else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow))
10462 				{
10463 					event_flag = ImGuiInputTextFlags_CallbackHistory;
10464 					event_key = ImGuiKey_UpArrow;
10465 				}
10466 				else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow))
10467 				{
10468 					event_flag = ImGuiInputTextFlags_CallbackHistory;
10469 					event_key = ImGuiKey_DownArrow;
10470 				}
10471 				else if (flags & ImGuiInputTextFlags_CallbackAlways)
10472 					event_flag = ImGuiInputTextFlags_CallbackAlways;
10473 
10474 				if (event_flag)
10475 				{
10476 					ImGuiTextEditCallbackData callback_data;
10477 					memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
10478 					callback_data.EventFlag = event_flag;
10479 					callback_data.Flags = flags;
10480 					callback_data.UserData = user_data;
10481 					callback_data.ReadOnly = !is_editable;
10482 
10483 					callback_data.EventKey = event_key;
10484 					callback_data.Buf = edit_state.TempTextBuffer.Data;
10485 					callback_data.BufTextLen = edit_state.CurLenA;
10486 					callback_data.BufSize = edit_state.BufSizeA;
10487 					callback_data.BufDirty = false;
10488 
10489 					// 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)
10490 					ImWchar* text = edit_state.Text.Data;
10491 					const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor);
10492 					const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start);
10493 					const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end);
10494 
10495 					// Call user code
10496 					callback(&callback_data);
10497 
10498 					// Read back what user may have modified
10499 					IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data);  // Invalid to modify those fields
10500 					IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA);
10501 					IM_ASSERT(callback_data.Flags == flags);
10502 					if (callback_data.CursorPos != utf8_cursor_pos) edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos);
10503 					if (callback_data.SelectionStart != utf8_selection_start) edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart);
10504 					if (callback_data.SelectionEnd != utf8_selection_end) edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd);
10505 					if (callback_data.BufDirty)
10506 					{
10507 						IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf));  // You need to maintain BufTextLen if you change the text!
10508 						edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, callback_data.Buf, NULL);
10509 						edit_state.CurLenA = callback_data.BufTextLen;  // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
10510 						edit_state.CursorAnimReset();
10511 					}
10512 				}
10513 			}
10514 
10515 			// Copy back to user buffer
10516 			if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0)
10517 			{
10518 				ImStrncpy(buf, edit_state.TempTextBuffer.Data, buf_size);
10519 				value_changed = true;
10520 			}
10521 		}
10522 	}
10523 
10524 	// Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value)
10525 	if (clear_active_id && g.ActiveId == id)
10526 		ClearActiveID();
10527 
10528 	// Render
10529 	// 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.
10530 	const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf;
10531 	buf = NULL;
10532 
10533 	RenderNavHighlight(frame_bb, id);
10534 	if (!is_multiline)
10535 		RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
10536 
10537 	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
10538 	ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;
10539 	ImVec2 text_size(0.f, 0.f);
10540 	const bool is_currently_scrolling = (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY"));
10541 	if (g.ActiveId == id || is_currently_scrolling)
10542 	{
10543 		edit_state.CursorAnim += io.DeltaTime;
10544 
10545 		// This is going to be messy. We need to:
10546 		// - Display the text (this alone can be more easily clipped)
10547 		// - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)
10548 		// - Measure text height (for scrollbar)
10549 		// 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)
10550 		// FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
10551 		const ImWchar* text_begin = edit_state.Text.Data;
10552 		ImVec2 cursor_offset, select_start_offset;
10553 
10554 		{
10555 			// Count lines + find lines numbers straddling 'cursor' and 'select_start' position.
10556 			const ImWchar* searches_input_ptr[2];
10557 			searches_input_ptr[0] = text_begin + edit_state.StbState.cursor;
10558 			searches_input_ptr[1] = NULL;
10559 			int searches_remaining = 1;
10560 			int searches_result_line_number[2] = {-1, -999};
10561 			if (edit_state.StbState.select_start != edit_state.StbState.select_end)
10562 			{
10563 				searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
10564 				searches_result_line_number[1] = -1;
10565 				searches_remaining++;
10566 			}
10567 
10568 			// Iterate all lines to find our line numbers
10569 			// In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter.
10570 			searches_remaining += is_multiline ? 1 : 0;
10571 			int line_count = 0;
10572 			for (const ImWchar* s = text_begin; *s != 0; s++)
10573 				if (*s == '\n')
10574 				{
10575 					line_count++;
10576 					if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0])
10577 					{
10578 						searches_result_line_number[0] = line_count;
10579 						if (--searches_remaining <= 0) break;
10580 					}
10581 					if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1])
10582 					{
10583 						searches_result_line_number[1] = line_count;
10584 						if (--searches_remaining <= 0) break;
10585 					}
10586 				}
10587 			line_count++;
10588 			if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count;
10589 			if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count;
10590 
10591 			// Calculate 2d position by finding the beginning of the line and measuring distance
10592 			cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;
10593 			cursor_offset.y = searches_result_line_number[0] * g.FontSize;
10594 			if (searches_result_line_number[1] >= 0)
10595 			{
10596 				select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;
10597 				select_start_offset.y = searches_result_line_number[1] * g.FontSize;
10598 			}
10599 
10600 			// Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224)
10601 			if (is_multiline)
10602 				text_size = ImVec2(size.x, line_count * g.FontSize);
10603 		}
10604 
10605 		// Scroll
10606 		if (edit_state.CursorFollow)
10607 		{
10608 			// Horizontal scroll in chunks of quarter width
10609 			if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll))
10610 			{
10611 				const float scroll_increment_x = size.x * 0.25f;
10612 				if (cursor_offset.x < edit_state.ScrollX)
10613 					edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x);
10614 				else if (cursor_offset.x - size.x >= edit_state.ScrollX)
10615 					edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x);
10616 			}
10617 			else
10618 			{
10619 				edit_state.ScrollX = 0.0f;
10620 			}
10621 
10622 			// Vertical scroll
10623 			if (is_multiline)
10624 			{
10625 				float scroll_y = draw_window->Scroll.y;
10626 				if (cursor_offset.y - g.FontSize < scroll_y)
10627 					scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
10628 				else if (cursor_offset.y - size.y >= scroll_y)
10629 					scroll_y = cursor_offset.y - size.y;
10630 				draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y);  // To avoid a frame of lag
10631 				draw_window->Scroll.y = scroll_y;
10632 				render_pos.y = draw_window->DC.CursorPos.y;
10633 			}
10634 		}
10635 		edit_state.CursorFollow = false;
10636 		const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f);
10637 
10638 		// Draw selection
10639 		if (edit_state.StbState.select_start != edit_state.StbState.select_end)
10640 		{
10641 			const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
10642 			const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end);
10643 
10644 			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.
10645 			float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
10646 			ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg);
10647 			ImVec2 rect_pos = render_pos + select_start_offset - render_scroll;
10648 			for (const ImWchar* p = text_selected_begin; p < text_selected_end;)
10649 			{
10650 				if (rect_pos.y > clip_rect.w + g.FontSize)
10651 					break;
10652 				if (rect_pos.y < clip_rect.y)
10653 				{
10654 					while (p < text_selected_end)
10655 						if (*p++ == '\n')
10656 							break;
10657 				}
10658 				else
10659 				{
10660 					ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true);
10661 					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
10662 					ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn));
10663 					rect.ClipWith(clip_rect);
10664 					if (rect.Overlaps(clip_rect))
10665 						draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
10666 				}
10667 				rect_pos.x = render_pos.x - render_scroll.x;
10668 				rect_pos.y += g.FontSize;
10669 			}
10670 		}
10671 
10672 		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);
10673 
10674 		// Draw blinking cursor
10675 		bool cursor_is_visible = (!g.IO.OptCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || fmodf(g.InputTextState.CursorAnim, 1.20f) <= 0.80f;
10676 		ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll;
10677 		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);
10678 		if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
10679 			draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
10680 
10681 		// 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.)
10682 		if (is_editable)
10683 			g.OsImePosRequest = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize);
10684 	}
10685 	else
10686 	{
10687 		// Render text only
10688 		const char* buf_end = NULL;
10689 		if (is_multiline)
10690 			text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize);  // We don't need width
10691 		draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect);
10692 	}
10693 
10694 	if (is_multiline)
10695 	{
10696 		Dummy(text_size + ImVec2(0.0f, g.FontSize));  // Always add room to scroll an extra line
10697 		EndChildFrame();
10698 		EndGroup();
10699 	}
10700 
10701 	if (is_password)
10702 		PopFont();
10703 
10704 	// Log as text
10705 	if (g.LogEnabled && !is_password)
10706 		LogRenderedText(&render_pos, buf_display, NULL);
10707 
10708 	if (label_size.x > 0)
10709 		RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
10710 
10711 	if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)
10712 		return enter_pressed;
10713 	else
10714 		return value_changed;
10715 }
10716 
InputText(const char * label,char * buf,size_t buf_size,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)10717 bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
10718 {
10719 	IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline));  // call InputTextMultiline()
10720 	return InputTextEx(label, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data);
10721 }
10722 
InputTextMultiline(const char * label,char * buf,size_t buf_size,const ImVec2 & size,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)10723 bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
10724 {
10725 	return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);
10726 }
10727 
10728 // 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)10729 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)
10730 {
10731 	ImGuiWindow* window = GetCurrentWindow();
10732 	if (window->SkipItems)
10733 		return false;
10734 
10735 	ImGuiContext& g = *GImGui;
10736 	const ImGuiStyle& style = g.Style;
10737 	const ImVec2 label_size = CalcTextSize(label, NULL, true);
10738 
10739 	BeginGroup();
10740 	PushID(label);
10741 	const ImVec2 button_sz = ImVec2(GetFrameHeight(), GetFrameHeight());
10742 	if (step_ptr)
10743 		PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_sz.x + style.ItemInnerSpacing.x) * 2));
10744 
10745 	char buf[64];
10746 	DataTypeFormatString(data_type, data_ptr, scalar_format, buf, IM_ARRAYSIZE(buf));
10747 
10748 	bool value_changed = false;
10749 	if (!(extra_flags & ImGuiInputTextFlags_CharsHexadecimal))
10750 		extra_flags |= ImGuiInputTextFlags_CharsDecimal;
10751 	extra_flags |= ImGuiInputTextFlags_AutoSelectAll;
10752 	if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags))  // PushId(label) + "" gives us the expected ID from outside point of view
10753 		value_changed = DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, scalar_format);
10754 
10755 	// Step buttons
10756 	if (step_ptr)
10757 	{
10758 		PopItemWidth();
10759 		SameLine(0, style.ItemInnerSpacing.x);
10760 		if (ButtonEx("-", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
10761 		{
10762 			DataTypeApplyOp(data_type, '-', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
10763 			value_changed = true;
10764 		}
10765 		SameLine(0, style.ItemInnerSpacing.x);
10766 		if (ButtonEx("+", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
10767 		{
10768 			DataTypeApplyOp(data_type, '+', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
10769 			value_changed = true;
10770 		}
10771 	}
10772 	PopID();
10773 
10774 	if (label_size.x > 0)
10775 	{
10776 		SameLine(0, style.ItemInnerSpacing.x);
10777 		RenderText(ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + style.FramePadding.y), label);
10778 		ItemSize(label_size, style.FramePadding.y);
10779 	}
10780 	EndGroup();
10781 
10782 	return value_changed;
10783 }
10784 
InputFloat(const char * label,float * v,float step,float step_fast,int decimal_precision,ImGuiInputTextFlags extra_flags)10785 bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags)
10786 {
10787 	char display_format[16];
10788 	if (decimal_precision < 0)
10789 		strcpy(display_format, "%f");  // Ideally we'd have a minimum decimal precision of 1 to visually denote that this is a float, while hiding non-significant digits? %f doesn't have a minimum of 1
10790 	else
10791 		ImFormatString(display_format, IM_ARRAYSIZE(display_format), "%%.%df", decimal_precision);
10792 	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);
10793 }
10794 
InputInt(const char * label,int * v,int step,int step_fast,ImGuiInputTextFlags extra_flags)10795 bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags)
10796 {
10797 	// 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.
10798 	const char* scalar_format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d";
10799 	return InputScalarEx(label, ImGuiDataType_Int, (void*)v, (void*)(step > 0.0f ? &step : NULL), (void*)(step_fast > 0.0f ? &step_fast : NULL), scalar_format, extra_flags);
10800 }
10801 
InputFloatN(const char * label,float * v,int components,int decimal_precision,ImGuiInputTextFlags extra_flags)10802 bool ImGui::InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags)
10803 {
10804 	ImGuiWindow* window = GetCurrentWindow();
10805 	if (window->SkipItems)
10806 		return false;
10807 
10808 	ImGuiContext& g = *GImGui;
10809 	bool value_changed = false;
10810 	BeginGroup();
10811 	PushID(label);
10812 	PushMultiItemsWidths(components);
10813 	for (int i = 0; i < components; i++)
10814 	{
10815 		PushID(i);
10816 		value_changed |= InputFloat("##v", &v[i], 0, 0, decimal_precision, extra_flags);
10817 		SameLine(0, g.Style.ItemInnerSpacing.x);
10818 		PopID();
10819 		PopItemWidth();
10820 	}
10821 	PopID();
10822 
10823 	TextUnformatted(label, FindRenderedTextEnd(label));
10824 	EndGroup();
10825 
10826 	return value_changed;
10827 }
10828 
InputFloat2(const char * label,float v[2],int decimal_precision,ImGuiInputTextFlags extra_flags)10829 bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags)
10830 {
10831 	return InputFloatN(label, v, 2, decimal_precision, extra_flags);
10832 }
10833 
InputFloat3(const char * label,float v[3],int decimal_precision,ImGuiInputTextFlags extra_flags)10834 bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags)
10835 {
10836 	return InputFloatN(label, v, 3, decimal_precision, extra_flags);
10837 }
10838 
InputFloat4(const char * label,float v[4],int decimal_precision,ImGuiInputTextFlags extra_flags)10839 bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags)
10840 {
10841 	return InputFloatN(label, v, 4, decimal_precision, extra_flags);
10842 }
10843 
InputIntN(const char * label,int * v,int components,ImGuiInputTextFlags extra_flags)10844 bool ImGui::InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags)
10845 {
10846 	ImGuiWindow* window = GetCurrentWindow();
10847 	if (window->SkipItems)
10848 		return false;
10849 
10850 	ImGuiContext& g = *GImGui;
10851 	bool value_changed = false;
10852 	BeginGroup();
10853 	PushID(label);
10854 	PushMultiItemsWidths(components);
10855 	for (int i = 0; i < components; i++)
10856 	{
10857 		PushID(i);
10858 		value_changed |= InputInt("##v", &v[i], 0, 0, extra_flags);
10859 		SameLine(0, g.Style.ItemInnerSpacing.x);
10860 		PopID();
10861 		PopItemWidth();
10862 	}
10863 	PopID();
10864 
10865 	TextUnformatted(label, FindRenderedTextEnd(label));
10866 	EndGroup();
10867 
10868 	return value_changed;
10869 }
10870 
InputInt2(const char * label,int v[2],ImGuiInputTextFlags extra_flags)10871 bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags)
10872 {
10873 	return InputIntN(label, v, 2, extra_flags);
10874 }
10875 
InputInt3(const char * label,int v[3],ImGuiInputTextFlags extra_flags)10876 bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags)
10877 {
10878 	return InputIntN(label, v, 3, extra_flags);
10879 }
10880 
InputInt4(const char * label,int v[4],ImGuiInputTextFlags extra_flags)10881 bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags)
10882 {
10883 	return InputIntN(label, v, 4, extra_flags);
10884 }
10885 
CalcMaxPopupHeightFromItemCount(int items_count)10886 static float CalcMaxPopupHeightFromItemCount(int items_count)
10887 {
10888 	ImGuiContext& g = *GImGui;
10889 	if (items_count <= 0)
10890 		return FLT_MAX;
10891 	return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2);
10892 }
10893 
BeginCombo(const char * label,const char * preview_value,ImGuiComboFlags flags)10894 bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags)
10895 {
10896 	// Always consume the SetNextWindowSizeConstraint() call in our early return paths
10897 	ImGuiContext& g = *GImGui;
10898 	ImGuiCond backup_next_window_size_constraint = g.NextWindowData.SizeConstraintCond;
10899 	g.NextWindowData.SizeConstraintCond = 0;
10900 
10901 	ImGuiWindow* window = GetCurrentWindow();
10902 	if (window->SkipItems)
10903 		return false;
10904 
10905 	const ImGuiStyle& style = g.Style;
10906 	const ImGuiID id = window->GetID(label);
10907 	const float w = CalcItemWidth();
10908 
10909 	const ImVec2 label_size = CalcTextSize(label, NULL, true);
10910 	const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
10911 	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));
10912 	ItemSize(total_bb, style.FramePadding.y);
10913 	if (!ItemAdd(total_bb, id, &frame_bb))
10914 		return false;
10915 
10916 	bool hovered, held;
10917 	bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
10918 	bool popup_open = IsPopupOpen(id);
10919 
10920 	const float arrow_size = GetFrameHeight();
10921 	const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
10922 	RenderNavHighlight(frame_bb, id);
10923 	RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
10924 	RenderFrame(ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(popup_open || hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding);  // FIXME-ROUNDING
10925 	RenderTriangle(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down);
10926 	if (preview_value != NULL)
10927 		RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, preview_value, NULL, NULL, ImVec2(0.0f, 0.0f));
10928 	if (label_size.x > 0)
10929 		RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
10930 
10931 	if ((pressed || g.NavActivateId == id) && !popup_open)
10932 	{
10933 		if (window->DC.NavLayerCurrent == 0)
10934 			window->NavLastIds[0] = id;
10935 		OpenPopupEx(id);
10936 		popup_open = true;
10937 	}
10938 
10939 	if (!popup_open)
10940 		return false;
10941 
10942 	if (backup_next_window_size_constraint)
10943 	{
10944 		g.NextWindowData.SizeConstraintCond = backup_next_window_size_constraint;
10945 		g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w);
10946 	}
10947 	else
10948 	{
10949 		if ((flags & ImGuiComboFlags_HeightMask_) == 0)
10950 			flags |= ImGuiComboFlags_HeightRegular;
10951 		IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_));  // Only one
10952 		int popup_max_height_in_items = -1;
10953 		if (flags & ImGuiComboFlags_HeightRegular)
10954 			popup_max_height_in_items = 8;
10955 		else if (flags & ImGuiComboFlags_HeightSmall)
10956 			popup_max_height_in_items = 4;
10957 		else if (flags & ImGuiComboFlags_HeightLarge)
10958 			popup_max_height_in_items = 20;
10959 		SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
10960 	}
10961 
10962 	char name[16];
10963 	ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.CurrentPopupStack.Size);  // Recycle windows based on depth
10964 
10965 	// Peak into expected window size so we can position it
10966 	if (ImGuiWindow* popup_window = FindWindowByName(name))
10967 		if (popup_window->WasActive)
10968 		{
10969 			ImVec2 size_contents = CalcSizeContents(popup_window);
10970 			ImVec2 size_expected = CalcSizeAfterConstraint(popup_window, CalcSizeAutoFit(popup_window, size_contents));
10971 			if (flags & ImGuiComboFlags_PopupAlignLeft)
10972 				popup_window->AutoPosLastDirection = ImGuiDir_Left;
10973 			ImVec2 pos = FindBestWindowPosForPopup(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, frame_bb, ImGuiPopupPositionPolicy_ComboBox);
10974 			SetNextWindowPos(pos);
10975 		}
10976 
10977 	ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
10978 	if (!Begin(name, NULL, window_flags))
10979 	{
10980 		EndPopup();
10981 		IM_ASSERT(0);  // This should never happen as we tested for IsPopupOpen() above
10982 		return false;
10983 	}
10984 
10985 	// Horizontally align ourselves with the framed text
10986 	if (style.FramePadding.x != style.WindowPadding.x)
10987 		Indent(style.FramePadding.x - style.WindowPadding.x);
10988 
10989 	return true;
10990 }
10991 
EndCombo()10992 void ImGui::EndCombo()
10993 {
10994 	const ImGuiStyle& style = GImGui->Style;
10995 	if (style.FramePadding.x != style.WindowPadding.x)
10996 		Unindent(style.FramePadding.x - style.WindowPadding.x);
10997 	EndPopup();
10998 }
10999 
11000 // 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)11001 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)
11002 {
11003 	ImGuiContext& g = *GImGui;
11004 
11005 	const char* preview_text = NULL;
11006 	if (*current_item >= 0 && *current_item < items_count)
11007 		items_getter(data, *current_item, &preview_text);
11008 
11009 	// The old Combo() API exposed "popup_max_height_in_items", however the new more general BeginCombo() API doesn't, so we emulate it here.
11010 	if (popup_max_height_in_items != -1 && !g.NextWindowData.SizeConstraintCond)
11011 	{
11012 		float popup_max_height = CalcMaxPopupHeightFromItemCount(popup_max_height_in_items);
11013 		SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, popup_max_height));
11014 	}
11015 
11016 	if (!BeginCombo(label, preview_text, 0))
11017 		return false;
11018 
11019 	// Display items
11020 	// FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed)
11021 	bool value_changed = false;
11022 	for (int i = 0; i < items_count; i++)
11023 	{
11024 		PushID((void*)(intptr_t)i);
11025 		const bool item_selected = (i == *current_item);
11026 		const char* item_text;
11027 		if (!items_getter(data, i, &item_text))
11028 			item_text = "*Unknown item*";
11029 		if (Selectable(item_text, item_selected))
11030 		{
11031 			value_changed = true;
11032 			*current_item = i;
11033 		}
11034 		if (item_selected)
11035 			SetItemDefaultFocus();
11036 		PopID();
11037 	}
11038 
11039 	EndCombo();
11040 	return value_changed;
11041 }
11042 
Items_ArrayGetter(void * data,int idx,const char ** out_text)11043 static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
11044 {
11045 	const char* const* items = (const char* const*)data;
11046 	if (out_text)
11047 		*out_text = items[idx];
11048 	return true;
11049 }
11050 
Items_SingleStringGetter(void * data,int idx,const char ** out_text)11051 static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
11052 {
11053 	// FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
11054 	const char* items_separated_by_zeros = (const char*)data;
11055 	int items_count = 0;
11056 	const char* p = items_separated_by_zeros;
11057 	while (*p)
11058 	{
11059 		if (idx == items_count)
11060 			break;
11061 		p += strlen(p) + 1;
11062 		items_count++;
11063 	}
11064 	if (!*p)
11065 		return false;
11066 	if (out_text)
11067 		*out_text = p;
11068 	return true;
11069 }
11070 
11071 // 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)11072 bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items)
11073 {
11074 	const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
11075 	return value_changed;
11076 }
11077 
11078 // 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)11079 bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
11080 {
11081 	int items_count = 0;
11082 	const char* p = items_separated_by_zeros;  // FIXME-OPT: Avoid computing this, or at least only when combo is open
11083 	while (*p)
11084 	{
11085 		p += strlen(p) + 1;
11086 		items_count++;
11087 	}
11088 	bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
11089 	return value_changed;
11090 }
11091 
11092 // Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image.
11093 // 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)11094 bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
11095 {
11096 	ImGuiWindow* window = GetCurrentWindow();
11097 	if (window->SkipItems)
11098 		return false;
11099 
11100 	ImGuiContext& g = *GImGui;
11101 	const ImGuiStyle& style = g.Style;
11102 
11103 	if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet)  // FIXME-OPT: Avoid if vertically clipped.
11104 		PopClipRect();
11105 
11106 	ImGuiID id = window->GetID(label);
11107 	ImVec2 label_size = CalcTextSize(label, NULL, true);
11108 	ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
11109 	ImVec2 pos = window->DC.CursorPos;
11110 	pos.y += window->DC.CurrentLineTextBaseOffset;
11111 	ImRect bb(pos, pos + size);
11112 	ItemSize(bb);
11113 
11114 	// Fill horizontal space.
11115 	ImVec2 window_padding = window->WindowPadding;
11116 	float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x;
11117 	float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x);
11118 	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);
11119 	ImRect bb_with_spacing(pos, pos + size_draw);
11120 	if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth))
11121 		bb_with_spacing.Max.x += window_padding.x;
11122 
11123 	// Selectables are tightly packed together, we extend the box to cover spacing between selectable.
11124 	float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f);
11125 	float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f);
11126 	float spacing_R = style.ItemSpacing.x - spacing_L;
11127 	float spacing_D = style.ItemSpacing.y - spacing_U;
11128 	bb_with_spacing.Min.x -= spacing_L;
11129 	bb_with_spacing.Min.y -= spacing_U;
11130 	bb_with_spacing.Max.x += spacing_R;
11131 	bb_with_spacing.Max.y += spacing_D;
11132 	if (!ItemAdd(bb_with_spacing, (flags & ImGuiSelectableFlags_Disabled) ? 0 : id))
11133 	{
11134 		if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet)
11135 			PushColumnClipRect();
11136 		return false;
11137 	}
11138 
11139 	ImGuiButtonFlags button_flags = 0;
11140 	if (flags & ImGuiSelectableFlags_Menu) button_flags |= ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_NoHoldingActiveID;
11141 	if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnRelease;
11142 	if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled;
11143 	if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
11144 	bool hovered, held;
11145 	bool pressed = ButtonBehavior(bb_with_spacing, id, &hovered, &held, button_flags);
11146 	if (flags & ImGuiSelectableFlags_Disabled)
11147 		selected = false;
11148 
11149 	// Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets)
11150 	if (pressed || hovered)  // && (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f))
11151 		if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerActiveMask)
11152 		{
11153 			g.NavDisableHighlight = true;
11154 			SetNavID(id, window->DC.NavLayerCurrent);
11155 		}
11156 
11157 	// Render
11158 	if (hovered || selected)
11159 	{
11160 		const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
11161 		RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, 0.0f);
11162 		RenderNavHighlight(bb_with_spacing, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
11163 	}
11164 
11165 	if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet)
11166 	{
11167 		PushColumnClipRect();
11168 		bb_with_spacing.Max.x -= (GetContentRegionMax().x - max_x);
11169 	}
11170 
11171 	if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
11172 	RenderTextClipped(bb.Min, bb_with_spacing.Max, label, NULL, &label_size, ImVec2(0.0f, 0.0f));
11173 	if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor();
11174 
11175 	// Automatically close popups
11176 	if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup))
11177 		CloseCurrentPopup();
11178 	return pressed;
11179 }
11180 
Selectable(const char * label,bool * p_selected,ImGuiSelectableFlags flags,const ImVec2 & size_arg)11181 bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
11182 {
11183 	if (Selectable(label, *p_selected, flags, size_arg))
11184 	{
11185 		*p_selected = !*p_selected;
11186 		return true;
11187 	}
11188 	return false;
11189 }
11190 
11191 // Helper to calculate the size of a listbox and display a label on the right.
11192 // 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)11193 bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
11194 {
11195 	ImGuiWindow* window = GetCurrentWindow();
11196 	if (window->SkipItems)
11197 		return false;
11198 
11199 	const ImGuiStyle& style = GetStyle();
11200 	const ImGuiID id = GetID(label);
11201 	const ImVec2 label_size = CalcTextSize(label, NULL, true);
11202 
11203 	// Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
11204 	ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y);
11205 	ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y));
11206 	ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
11207 	ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
11208 	window->DC.LastItemRect = bb;  // Forward storage for ListBoxFooter.. dodgy.
11209 
11210 	BeginGroup();
11211 	if (label_size.x > 0)
11212 		RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
11213 
11214 	BeginChildFrame(id, frame_bb.GetSize());
11215 	return true;
11216 }
11217 
ListBoxHeader(const char * label,int items_count,int height_in_items)11218 bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items)
11219 {
11220 	// Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
11221 	// 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.
11222 	// I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution.
11223 	if (height_in_items < 0)
11224 		height_in_items = ImMin(items_count, 7);
11225 	float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f);
11226 
11227 	// 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().
11228 	ImVec2 size;
11229 	size.x = 0.0f;
11230 	size.y = GetTextLineHeightWithSpacing() * height_in_items_f + GetStyle().ItemSpacing.y;
11231 	return ListBoxHeader(label, size);
11232 }
11233 
ListBoxFooter()11234 void ImGui::ListBoxFooter()
11235 {
11236 	ImGuiWindow* parent_window = GetCurrentWindow()->ParentWindow;
11237 	const ImRect bb = parent_window->DC.LastItemRect;
11238 	const ImGuiStyle& style = GetStyle();
11239 
11240 	EndChildFrame();
11241 
11242 	// Redeclare item size so that it includes the label (we have stored the full size in LastItemRect)
11243 	// We call SameLine() to restore DC.CurrentLine* data
11244 	SameLine();
11245 	parent_window->DC.CursorPos = bb.Min;
11246 	ItemSize(bb, style.FramePadding.y);
11247 	EndGroup();
11248 }
11249 
ListBox(const char * label,int * current_item,const char * const items[],int items_count,int height_items)11250 bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items)
11251 {
11252 	const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items);
11253 	return value_changed;
11254 }
11255 
ListBox(const char * label,int * current_item,bool (* items_getter)(void *,int,const char **),void * data,int items_count,int height_in_items)11256 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)
11257 {
11258 	if (!ListBoxHeader(label, items_count, height_in_items))
11259 		return false;
11260 
11261 	// 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.
11262 	bool value_changed = false;
11263 	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.
11264 	while (clipper.Step())
11265 		for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
11266 		{
11267 			const bool item_selected = (i == *current_item);
11268 			const char* item_text;
11269 			if (!items_getter(data, i, &item_text))
11270 				item_text = "*Unknown item*";
11271 
11272 			PushID(i);
11273 			if (Selectable(item_text, item_selected))
11274 			{
11275 				*current_item = i;
11276 				value_changed = true;
11277 			}
11278 			if (item_selected)
11279 				SetItemDefaultFocus();
11280 			PopID();
11281 		}
11282 	ListBoxFooter();
11283 	return value_changed;
11284 }
11285 
MenuItem(const char * label,const char * shortcut,bool selected,bool enabled)11286 bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)
11287 {
11288 	ImGuiWindow* window = GetCurrentWindow();
11289 	if (window->SkipItems)
11290 		return false;
11291 
11292 	ImGuiContext& g = *GImGui;
11293 	ImGuiStyle& style = g.Style;
11294 	ImVec2 pos = window->DC.CursorPos;
11295 	ImVec2 label_size = CalcTextSize(label, NULL, true);
11296 
11297 	ImGuiSelectableFlags flags = ImGuiSelectableFlags_MenuItem | (enabled ? 0 : ImGuiSelectableFlags_Disabled);
11298 	bool pressed;
11299 	if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
11300 	{
11301 		// Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
11302 		// Note that in this situation we render neither the shortcut neither the selected tick mark
11303 		float w = label_size.x;
11304 		window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
11305 		PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
11306 		pressed = Selectable(label, false, flags, ImVec2(w, 0.0f));
11307 		PopStyleVar();
11308 		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().
11309 	}
11310 	else
11311 	{
11312 		ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f);
11313 		float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f));  // Feedback for next frame
11314 		float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
11315 		pressed = Selectable(label, false, flags | ImGuiSelectableFlags_DrawFillAvailWidth, ImVec2(w, 0.0f));
11316 		if (shortcut_size.x > 0.0f)
11317 		{
11318 			PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
11319 			RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
11320 			PopStyleColor();
11321 		}
11322 		if (selected)
11323 			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);
11324 	}
11325 	return pressed;
11326 }
11327 
MenuItem(const char * label,const char * shortcut,bool * p_selected,bool enabled)11328 bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled)
11329 {
11330 	if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled))
11331 	{
11332 		if (p_selected)
11333 			*p_selected = !*p_selected;
11334 		return true;
11335 	}
11336 	return false;
11337 }
11338 
BeginMainMenuBar()11339 bool ImGui::BeginMainMenuBar()
11340 {
11341 	ImGuiContext& g = *GImGui;
11342 	SetNextWindowPos(ImVec2(0.0f, 0.0f));
11343 	SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.FontBaseSize + g.Style.FramePadding.y * 2.0f));
11344 	PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
11345 	PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0));
11346 	if (!Begin("##MainMenuBar", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar) || !BeginMenuBar())
11347 	{
11348 		End();
11349 		PopStyleVar(2);
11350 		return false;
11351 	}
11352 	g.CurrentWindow->DC.MenuBarOffsetX += g.Style.DisplaySafeAreaPadding.x;
11353 	return true;
11354 }
11355 
EndMainMenuBar()11356 void ImGui::EndMainMenuBar()
11357 {
11358 	EndMenuBar();
11359 
11360 	// When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window
11361 	ImGuiContext& g = *GImGui;
11362 	if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0)
11363 		FocusFrontMostActiveWindow(g.NavWindow);
11364 
11365 	End();
11366 	PopStyleVar(2);
11367 }
11368 
BeginMenuBar()11369 bool ImGui::BeginMenuBar()
11370 {
11371 	ImGuiWindow* window = GetCurrentWindow();
11372 	if (window->SkipItems)
11373 		return false;
11374 	if (!(window->Flags & ImGuiWindowFlags_MenuBar))
11375 		return false;
11376 
11377 	IM_ASSERT(!window->DC.MenuBarAppending);
11378 	BeginGroup();  // Save position
11379 	PushID("##menubar");
11380 
11381 	// 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.
11382 	// 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.
11383 	ImRect bar_rect = window->MenuBarRect();
11384 	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));
11385 	clip_rect.ClipWith(window->WindowRectClipped);
11386 	PushClipRect(clip_rect.Min, clip_rect.Max, false);
11387 
11388 	window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffsetX, bar_rect.Min.y);  // + g.Style.FramePadding.y);
11389 	window->DC.LayoutType = ImGuiLayoutType_Horizontal;
11390 	window->DC.NavLayerCurrent++;
11391 	window->DC.NavLayerCurrentMask <<= 1;
11392 	window->DC.MenuBarAppending = true;
11393 	AlignTextToFramePadding();
11394 	return true;
11395 }
11396 
EndMenuBar()11397 void ImGui::EndMenuBar()
11398 {
11399 	ImGuiWindow* window = GetCurrentWindow();
11400 	if (window->SkipItems)
11401 		return;
11402 	ImGuiContext& g = *GImGui;
11403 
11404 	// Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings.
11405 	if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
11406 	{
11407 		ImGuiWindow* nav_earliest_child = g.NavWindow;
11408 		while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu))
11409 			nav_earliest_child = nav_earliest_child->ParentWindow;
11410 		if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None)
11411 		{
11412 			// To do so we claim focus back, restore NavId and then process the movement request for yet another frame.
11413 			// 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)
11414 			IM_ASSERT(window->DC.NavLayerActiveMaskNext & 0x02);  // Sanity check
11415 			FocusWindow(window);
11416 			SetNavIDAndMoveMouse(window->NavLastIds[1], 1, window->NavRectRel[1]);
11417 			g.NavLayer = 1;
11418 			g.NavDisableHighlight = true;  // Hide highlight for the current frame so we don't see the intermediary selection.
11419 			g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
11420 			NavMoveRequestCancel();
11421 		}
11422 	}
11423 
11424 	IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);
11425 	IM_ASSERT(window->DC.MenuBarAppending);
11426 	PopClipRect();
11427 	PopID();
11428 	window->DC.MenuBarOffsetX = window->DC.CursorPos.x - window->MenuBarRect().Min.x;
11429 	window->DC.GroupStack.back().AdvanceCursor = false;
11430 	EndGroup();
11431 	window->DC.LayoutType = ImGuiLayoutType_Vertical;
11432 	window->DC.NavLayerCurrent--;
11433 	window->DC.NavLayerCurrentMask >>= 1;
11434 	window->DC.MenuBarAppending = false;
11435 }
11436 
BeginMenu(const char * label,bool enabled)11437 bool ImGui::BeginMenu(const char* label, bool enabled)
11438 {
11439 	ImGuiWindow* window = GetCurrentWindow();
11440 	if (window->SkipItems)
11441 		return false;
11442 
11443 	ImGuiContext& g = *GImGui;
11444 	const ImGuiStyle& style = g.Style;
11445 	const ImGuiID id = window->GetID(label);
11446 
11447 	ImVec2 label_size = CalcTextSize(label, NULL, true);
11448 
11449 	bool pressed;
11450 	bool menu_is_open = IsPopupOpen(id);
11451 	bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].OpenParentId == window->IDStack.back());
11452 	ImGuiWindow* backed_nav_window = g.NavWindow;
11453 	if (menuset_is_open)
11454 		g.NavWindow = window;  // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent)
11455 
11456 	// The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestPopupWindowPos).
11457 	ImVec2 popup_pos, pos = window->DC.CursorPos;
11458 	if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
11459 	{
11460 		// Menu inside an horizontal menu bar
11461 		// Selectable extend their highlight by half ItemSpacing in each direction.
11462 		// For ChildMenu, the popup position will be overwritten by the call to FindBestPopupWindowPos() in Begin()
11463 		popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight());
11464 		window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
11465 		PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
11466 		float w = label_size.x;
11467 		pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
11468 		PopStyleVar();
11469 		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().
11470 	}
11471 	else
11472 	{
11473 		// Menu inside a menu
11474 		popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
11475 		float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f));  // Feedback to next frame
11476 		float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
11477 		pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
11478 		if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
11479 		RenderTriangle(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), ImGuiDir_Right);
11480 		if (!enabled) PopStyleColor();
11481 	}
11482 
11483 	const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id);
11484 	if (menuset_is_open)
11485 		g.NavWindow = backed_nav_window;
11486 
11487 	bool want_open = false, want_close = false;
11488 	if (window->DC.LayoutType == ImGuiLayoutType_Vertical)  // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
11489 	{
11490 		// Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
11491 		bool moving_within_opened_triangle = false;
11492 		if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar))
11493 		{
11494 			if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window)
11495 			{
11496 				ImRect next_window_rect = next_window->Rect();
11497 				ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
11498 				ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
11499 				ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
11500 				float extra = ImClamp(fabsf(ta.x - tb.x) * 0.30f, 5.0f, 30.0f);  // add a bit of extra slack.
11501 				ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f;    // to avoid numerical issues
11502 				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?
11503 				tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
11504 				moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
11505 				//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
11506 			}
11507 		}
11508 
11509 		want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle);
11510 		want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed);
11511 
11512 		if (g.NavActivateId == id)
11513 		{
11514 			want_close = menu_is_open;
11515 			want_open = !menu_is_open;
11516 		}
11517 		if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right)  // Nav-Right to open
11518 		{
11519 			want_open = true;
11520 			NavMoveRequestCancel();
11521 		}
11522 	}
11523 	else
11524 	{
11525 		// Menu bar
11526 		if (menu_is_open && pressed && menuset_is_open)  // Click an open menu again to close it
11527 		{
11528 			want_close = true;
11529 			want_open = menu_is_open = false;
11530 		}
11531 		else if (pressed || (hovered && menuset_is_open && !menu_is_open))  // First click to open, then hover to open others
11532 		{
11533 			want_open = true;
11534 		}
11535 		else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down)  // Nav-Down to open
11536 		{
11537 			want_open = true;
11538 			NavMoveRequestCancel();
11539 		}
11540 	}
11541 
11542 	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.. }'
11543 		want_close = true;
11544 	if (want_close && IsPopupOpen(id))
11545 		ClosePopupToLevel(g.CurrentPopupStack.Size);
11546 
11547 	if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size)
11548 	{
11549 		// Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
11550 		OpenPopup(label);
11551 		return false;
11552 	}
11553 
11554 	menu_is_open |= want_open;
11555 	if (want_open)
11556 		OpenPopup(label);
11557 
11558 	if (menu_is_open)
11559 	{
11560 		SetNextWindowPos(popup_pos, ImGuiCond_Always);
11561 		ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ((window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu);
11562 		menu_is_open = BeginPopupEx(id, flags);  // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
11563 	}
11564 
11565 	return menu_is_open;
11566 }
11567 
EndMenu()11568 void ImGui::EndMenu()
11569 {
11570 	// Nav: When a left move request _within our child menu_ failed, close the menu.
11571 	// A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs.
11572 	// 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.
11573 	ImGuiContext& g = *GImGui;
11574 	ImGuiWindow* window = g.CurrentWindow;
11575 	if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical)
11576 	{
11577 		ClosePopupToLevel(g.OpenPopupStack.Size - 1);
11578 		NavMoveRequestCancel();
11579 	}
11580 
11581 	EndPopup();
11582 }
11583 
11584 // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
ColorTooltip(const char * text,const float * col,ImGuiColorEditFlags flags)11585 void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags)
11586 {
11587 	ImGuiContext& g = *GImGui;
11588 
11589 	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]);
11590 	BeginTooltipEx(0, true);
11591 
11592 	const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text;
11593 	if (text_end > text)
11594 	{
11595 		TextUnformatted(text, text_end);
11596 		Separator();
11597 	}
11598 
11599 	ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2);
11600 	ColorButton("##preview", ImVec4(col[0], col[1], col[2], col[3]), (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz);
11601 	SameLine();
11602 	if (flags & ImGuiColorEditFlags_NoAlpha)
11603 		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]);
11604 	else
11605 		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]);
11606 	EndTooltip();
11607 }
11608 
ImAlphaBlendColor(ImU32 col_a,ImU32 col_b)11609 static inline ImU32 ImAlphaBlendColor(ImU32 col_a, ImU32 col_b)
11610 {
11611 	float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
11612 	int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
11613 	int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
11614 	int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
11615 	return IM_COL32(r, g, b, 0xFF);
11616 }
11617 
11618 // 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.
11619 // 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)11620 void ImGui::RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, int rounding_corners_flags)
11621 {
11622 	ImGuiWindow* window = GetCurrentWindow();
11623 	if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF)
11624 	{
11625 		ImU32 col_bg1 = GetColorU32(ImAlphaBlendColor(IM_COL32(204, 204, 204, 255), col));
11626 		ImU32 col_bg2 = GetColorU32(ImAlphaBlendColor(IM_COL32(128, 128, 128, 255), col));
11627 		window->DrawList->AddRectFilled(p_min, p_max, col_bg1, rounding, rounding_corners_flags);
11628 
11629 		int yi = 0;
11630 		for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++)
11631 		{
11632 			float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y);
11633 			if (y2 <= y1)
11634 				continue;
11635 			for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f)
11636 			{
11637 				float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x);
11638 				if (x2 <= x1)
11639 					continue;
11640 				int rounding_corners_flags_cell = 0;
11641 				if (y1 <= p_min.y)
11642 				{
11643 					if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopLeft;
11644 					if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopRight;
11645 				}
11646 				if (y2 >= p_max.y)
11647 				{
11648 					if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotLeft;
11649 					if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotRight;
11650 				}
11651 				rounding_corners_flags_cell &= rounding_corners_flags;
11652 				window->DrawList->AddRectFilled(ImVec2(x1, y1), ImVec2(x2, y2), col_bg2, rounding_corners_flags_cell ? rounding : 0.0f, rounding_corners_flags_cell);
11653 			}
11654 		}
11655 	}
11656 	else
11657 	{
11658 		window->DrawList->AddRectFilled(p_min, p_max, col, rounding, rounding_corners_flags);
11659 	}
11660 }
11661 
SetColorEditOptions(ImGuiColorEditFlags flags)11662 void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags)
11663 {
11664 	ImGuiContext& g = *GImGui;
11665 	if ((flags & ImGuiColorEditFlags__InputsMask) == 0)
11666 		flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputsMask;
11667 	if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0)
11668 		flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask;
11669 	if ((flags & ImGuiColorEditFlags__PickerMask) == 0)
11670 		flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask;
11671 	IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__InputsMask)));    // Check only 1 option is selected
11672 	IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__DataTypeMask)));  // Check only 1 option is selected
11673 	IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask)));    // Check only 1 option is selected
11674 	g.ColorEditOptions = flags;
11675 }
11676 
11677 // A little colored square. Return true when clicked.
11678 // FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip.
11679 // '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)11680 bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size)
11681 {
11682 	ImGuiWindow* window = GetCurrentWindow();
11683 	if (window->SkipItems)
11684 		return false;
11685 
11686 	ImGuiContext& g = *GImGui;
11687 	const ImGuiID id = window->GetID(desc_id);
11688 	float default_size = GetFrameHeight();
11689 	if (size.x == 0.0f)
11690 		size.x = default_size;
11691 	if (size.y == 0.0f)
11692 		size.y = default_size;
11693 	const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
11694 	ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f);
11695 	if (!ItemAdd(bb, id))
11696 		return false;
11697 
11698 	bool hovered, held;
11699 	bool pressed = ButtonBehavior(bb, id, &hovered, &held);
11700 
11701 	if (flags & ImGuiColorEditFlags_NoAlpha)
11702 		flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf);
11703 
11704 	ImVec4 col_without_alpha(col.x, col.y, col.z, 1.0f);
11705 	float grid_step = ImMin(size.x, size.y) / 2.99f;
11706 	float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f);
11707 	ImRect bb_inner = bb;
11708 	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.
11709 	bb_inner.Expand(off);
11710 	if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col.w < 1.0f)
11711 	{
11712 		float mid_x = (float)(int)((bb_inner.Min.x + bb_inner.Max.x) * 0.5f + 0.5f);
11713 		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);
11714 		window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_without_alpha), rounding, ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft);
11715 	}
11716 	else
11717 	{
11718 		// Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha
11719 		ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col : col_without_alpha;
11720 		if (col_source.w < 1.0f)
11721 			RenderColorRectWithAlphaCheckerboard(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding);
11722 		else
11723 			window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding, ImDrawCornerFlags_All);
11724 	}
11725 	RenderNavHighlight(bb, id);
11726 	if (g.Style.FrameBorderSize > 0.0f)
11727 		RenderFrameBorder(bb.Min, bb.Max, rounding);
11728 	else
11729 		window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding);  // Color button are often in need of some sort of border
11730 
11731 	// Drag and Drop Source
11732 	if (g.ActiveId == id && BeginDragDropSource())  // NB: The ActiveId test is merely an optional micro-optimization
11733 	{
11734 		if (flags & ImGuiColorEditFlags_NoAlpha)
11735 			SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col, sizeof(float) * 3, ImGuiCond_Once);
11736 		else
11737 			SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col, sizeof(float) * 4, ImGuiCond_Once);
11738 		ColorButton(desc_id, col, flags);
11739 		SameLine();
11740 		TextUnformatted("Color");
11741 		EndDragDropSource();
11742 		hovered = false;
11743 	}
11744 
11745 	// Tooltip
11746 	if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered)
11747 		ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf));
11748 
11749 	return pressed;
11750 }
11751 
ColorEdit3(const char * label,float col[3],ImGuiColorEditFlags flags)11752 bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags)
11753 {
11754 	return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha);
11755 }
11756 
ColorEditOptionsPopup(const float * col,ImGuiColorEditFlags flags)11757 void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
11758 {
11759 	bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__InputsMask);
11760 	bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask);
11761 	if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context"))
11762 		return;
11763 	ImGuiContext& g = *GImGui;
11764 	ImGuiColorEditFlags opts = g.ColorEditOptions;
11765 	if (allow_opt_inputs)
11766 	{
11767 		if (RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB;
11768 		if (RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV;
11769 		if (RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX;
11770 	}
11771 	if (allow_opt_datatype)
11772 	{
11773 		if (allow_opt_inputs) Separator();
11774 		if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8;
11775 		if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float;
11776 	}
11777 
11778 	if (allow_opt_inputs || allow_opt_datatype)
11779 		Separator();
11780 	if (Button("Copy as..", ImVec2(-1, 0)))
11781 		OpenPopup("Copy");
11782 	if (BeginPopup("Copy"))
11783 	{
11784 		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]);
11785 		char buf[64];
11786 		ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
11787 		if (Selectable(buf))
11788 			SetClipboardText(buf);
11789 		ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca);
11790 		if (Selectable(buf))
11791 			SetClipboardText(buf);
11792 		if (flags & ImGuiColorEditFlags_NoAlpha)
11793 			ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X", cr, cg, cb);
11794 		else
11795 			ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X%02X", cr, cg, cb, ca);
11796 		if (Selectable(buf))
11797 			SetClipboardText(buf);
11798 		EndPopup();
11799 	}
11800 
11801 	g.ColorEditOptions = opts;
11802 	EndPopup();
11803 }
11804 
ColorPickerOptionsPopup(ImGuiColorEditFlags flags,const float * ref_col)11805 static void ColorPickerOptionsPopup(ImGuiColorEditFlags flags, const float* ref_col)
11806 {
11807 	bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask);
11808 	bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar);
11809 	if ((!allow_opt_picker && !allow_opt_alpha_bar) || !ImGui::BeginPopup("context"))
11810 		return;
11811 	ImGuiContext& g = *GImGui;
11812 	if (allow_opt_picker)
11813 	{
11814 		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
11815 		ImGui::PushItemWidth(picker_size.x);
11816 		for (int picker_type = 0; picker_type < 2; picker_type++)
11817 		{
11818 			// Draw small/thumbnail version of each picker type (over an invisible button for selection)
11819 			if (picker_type > 0) ImGui::Separator();
11820 			ImGui::PushID(picker_type);
11821 			ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoSidePreview | (flags & ImGuiColorEditFlags_NoAlpha);
11822 			if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar;
11823 			if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel;
11824 			ImVec2 backup_pos = ImGui::GetCursorScreenPos();
11825 			if (ImGui::Selectable("##selectable", false, 0, picker_size))  // By default, Selectable() is closing popup
11826 				g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask);
11827 			ImGui::SetCursorScreenPos(backup_pos);
11828 			ImVec4 dummy_ref_col;
11829 			memcpy(&dummy_ref_col.x, ref_col, sizeof(float) * (picker_flags & ImGuiColorEditFlags_NoAlpha ? 3 : 4));
11830 			ImGui::ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags);
11831 			ImGui::PopID();
11832 		}
11833 		ImGui::PopItemWidth();
11834 	}
11835 	if (allow_opt_alpha_bar)
11836 	{
11837 		if (allow_opt_picker) ImGui::Separator();
11838 		ImGui::CheckboxFlags("Alpha Bar", (unsigned int*)&g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar);
11839 	}
11840 	ImGui::EndPopup();
11841 }
11842 
11843 // Edit colors components (each component in 0.0f..1.0f range).
11844 // See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
11845 // 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)11846 bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags)
11847 {
11848 	ImGuiWindow* window = GetCurrentWindow();
11849 	if (window->SkipItems)
11850 		return false;
11851 
11852 	ImGuiContext& g = *GImGui;
11853 	const ImGuiStyle& style = g.Style;
11854 	const float square_sz = GetFrameHeight();
11855 	const float w_extra = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x);
11856 	const float w_items_all = CalcItemWidth() - w_extra;
11857 	const char* label_display_end = FindRenderedTextEnd(label);
11858 
11859 	const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0;
11860 	const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0;
11861 	const int components = alpha ? 4 : 3;
11862 	const ImGuiColorEditFlags flags_untouched = flags;
11863 
11864 	BeginGroup();
11865 	PushID(label);
11866 
11867 	// If we're not showing any slider there's no point in doing any HSV conversions
11868 	if (flags & ImGuiColorEditFlags_NoInputs)
11869 		flags = (flags & (~ImGuiColorEditFlags__InputsMask)) | ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_NoOptions;
11870 
11871 	// Context menu: display and modify options (before defaults are applied)
11872 	if (!(flags & ImGuiColorEditFlags_NoOptions))
11873 		ColorEditOptionsPopup(col, flags);
11874 
11875 	// Read stored options
11876 	if (!(flags & ImGuiColorEditFlags__InputsMask))
11877 		flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputsMask);
11878 	if (!(flags & ImGuiColorEditFlags__DataTypeMask))
11879 		flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask);
11880 	if (!(flags & ImGuiColorEditFlags__PickerMask))
11881 		flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask);
11882 	flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask));
11883 
11884 	// Convert to the formats we need
11885 	float f[4] = {col[0], col[1], col[2], alpha ? col[3] : 1.0f};
11886 	if (flags & ImGuiColorEditFlags_HSV)
11887 		ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
11888 	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])};
11889 
11890 	bool value_changed = false;
11891 	bool value_changed_as_float = false;
11892 
11893 	if ((flags & (ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_HSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
11894 	{
11895 		// RGB/HSV 0..255 Sliders
11896 		const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));
11897 		const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));
11898 
11899 		const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x);
11900 		const char* ids[4] = {"##X", "##Y", "##Z", "##W"};
11901 		const char* fmt_table_int[3][4] =
11902 			{
11903 				{"%3.0f", "%3.0f", "%3.0f", "%3.0f"},          // Short display
11904 				{"R:%3.0f", "G:%3.0f", "B:%3.0f", "A:%3.0f"},  // Long display for RGBA
11905 				{"H:%3.0f", "S:%3.0f", "V:%3.0f", "A:%3.0f"}   // Long display for HSVA
11906 			};
11907 		const char* fmt_table_float[3][4] =
11908 			{
11909 				{"%0.3f", "%0.3f", "%0.3f", "%0.3f"},          // Short display
11910 				{"R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f"},  // Long display for RGBA
11911 				{"H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f"}   // Long display for HSVA
11912 			};
11913 		const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_HSV) ? 2 : 1;
11914 
11915 		PushItemWidth(w_item_one);
11916 		for (int n = 0; n < components; n++)
11917 		{
11918 			if (n > 0)
11919 				SameLine(0, style.ItemInnerSpacing.x);
11920 			if (n + 1 == components)
11921 				PushItemWidth(w_item_last);
11922 			if (flags & ImGuiColorEditFlags_Float)
11923 				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]);
11924 			else
11925 				value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]);
11926 			if (!(flags & ImGuiColorEditFlags_NoOptions))
11927 				OpenPopupOnItemClick("context");
11928 		}
11929 		PopItemWidth();
11930 		PopItemWidth();
11931 	}
11932 	else if ((flags & ImGuiColorEditFlags_HEX) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
11933 	{
11934 		// RGB Hexadecimal Input
11935 		char buf[64];
11936 		if (alpha)
11937 			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));
11938 		else
11939 			ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255));
11940 		PushItemWidth(w_items_all);
11941 		if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase))
11942 		{
11943 			value_changed = true;
11944 			char* p = buf;
11945 			while (*p == '#' || ImCharIsSpace(*p))
11946 				p++;
11947 			i[0] = i[1] = i[2] = i[3] = 0;
11948 			if (alpha)
11949 				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)
11950 			else
11951 				sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
11952 		}
11953 		if (!(flags & ImGuiColorEditFlags_NoOptions))
11954 			OpenPopupOnItemClick("context");
11955 		PopItemWidth();
11956 	}
11957 
11958 	ImGuiWindow* picker_active_window = NULL;
11959 	if (!(flags & ImGuiColorEditFlags_NoSmallPreview))
11960 	{
11961 		if (!(flags & ImGuiColorEditFlags_NoInputs))
11962 			SameLine(0, style.ItemInnerSpacing.x);
11963 
11964 		const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f);
11965 		if (ColorButton("##ColorButton", col_v4, flags))
11966 		{
11967 			if (!(flags & ImGuiColorEditFlags_NoPicker))
11968 			{
11969 				// Store current color and open a picker
11970 				g.ColorPickerRef = col_v4;
11971 				OpenPopup("picker");
11972 				SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1, style.ItemSpacing.y));
11973 			}
11974 		}
11975 		if (!(flags & ImGuiColorEditFlags_NoOptions))
11976 			OpenPopupOnItemClick("context");
11977 
11978 		if (BeginPopup("picker"))
11979 		{
11980 			picker_active_window = g.CurrentWindow;
11981 			if (label != label_display_end)
11982 			{
11983 				TextUnformatted(label, label_display_end);
11984 				Separator();
11985 			}
11986 			ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar;
11987 			ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf;
11988 			PushItemWidth(square_sz * 12.0f);  // Use 256 + bar sizes?
11989 			value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x);
11990 			PopItemWidth();
11991 			EndPopup();
11992 		}
11993 	}
11994 
11995 	if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel))
11996 	{
11997 		SameLine(0, style.ItemInnerSpacing.x);
11998 		TextUnformatted(label, label_display_end);
11999 	}
12000 
12001 	// Convert back
12002 	if (picker_active_window == NULL)
12003 	{
12004 		if (!value_changed_as_float)
12005 			for (int n = 0; n < 4; n++)
12006 				f[n] = i[n] / 255.0f;
12007 		if (flags & ImGuiColorEditFlags_HSV)
12008 			ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
12009 		if (value_changed)
12010 		{
12011 			col[0] = f[0];
12012 			col[1] = f[1];
12013 			col[2] = f[2];
12014 			if (alpha)
12015 				col[3] = f[3];
12016 		}
12017 	}
12018 
12019 	PopID();
12020 	EndGroup();
12021 
12022 	// Drag and Drop Target
12023 	if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) && BeginDragDropTarget())  // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test.
12024 	{
12025 		if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
12026 		{
12027 			memcpy((float*)col, payload->Data, sizeof(float) * 3);
12028 			value_changed = true;
12029 		}
12030 		if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
12031 		{
12032 			memcpy((float*)col, payload->Data, sizeof(float) * components);
12033 			value_changed = true;
12034 		}
12035 		EndDragDropTarget();
12036 	}
12037 
12038 	// When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4().
12039 	if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window)
12040 		window->DC.LastItemId = g.ActiveId;
12041 
12042 	return value_changed;
12043 }
12044 
ColorPicker3(const char * label,float col[3],ImGuiColorEditFlags flags)12045 bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags)
12046 {
12047 	float col4[4] = {col[0], col[1], col[2], 1.0f};
12048 	if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha))
12049 		return false;
12050 	col[0] = col4[0];
12051 	col[1] = col4[1];
12052 	col[2] = col4[2];
12053 	return true;
12054 }
12055 
12056 // '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)12057 static void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col)
12058 {
12059 	switch (direction)
12060 	{
12061 		case ImGuiDir_Left:
12062 			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);
12063 			return;
12064 		case ImGuiDir_Right:
12065 			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);
12066 			return;
12067 		case ImGuiDir_Up:
12068 			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);
12069 			return;
12070 		case ImGuiDir_Down:
12071 			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);
12072 			return;
12073 		case ImGuiDir_None:
12074 		case ImGuiDir_Count_:
12075 			break;  // Fix warnings
12076 	}
12077 }
12078 
RenderArrowsForVerticalBar(ImDrawList * draw_list,ImVec2 pos,ImVec2 half_sz,float bar_w)12079 static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w)
12080 {
12081 	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);
12082 	RenderArrow(draw_list, ImVec2(pos.x + half_sz.x, pos.y), half_sz, ImGuiDir_Right, IM_COL32_WHITE);
12083 	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);
12084 	RenderArrow(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, ImGuiDir_Left, IM_COL32_WHITE);
12085 }
12086 
12087 // ColorPicker
12088 // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
12089 // 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)12090 bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col)
12091 {
12092 	ImGuiContext& g = *GImGui;
12093 	ImGuiWindow* window = GetCurrentWindow();
12094 	ImDrawList* draw_list = window->DrawList;
12095 
12096 	ImGuiStyle& style = g.Style;
12097 	ImGuiIO& io = g.IO;
12098 
12099 	PushID(label);
12100 	BeginGroup();
12101 
12102 	if (!(flags & ImGuiColorEditFlags_NoSidePreview))
12103 		flags |= ImGuiColorEditFlags_NoSmallPreview;
12104 
12105 	// Context menu: display and store options.
12106 	if (!(flags & ImGuiColorEditFlags_NoOptions))
12107 		ColorPickerOptionsPopup(flags, col);
12108 
12109 	// Read stored options
12110 	if (!(flags & ImGuiColorEditFlags__PickerMask))
12111 		flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask;
12112 	IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask)));  // Check that only 1 is selected
12113 	if (!(flags & ImGuiColorEditFlags_NoOptions))
12114 		flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar);
12115 
12116 	// Setup
12117 	int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4;
12118 	bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha);
12119 	ImVec2 picker_pos = window->DC.CursorPos;
12120 	float square_sz = GetFrameHeight();
12121 	float bars_width = square_sz;                                                                                                   // Arbitrary smallish width of Hue/Alpha picking bars
12122 	float sv_picker_size = ImMax(bars_width * 1, CalcItemWidth() - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x));  // Saturation/Value picking box
12123 	float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x;
12124 	float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x;
12125 	float bars_triangles_half_sz = (float)(int)(bars_width * 0.20f);
12126 
12127 	float backup_initial_col[4];
12128 	memcpy(backup_initial_col, col, components * sizeof(float));
12129 
12130 	float wheel_thickness = sv_picker_size * 0.08f;
12131 	float wheel_r_outer = sv_picker_size * 0.50f;
12132 	float wheel_r_inner = wheel_r_outer - wheel_thickness;
12133 	ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width) * 0.5f, picker_pos.y + sv_picker_size * 0.5f);
12134 
12135 	// Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic.
12136 	float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f);
12137 	ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f);                             // Hue point.
12138 	ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f);  // Black point.
12139 	ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f);  // White point.
12140 
12141 	float H, S, V;
12142 	ColorConvertRGBtoHSV(col[0], col[1], col[2], H, S, V);
12143 
12144 	bool value_changed = false, value_changed_h = false, value_changed_sv = false;
12145 
12146 	PushItemFlag(ImGuiItemFlags_NoNav, true);
12147 	if (flags & ImGuiColorEditFlags_PickerHueWheel)
12148 	{
12149 		// Hue wheel + SV triangle logic
12150 		InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size));
12151 		if (IsItemActive())
12152 		{
12153 			ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center;
12154 			ImVec2 current_off = g.IO.MousePos - wheel_center;
12155 			float initial_dist2 = ImLengthSqr(initial_off);
12156 			if (initial_dist2 >= (wheel_r_inner - 1) * (wheel_r_inner - 1) && initial_dist2 <= (wheel_r_outer + 1) * (wheel_r_outer + 1))
12157 			{
12158 				// Interactive with Hue wheel
12159 				H = atan2f(current_off.y, current_off.x) / IM_PI * 0.5f;
12160 				if (H < 0.0f)
12161 					H += 1.0f;
12162 				value_changed = value_changed_h = true;
12163 			}
12164 			float cos_hue_angle = cosf(-H * 2.0f * IM_PI);
12165 			float sin_hue_angle = sinf(-H * 2.0f * IM_PI);
12166 			if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle)))
12167 			{
12168 				// Interacting with SV triangle
12169 				ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle);
12170 				if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated))
12171 					current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated);
12172 				float uu, vv, ww;
12173 				ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww);
12174 				V = ImClamp(1.0f - vv, 0.0001f, 1.0f);
12175 				S = ImClamp(uu / V, 0.0001f, 1.0f);
12176 				value_changed = value_changed_sv = true;
12177 			}
12178 		}
12179 		if (!(flags & ImGuiColorEditFlags_NoOptions))
12180 			OpenPopupOnItemClick("context");
12181 	}
12182 	else if (flags & ImGuiColorEditFlags_PickerHueBar)
12183 	{
12184 		// SV rectangle logic
12185 		InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size));
12186 		if (IsItemActive())
12187 		{
12188 			S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1));
12189 			V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));
12190 			value_changed = value_changed_sv = true;
12191 		}
12192 		if (!(flags & ImGuiColorEditFlags_NoOptions))
12193 			OpenPopupOnItemClick("context");
12194 
12195 		// Hue bar logic
12196 		SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y));
12197 		InvisibleButton("hue", ImVec2(bars_width, sv_picker_size));
12198 		if (IsItemActive())
12199 		{
12200 			H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));
12201 			value_changed = value_changed_h = true;
12202 		}
12203 	}
12204 
12205 	// Alpha bar logic
12206 	if (alpha_bar)
12207 	{
12208 		SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y));
12209 		InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size));
12210 		if (IsItemActive())
12211 		{
12212 			col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));
12213 			value_changed = true;
12214 		}
12215 	}
12216 	PopItemFlag();  // ImGuiItemFlags_NoNav
12217 
12218 	if (!(flags & ImGuiColorEditFlags_NoSidePreview))
12219 	{
12220 		SameLine(0, style.ItemInnerSpacing.x);
12221 		BeginGroup();
12222 	}
12223 
12224 	if (!(flags & ImGuiColorEditFlags_NoLabel))
12225 	{
12226 		const char* label_display_end = FindRenderedTextEnd(label);
12227 		if (label != label_display_end)
12228 		{
12229 			if ((flags & ImGuiColorEditFlags_NoSidePreview))
12230 				SameLine(0, style.ItemInnerSpacing.x);
12231 			TextUnformatted(label, label_display_end);
12232 		}
12233 	}
12234 
12235 	if (!(flags & ImGuiColorEditFlags_NoSidePreview))
12236 	{
12237 		PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true);
12238 		ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
12239 		if ((flags & ImGuiColorEditFlags_NoLabel))
12240 			Text("Current");
12241 		ColorButton("##current", col_v4, (flags & (ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2));
12242 		if (ref_col != NULL)
12243 		{
12244 			Text("Original");
12245 			ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]);
12246 			if (ColorButton("##original", ref_col_v4, (flags & (ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2)))
12247 			{
12248 				memcpy(col, ref_col, components * sizeof(float));
12249 				value_changed = true;
12250 			}
12251 		}
12252 		PopItemFlag();
12253 		EndGroup();
12254 	}
12255 
12256 	// Convert back color to RGB
12257 	if (value_changed_h || value_changed_sv)
12258 		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]);
12259 
12260 	// R,G,B and H,S,V slider color editor
12261 	if ((flags & ImGuiColorEditFlags_NoInputs) == 0)
12262 	{
12263 		PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x);
12264 		ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf;
12265 		ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker;
12266 		if (flags & ImGuiColorEditFlags_RGB || (flags & ImGuiColorEditFlags__InputsMask) == 0)
12267 			value_changed |= ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_RGB);
12268 		if (flags & ImGuiColorEditFlags_HSV || (flags & ImGuiColorEditFlags__InputsMask) == 0)
12269 			value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_HSV);
12270 		if (flags & ImGuiColorEditFlags_HEX || (flags & ImGuiColorEditFlags__InputsMask) == 0)
12271 			value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_HEX);
12272 		PopItemWidth();
12273 	}
12274 
12275 	// Try to cancel hue wrap (after ColorEdit), if any
12276 	if (value_changed)
12277 	{
12278 		float new_H, new_S, new_V;
12279 		ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V);
12280 		if (new_H <= 0 && H > 0)
12281 		{
12282 			if (new_V <= 0 && V != new_V)
12283 				ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]);
12284 			else if (new_S <= 0)
12285 				ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]);
12286 		}
12287 	}
12288 
12289 	ImVec4 hue_color_f(1, 1, 1, 1);
12290 	ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z);
12291 	ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f);
12292 	ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 1.0f));
12293 
12294 	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)};
12295 	ImVec2 sv_cursor_pos;
12296 
12297 	if (flags & ImGuiColorEditFlags_PickerHueWheel)
12298 	{
12299 		// Render Hue Wheel
12300 		const float aeps = 1.5f / wheel_r_outer;  // Half a pixel arc length in radians (2pi cancels out).
12301 		const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12);
12302 		for (int n = 0; n < 6; n++)
12303 		{
12304 			const float a0 = (n) / 6.0f * 2.0f * IM_PI - aeps;
12305 			const float a1 = (n + 1.0f) / 6.0f * 2.0f * IM_PI + aeps;
12306 			const int vert_start_idx = draw_list->VtxBuffer.Size;
12307 			draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer) * 0.5f, a0, a1, segment_per_arc);
12308 			draw_list->PathStroke(IM_COL32_WHITE, false, wheel_thickness);
12309 			const int vert_end_idx = draw_list->VtxBuffer.Size;
12310 
12311 			// Paint colors over existing vertices
12312 			ImVec2 gradient_p0(wheel_center.x + cosf(a0) * wheel_r_inner, wheel_center.y + sinf(a0) * wheel_r_inner);
12313 			ImVec2 gradient_p1(wheel_center.x + cosf(a1) * wheel_r_inner, wheel_center.y + sinf(a1) * wheel_r_inner);
12314 			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]);
12315 		}
12316 
12317 		// Render Cursor + preview on Hue Wheel
12318 		float cos_hue_angle = cosf(H * 2.0f * IM_PI);
12319 		float sin_hue_angle = sinf(H * 2.0f * IM_PI);
12320 		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);
12321 		float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f;
12322 		int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32);
12323 		draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments);
12324 		draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad + 1, IM_COL32(128, 128, 128, 255), hue_cursor_segments);
12325 		draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, IM_COL32_WHITE, hue_cursor_segments);
12326 
12327 		// Render SV triangle (rotated according to hue)
12328 		ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle);
12329 		ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle);
12330 		ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle);
12331 		ImVec2 uv_white = GetFontTexUvWhitePixel();
12332 		draw_list->PrimReserve(6, 6);
12333 		draw_list->PrimVtx(tra, uv_white, hue_color32);
12334 		draw_list->PrimVtx(trb, uv_white, hue_color32);
12335 		draw_list->PrimVtx(trc, uv_white, IM_COL32_WHITE);
12336 		draw_list->PrimVtx(tra, uv_white, IM_COL32_BLACK_TRANS);
12337 		draw_list->PrimVtx(trb, uv_white, IM_COL32_BLACK);
12338 		draw_list->PrimVtx(trc, uv_white, IM_COL32_BLACK_TRANS);
12339 		draw_list->AddTriangle(tra, trb, trc, IM_COL32(128, 128, 128, 255), 1.5f);
12340 		sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V));
12341 	}
12342 	else if (flags & ImGuiColorEditFlags_PickerHueBar)
12343 	{
12344 		// Render SV Square
12345 		draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), IM_COL32_WHITE, hue_color32, hue_color32, IM_COL32_WHITE);
12346 		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);
12347 		RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0.0f);
12348 		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
12349 		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);
12350 
12351 		// Render Hue Bar
12352 		for (int i = 0; i < 6; ++i)
12353 			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]);
12354 		float bar0_line_y = (float)(int)(picker_pos.y + H * sv_picker_size + 0.5f);
12355 		RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f);
12356 		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);
12357 	}
12358 
12359 	// Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range)
12360 	float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f;
12361 	draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, col32_no_alpha, 12);
12362 	draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad + 1, IM_COL32(128, 128, 128, 255), 12);
12363 	draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, IM_COL32_WHITE, 12);
12364 
12365 	// Render alpha bar
12366 	if (alpha_bar)
12367 	{
12368 		float alpha = ImSaturate(col[3]);
12369 		ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size);
12370 		RenderColorRectWithAlphaCheckerboard(bar1_bb.Min, bar1_bb.Max, IM_COL32(0, 0, 0, 0), bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f));
12371 		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);
12372 		float bar1_line_y = (float)(int)(picker_pos.y + (1.0f - alpha) * sv_picker_size + 0.5f);
12373 		RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f);
12374 		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);
12375 	}
12376 
12377 	EndGroup();
12378 	PopID();
12379 
12380 	return value_changed && memcmp(backup_initial_col, col, components * sizeof(float));
12381 }
12382 
12383 // Horizontal separating line.
Separator()12384 void ImGui::Separator()
12385 {
12386 	ImGuiWindow* window = GetCurrentWindow();
12387 	if (window->SkipItems)
12388 		return;
12389 	ImGuiContext& g = *GImGui;
12390 
12391 	ImGuiWindowFlags flags = 0;
12392 	if ((flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)) == 0)
12393 		flags |= (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal;
12394 	IM_ASSERT(ImIsPowerOfTwo((int)(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical))));  // Check that only 1 option is selected
12395 	if (flags & ImGuiSeparatorFlags_Vertical)
12396 	{
12397 		VerticalSeparator();
12398 		return;
12399 	}
12400 
12401 	// Horizontal Separator
12402 	if (window->DC.ColumnsSet)
12403 		PopClipRect();
12404 
12405 	float x1 = window->Pos.x;
12406 	float x2 = window->Pos.x + window->Size.x;
12407 	if (!window->DC.GroupStack.empty())
12408 		x1 += window->DC.IndentX;
12409 
12410 	const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + 1.0f));
12411 	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.
12412 	if (!ItemAdd(bb, 0))
12413 	{
12414 		if (window->DC.ColumnsSet)
12415 			PushColumnClipRect();
12416 		return;
12417 	}
12418 
12419 	window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x, bb.Min.y), GetColorU32(ImGuiCol_Separator));
12420 
12421 	if (g.LogEnabled)
12422 		LogRenderedText(NULL, IM_NEWLINE "--------------------------------");
12423 
12424 	if (window->DC.ColumnsSet)
12425 	{
12426 		PushColumnClipRect();
12427 		window->DC.ColumnsSet->CellMinY = window->DC.CursorPos.y;
12428 	}
12429 }
12430 
VerticalSeparator()12431 void ImGui::VerticalSeparator()
12432 {
12433 	ImGuiWindow* window = GetCurrentWindow();
12434 	if (window->SkipItems)
12435 		return;
12436 	ImGuiContext& g = *GImGui;
12437 
12438 	float y1 = window->DC.CursorPos.y;
12439 	float y2 = window->DC.CursorPos.y + window->DC.CurrentLineHeight;
12440 	const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + 1.0f, y2));
12441 	ItemSize(ImVec2(bb.GetWidth(), 0.0f));
12442 	if (!ItemAdd(bb, 0))
12443 		return;
12444 
12445 	window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x, bb.Max.y), GetColorU32(ImGuiCol_Separator));
12446 	if (g.LogEnabled)
12447 		LogText(" |");
12448 }
12449 
SplitterBehavior(ImGuiID id,const ImRect & bb,ImGuiAxis axis,float * size1,float * size2,float min_size1,float min_size2,float hover_extend)12450 bool ImGui::SplitterBehavior(ImGuiID id, const ImRect& bb, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend)
12451 {
12452 	ImGuiContext& g = *GImGui;
12453 	ImGuiWindow* window = g.CurrentWindow;
12454 
12455 	const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
12456 	window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus;
12457 	bool item_add = ItemAdd(bb, id);
12458 	window->DC.ItemFlags = item_flags_backup;
12459 	if (!item_add)
12460 		return false;
12461 
12462 	bool hovered, held;
12463 	ImRect bb_interact = bb;
12464 	bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f));
12465 	ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap);
12466 	if (g.ActiveId != id)
12467 		SetItemAllowOverlap();
12468 
12469 	if (held || (g.HoveredId == id && g.HoveredIdPreviousFrame == id))
12470 		SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW);
12471 
12472 	ImRect bb_render = bb;
12473 	if (held)
12474 	{
12475 		ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min;
12476 		float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x;
12477 
12478 		// Minimum pane size
12479 		if (mouse_delta < min_size1 - *size1)
12480 			mouse_delta = min_size1 - *size1;
12481 		if (mouse_delta > *size2 - min_size2)
12482 			mouse_delta = *size2 - min_size2;
12483 
12484 		// Apply resize
12485 		*size1 += mouse_delta;
12486 		*size2 -= mouse_delta;
12487 		bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta));
12488 	}
12489 
12490 	// Render
12491 	const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
12492 	window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, g.Style.FrameRounding);
12493 
12494 	return held;
12495 }
12496 
Spacing()12497 void ImGui::Spacing()
12498 {
12499 	ImGuiWindow* window = GetCurrentWindow();
12500 	if (window->SkipItems)
12501 		return;
12502 	ItemSize(ImVec2(0, 0));
12503 }
12504 
Dummy(const ImVec2 & size)12505 void ImGui::Dummy(const ImVec2& size)
12506 {
12507 	ImGuiWindow* window = GetCurrentWindow();
12508 	if (window->SkipItems)
12509 		return;
12510 
12511 	const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
12512 	ItemSize(bb);
12513 	ItemAdd(bb, 0);
12514 }
12515 
IsRectVisible(const ImVec2 & size)12516 bool ImGui::IsRectVisible(const ImVec2& size)
12517 {
12518 	ImGuiWindow* window = GetCurrentWindowRead();
12519 	return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
12520 }
12521 
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)12522 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
12523 {
12524 	ImGuiWindow* window = GetCurrentWindowRead();
12525 	return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
12526 }
12527 
12528 // 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()12529 void ImGui::BeginGroup()
12530 {
12531 	ImGuiWindow* window = GetCurrentWindow();
12532 
12533 	window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
12534 	ImGuiGroupData& group_data = window->DC.GroupStack.back();
12535 	group_data.BackupCursorPos = window->DC.CursorPos;
12536 	group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
12537 	group_data.BackupIndentX = window->DC.IndentX;
12538 	group_data.BackupGroupOffsetX = window->DC.GroupOffsetX;
12539 	group_data.BackupCurrentLineHeight = window->DC.CurrentLineHeight;
12540 	group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
12541 	group_data.BackupLogLinePosY = window->DC.LogLinePosY;
12542 	group_data.BackupActiveIdIsAlive = GImGui->ActiveIdIsAlive;
12543 	group_data.AdvanceCursor = true;
12544 
12545 	window->DC.GroupOffsetX = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffsetX;
12546 	window->DC.IndentX = window->DC.GroupOffsetX;
12547 	window->DC.CursorMaxPos = window->DC.CursorPos;
12548 	window->DC.CurrentLineHeight = 0.0f;
12549 	window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
12550 }
12551 
EndGroup()12552 void ImGui::EndGroup()
12553 {
12554 	ImGuiContext& g = *GImGui;
12555 	ImGuiWindow* window = GetCurrentWindow();
12556 
12557 	IM_ASSERT(!window->DC.GroupStack.empty());  // Mismatched BeginGroup()/EndGroup() calls
12558 
12559 	ImGuiGroupData& group_data = window->DC.GroupStack.back();
12560 
12561 	ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
12562 	group_bb.Max = ImMax(group_bb.Min, group_bb.Max);
12563 
12564 	window->DC.CursorPos = group_data.BackupCursorPos;
12565 	window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
12566 	window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight;
12567 	window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
12568 	window->DC.IndentX = group_data.BackupIndentX;
12569 	window->DC.GroupOffsetX = group_data.BackupGroupOffsetX;
12570 	window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
12571 
12572 	if (group_data.AdvanceCursor)
12573 	{
12574 		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.
12575 		ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset);
12576 		ItemAdd(group_bb, 0);
12577 	}
12578 
12579 	// 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.
12580 	// 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.
12581 	const bool active_id_within_group = (!group_data.BackupActiveIdIsAlive && g.ActiveIdIsAlive && g.ActiveId && g.ActiveIdWindow->RootWindow == window->RootWindow);
12582 	if (active_id_within_group)
12583 		window->DC.LastItemId = g.ActiveId;
12584 	window->DC.LastItemRect = group_bb;
12585 
12586 	window->DC.GroupStack.pop_back();
12587 
12588 	//window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
12589 }
12590 
12591 // Gets back to previous line and continue with horizontal layout
12592 //      pos_x == 0      : follow right after previous item
12593 //      pos_x != 0      : align to specified x position (relative to window/group left)
12594 //      spacing_w < 0   : use default spacing if pos_x == 0, no spacing if pos_x != 0
12595 //      spacing_w >= 0  : enforce spacing amount
SameLine(float pos_x,float spacing_w)12596 void ImGui::SameLine(float pos_x, float spacing_w)
12597 {
12598 	ImGuiWindow* window = GetCurrentWindow();
12599 	if (window->SkipItems)
12600 		return;
12601 
12602 	ImGuiContext& g = *GImGui;
12603 	if (pos_x != 0.0f)
12604 	{
12605 		if (spacing_w < 0.0f) spacing_w = 0.0f;
12606 		window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffsetX + window->DC.ColumnsOffsetX;
12607 		window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
12608 	}
12609 	else
12610 	{
12611 		if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
12612 		window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
12613 		window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
12614 	}
12615 	window->DC.CurrentLineHeight = window->DC.PrevLineHeight;
12616 	window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
12617 }
12618 
NewLine()12619 void ImGui::NewLine()
12620 {
12621 	ImGuiWindow* window = GetCurrentWindow();
12622 	if (window->SkipItems)
12623 		return;
12624 
12625 	ImGuiContext& g = *GImGui;
12626 	const ImGuiLayoutType backup_layout_type = window->DC.LayoutType;
12627 	window->DC.LayoutType = ImGuiLayoutType_Vertical;
12628 	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.
12629 		ItemSize(ImVec2(0, 0));
12630 	else
12631 		ItemSize(ImVec2(0.0f, g.FontSize));
12632 	window->DC.LayoutType = backup_layout_type;
12633 }
12634 
NextColumn()12635 void ImGui::NextColumn()
12636 {
12637 	ImGuiWindow* window = GetCurrentWindow();
12638 	if (window->SkipItems || window->DC.ColumnsSet == NULL)
12639 		return;
12640 
12641 	ImGuiContext& g = *GImGui;
12642 	PopItemWidth();
12643 	PopClipRect();
12644 
12645 	ImGuiColumnsSet* columns = window->DC.ColumnsSet;
12646 	columns->CellMaxY = ImMax(columns->CellMaxY, window->DC.CursorPos.y);
12647 	if (++columns->Current < columns->Count)
12648 	{
12649 		// Columns 1+ cancel out IndentX
12650 		window->DC.ColumnsOffsetX = GetColumnOffset(columns->Current) - window->DC.IndentX + g.Style.ItemSpacing.x;
12651 		window->DrawList->ChannelsSetCurrent(columns->Current);
12652 	}
12653 	else
12654 	{
12655 		window->DC.ColumnsOffsetX = 0.0f;
12656 		window->DrawList->ChannelsSetCurrent(0);
12657 		columns->Current = 0;
12658 		columns->CellMinY = columns->CellMaxY;
12659 	}
12660 	window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
12661 	window->DC.CursorPos.y = columns->CellMinY;
12662 	window->DC.CurrentLineHeight = 0.0f;
12663 	window->DC.CurrentLineTextBaseOffset = 0.0f;
12664 
12665 	PushColumnClipRect();
12666 	PushItemWidth(GetColumnWidth() * 0.65f);  // FIXME: Move on columns setup
12667 }
12668 
GetColumnIndex()12669 int ImGui::GetColumnIndex()
12670 {
12671 	ImGuiWindow* window = GetCurrentWindowRead();
12672 	return window->DC.ColumnsSet ? window->DC.ColumnsSet->Current : 0;
12673 }
12674 
GetColumnsCount()12675 int ImGui::GetColumnsCount()
12676 {
12677 	ImGuiWindow* window = GetCurrentWindowRead();
12678 	return window->DC.ColumnsSet ? window->DC.ColumnsSet->Count : 1;
12679 }
12680 
OffsetNormToPixels(const ImGuiColumnsSet * columns,float offset_norm)12681 static float OffsetNormToPixels(const ImGuiColumnsSet* columns, float offset_norm)
12682 {
12683 	return offset_norm * (columns->MaxX - columns->MinX);
12684 }
12685 
PixelsToOffsetNorm(const ImGuiColumnsSet * columns,float offset)12686 static float PixelsToOffsetNorm(const ImGuiColumnsSet* columns, float offset)
12687 {
12688 	return offset / (columns->MaxX - columns->MinX);
12689 }
12690 
GetColumnsRectHalfWidth()12691 static inline float GetColumnsRectHalfWidth() { return 4.0f; }
12692 
GetDraggedColumnOffset(ImGuiColumnsSet * columns,int column_index)12693 static float GetDraggedColumnOffset(ImGuiColumnsSet* columns, int column_index)
12694 {
12695 	// Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
12696 	// window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
12697 	ImGuiContext& g = *GImGui;
12698 	ImGuiWindow* window = g.CurrentWindow;
12699 	IM_ASSERT(column_index > 0);  // We cannot drag column 0. If you get this assert you may have a conflict between the ID of your columns and another widgets.
12700 	IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));
12701 
12702 	float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + GetColumnsRectHalfWidth() - window->Pos.x;
12703 	x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing);
12704 	if ((columns->Flags & ImGuiColumnsFlags_NoPreserveWidths))
12705 		x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);
12706 
12707 	return x;
12708 }
12709 
GetColumnOffset(int column_index)12710 float ImGui::GetColumnOffset(int column_index)
12711 {
12712 	ImGuiWindow* window = GetCurrentWindowRead();
12713 	ImGuiColumnsSet* columns = window->DC.ColumnsSet;
12714 	IM_ASSERT(columns != NULL);
12715 
12716 	if (column_index < 0)
12717 		column_index = columns->Current;
12718 	IM_ASSERT(column_index < columns->Columns.Size);
12719 
12720 	/*
12721     if (g.ActiveId)
12722     {
12723         ImGuiContext& g = *GImGui;
12724         const ImGuiID column_id = columns->ColumnsSetId + ImGuiID(column_index);
12725         if (g.ActiveId == column_id)
12726             return GetDraggedColumnOffset(columns, column_index);
12727     }
12728     */
12729 
12730 	const float t = columns->Columns[column_index].OffsetNorm;
12731 	const float x_offset = ImLerp(columns->MinX, columns->MaxX, t);
12732 	return x_offset;
12733 }
12734 
GetColumnWidthEx(ImGuiColumnsSet * columns,int column_index,bool before_resize=false)12735 static float GetColumnWidthEx(ImGuiColumnsSet* columns, int column_index, bool before_resize = false)
12736 {
12737 	if (column_index < 0)
12738 		column_index = columns->Current;
12739 
12740 	float offset_norm;
12741 	if (before_resize)
12742 		offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize;
12743 	else
12744 		offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm;
12745 	return OffsetNormToPixels(columns, offset_norm);
12746 }
12747 
GetColumnWidth(int column_index)12748 float ImGui::GetColumnWidth(int column_index)
12749 {
12750 	ImGuiWindow* window = GetCurrentWindowRead();
12751 	ImGuiColumnsSet* columns = window->DC.ColumnsSet;
12752 	IM_ASSERT(columns != NULL);
12753 
12754 	if (column_index < 0)
12755 		column_index = columns->Current;
12756 	return OffsetNormToPixels(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm);
12757 }
12758 
SetColumnOffset(int column_index,float offset)12759 void ImGui::SetColumnOffset(int column_index, float offset)
12760 {
12761 	ImGuiContext& g = *GImGui;
12762 	ImGuiWindow* window = g.CurrentWindow;
12763 	ImGuiColumnsSet* columns = window->DC.ColumnsSet;
12764 	IM_ASSERT(columns != NULL);
12765 
12766 	if (column_index < 0)
12767 		column_index = columns->Current;
12768 	IM_ASSERT(column_index < columns->Columns.Size);
12769 
12770 	const bool preserve_width = !(columns->Flags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < columns->Count - 1);
12771 	const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f;
12772 
12773 	if (!(columns->Flags & ImGuiColumnsFlags_NoForceWithinWindow))
12774 		offset = ImMin(offset, columns->MaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index));
12775 	columns->Columns[column_index].OffsetNorm = PixelsToOffsetNorm(columns, offset - columns->MinX);
12776 
12777 	if (preserve_width)
12778 		SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width));
12779 }
12780 
SetColumnWidth(int column_index,float width)12781 void ImGui::SetColumnWidth(int column_index, float width)
12782 {
12783 	ImGuiWindow* window = GetCurrentWindowRead();
12784 	ImGuiColumnsSet* columns = window->DC.ColumnsSet;
12785 	IM_ASSERT(columns != NULL);
12786 
12787 	if (column_index < 0)
12788 		column_index = columns->Current;
12789 	SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width);
12790 }
12791 
PushColumnClipRect(int column_index)12792 void ImGui::PushColumnClipRect(int column_index)
12793 {
12794 	ImGuiWindow* window = GetCurrentWindowRead();
12795 	ImGuiColumnsSet* columns = window->DC.ColumnsSet;
12796 	if (column_index < 0)
12797 		column_index = columns->Current;
12798 
12799 	PushClipRect(columns->Columns[column_index].ClipRect.Min, columns->Columns[column_index].ClipRect.Max, false);
12800 }
12801 
FindOrAddColumnsSet(ImGuiWindow * window,ImGuiID id)12802 static ImGuiColumnsSet* FindOrAddColumnsSet(ImGuiWindow* window, ImGuiID id)
12803 {
12804 	for (int n = 0; n < window->ColumnsStorage.Size; n++)
12805 		if (window->ColumnsStorage[n].ID == id)
12806 			return &window->ColumnsStorage[n];
12807 
12808 	window->ColumnsStorage.push_back(ImGuiColumnsSet());
12809 	ImGuiColumnsSet* columns = &window->ColumnsStorage.back();
12810 	columns->ID = id;
12811 	return columns;
12812 }
12813 
BeginColumns(const char * str_id,int columns_count,ImGuiColumnsFlags flags)12814 void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlags flags)
12815 {
12816 	ImGuiContext& g = *GImGui;
12817 	ImGuiWindow* window = GetCurrentWindow();
12818 
12819 	IM_ASSERT(columns_count > 1);
12820 	IM_ASSERT(window->DC.ColumnsSet == NULL);  // Nested columns are currently not supported
12821 
12822 	// Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
12823 	// In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
12824 	PushID(0x11223347 + (str_id ? 0 : columns_count));
12825 	ImGuiID id = window->GetID(str_id ? str_id : "columns");
12826 	PopID();
12827 
12828 	// Acquire storage for the columns set
12829 	ImGuiColumnsSet* columns = FindOrAddColumnsSet(window, id);
12830 	IM_ASSERT(columns->ID == id);
12831 	columns->Current = 0;
12832 	columns->Count = columns_count;
12833 	columns->Flags = flags;
12834 	window->DC.ColumnsSet = columns;
12835 
12836 	// Set state for first column
12837 	const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? (window->SizeContentsExplicit.x) : (window->Size.x - window->ScrollbarSizes.x);
12838 	columns->MinX = window->DC.IndentX - g.Style.ItemSpacing.x;  // Lock our horizontal range
12839 	//column->MaxX = content_region_width - window->Scroll.x - ((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x;
12840 	columns->MaxX = content_region_width - window->Scroll.x;
12841 	columns->StartPosY = window->DC.CursorPos.y;
12842 	columns->StartMaxPosX = window->DC.CursorMaxPos.x;
12843 	columns->CellMinY = columns->CellMaxY = window->DC.CursorPos.y;
12844 	window->DC.ColumnsOffsetX = 0.0f;
12845 	window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
12846 
12847 	// Clear data if columns count changed
12848 	if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1)
12849 		columns->Columns.resize(0);
12850 
12851 	// Initialize defaults
12852 	columns->IsFirstFrame = (columns->Columns.Size == 0);
12853 	if (columns->Columns.Size == 0)
12854 	{
12855 		columns->Columns.reserve(columns_count + 1);
12856 		for (int n = 0; n < columns_count + 1; n++)
12857 		{
12858 			ImGuiColumnData column;
12859 			column.OffsetNorm = n / (float)columns_count;
12860 			columns->Columns.push_back(column);
12861 		}
12862 	}
12863 
12864 	for (int n = 0; n < columns_count + 1; n++)
12865 	{
12866 		// Clamp position
12867 		ImGuiColumnData* column = &columns->Columns[n];
12868 		float t = column->OffsetNorm;
12869 		if (!(columns->Flags & ImGuiColumnsFlags_NoForceWithinWindow))
12870 			t = ImMin(t, PixelsToOffsetNorm(columns, (columns->MaxX - columns->MinX) - g.Style.ColumnsMinSpacing * (columns->Count - n)));
12871 		column->OffsetNorm = t;
12872 
12873 		if (n == columns_count)
12874 			continue;
12875 
12876 		// Compute clipping rectangle
12877 		float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n) - 1.0f);
12878 		float clip_x2 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n + 1) - 1.0f);
12879 		column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);
12880 		column->ClipRect.ClipWith(window->ClipRect);
12881 	}
12882 
12883 	window->DrawList->ChannelsSplit(columns->Count);
12884 	PushColumnClipRect();
12885 	PushItemWidth(GetColumnWidth() * 0.65f);
12886 }
12887 
EndColumns()12888 void ImGui::EndColumns()
12889 {
12890 	ImGuiContext& g = *GImGui;
12891 	ImGuiWindow* window = GetCurrentWindow();
12892 	ImGuiColumnsSet* columns = window->DC.ColumnsSet;
12893 	IM_ASSERT(columns != NULL);
12894 
12895 	PopItemWidth();
12896 	PopClipRect();
12897 	window->DrawList->ChannelsMerge();
12898 
12899 	columns->CellMaxY = ImMax(columns->CellMaxY, window->DC.CursorPos.y);
12900 	window->DC.CursorPos.y = columns->CellMaxY;
12901 	if (!(columns->Flags & ImGuiColumnsFlags_GrowParentContentsSize))
12902 		window->DC.CursorMaxPos.x = ImMax(columns->StartMaxPosX, columns->MaxX);  // Restore cursor max pos, as columns don't grow parent
12903 
12904 	// Draw columns borders and handle resize
12905 	bool is_being_resized = false;
12906 	if (!(columns->Flags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems)
12907 	{
12908 		const float y1 = columns->StartPosY;
12909 		const float y2 = window->DC.CursorPos.y;
12910 		int dragging_column = -1;
12911 		for (int n = 1; n < columns->Count; n++)
12912 		{
12913 			float x = window->Pos.x + GetColumnOffset(n);
12914 			const ImGuiID column_id = columns->ID + ImGuiID(n);
12915 			const float column_hw = GetColumnsRectHalfWidth();  // Half-width for interaction
12916 			const ImRect column_rect(ImVec2(x - column_hw, y1), ImVec2(x + column_hw, y2));
12917 			KeepAliveID(column_id);
12918 			if (IsClippedEx(column_rect, column_id, false))
12919 				continue;
12920 
12921 			bool hovered = false, held = false;
12922 			if (!(columns->Flags & ImGuiColumnsFlags_NoResize))
12923 			{
12924 				ButtonBehavior(column_rect, column_id, &hovered, &held);
12925 				if (hovered || held)
12926 					g.MouseCursor = ImGuiMouseCursor_ResizeEW;
12927 				if (held && !(columns->Columns[n].Flags & ImGuiColumnsFlags_NoResize))
12928 					dragging_column = n;
12929 			}
12930 
12931 			// Draw column (we clip the Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.)
12932 			const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
12933 			const float xi = (float)(int)x;
12934 			window->DrawList->AddLine(ImVec2(xi, ImMax(y1 + 1.0f, window->ClipRect.Min.y)), ImVec2(xi, ImMin(y2, window->ClipRect.Max.y)), col);
12935 		}
12936 
12937 		// Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
12938 		if (dragging_column != -1)
12939 		{
12940 			if (!columns->IsBeingResized)
12941 				for (int n = 0; n < columns->Count + 1; n++)
12942 					columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm;
12943 			columns->IsBeingResized = is_being_resized = true;
12944 			float x = GetDraggedColumnOffset(columns, dragging_column);
12945 			SetColumnOffset(dragging_column, x);
12946 		}
12947 	}
12948 	columns->IsBeingResized = is_being_resized;
12949 
12950 	window->DC.ColumnsSet = NULL;
12951 	window->DC.ColumnsOffsetX = 0.0f;
12952 	window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
12953 }
12954 
12955 // [2017/12: 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)12956 void ImGui::Columns(int columns_count, const char* id, bool border)
12957 {
12958 	ImGuiWindow* window = GetCurrentWindow();
12959 	IM_ASSERT(columns_count >= 1);
12960 	if (window->DC.ColumnsSet != NULL && window->DC.ColumnsSet->Count != columns_count)
12961 		EndColumns();
12962 
12963 	ImGuiColumnsFlags flags = (border ? 0 : ImGuiColumnsFlags_NoBorder);
12964 	//flags |= ImGuiColumnsFlags_NoPreserveWidths; // NB: Legacy behavior
12965 	if (columns_count != 1)
12966 		BeginColumns(id, columns_count, flags);
12967 }
12968 
Indent(float indent_w)12969 void ImGui::Indent(float indent_w)
12970 {
12971 	ImGuiContext& g = *GImGui;
12972 	ImGuiWindow* window = GetCurrentWindow();
12973 	window->DC.IndentX += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
12974 	window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
12975 }
12976 
Unindent(float indent_w)12977 void ImGui::Unindent(float indent_w)
12978 {
12979 	ImGuiContext& g = *GImGui;
12980 	ImGuiWindow* window = GetCurrentWindow();
12981 	window->DC.IndentX -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
12982 	window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
12983 }
12984 
TreePush(const char * str_id)12985 void ImGui::TreePush(const char* str_id)
12986 {
12987 	ImGuiWindow* window = GetCurrentWindow();
12988 	Indent();
12989 	window->DC.TreeDepth++;
12990 	PushID(str_id ? str_id : "#TreePush");
12991 }
12992 
TreePush(const void * ptr_id)12993 void ImGui::TreePush(const void* ptr_id)
12994 {
12995 	ImGuiWindow* window = GetCurrentWindow();
12996 	Indent();
12997 	window->DC.TreeDepth++;
12998 	PushID(ptr_id ? ptr_id : (const void*)"#TreePush");
12999 }
13000 
TreePushRawID(ImGuiID id)13001 void ImGui::TreePushRawID(ImGuiID id)
13002 {
13003 	ImGuiWindow* window = GetCurrentWindow();
13004 	Indent();
13005 	window->DC.TreeDepth++;
13006 	window->IDStack.push_back(id);
13007 }
13008 
TreePop()13009 void ImGui::TreePop()
13010 {
13011 	ImGuiContext& g = *GImGui;
13012 	ImGuiWindow* window = g.CurrentWindow;
13013 	Unindent();
13014 
13015 	window->DC.TreeDepth--;
13016 	if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())
13017 		if (g.NavIdIsAlive && (window->DC.TreeDepthMayCloseOnPop & (1 << window->DC.TreeDepth)))
13018 		{
13019 			SetNavID(window->IDStack.back(), g.NavLayer);
13020 			NavMoveRequestCancel();
13021 		}
13022 	window->DC.TreeDepthMayCloseOnPop &= (1 << window->DC.TreeDepth) - 1;
13023 
13024 	PopID();
13025 }
13026 
Value(const char * prefix,bool b)13027 void ImGui::Value(const char* prefix, bool b)
13028 {
13029 	Text("%s: %s", prefix, (b ? "true" : "false"));
13030 }
13031 
Value(const char * prefix,int v)13032 void ImGui::Value(const char* prefix, int v)
13033 {
13034 	Text("%s: %d", prefix, v);
13035 }
13036 
Value(const char * prefix,unsigned int v)13037 void ImGui::Value(const char* prefix, unsigned int v)
13038 {
13039 	Text("%s: %d", prefix, v);
13040 }
13041 
Value(const char * prefix,float v,const char * float_format)13042 void ImGui::Value(const char* prefix, float v, const char* float_format)
13043 {
13044 	if (float_format)
13045 	{
13046 		char fmt[64];
13047 		ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format);
13048 		Text(fmt, prefix, v);
13049 	}
13050 	else
13051 	{
13052 		Text("%s: %.3f", prefix, v);
13053 	}
13054 }
13055 
13056 //-----------------------------------------------------------------------------
13057 // DRAG AND DROP
13058 //-----------------------------------------------------------------------------
13059 
ClearDragDrop()13060 void ImGui::ClearDragDrop()
13061 {
13062 	ImGuiContext& g = *GImGui;
13063 	g.DragDropActive = false;
13064 	g.DragDropPayload.Clear();
13065 	g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
13066 	g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
13067 	g.DragDropAcceptFrameCount = -1;
13068 }
13069 
13070 // Call when current ID is active.
13071 // 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)13072 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
13073 {
13074 	ImGuiContext& g = *GImGui;
13075 	ImGuiWindow* window = g.CurrentWindow;
13076 
13077 	bool source_drag_active = false;
13078 	ImGuiID source_id = 0;
13079 	ImGuiID source_parent_id = 0;
13080 	int mouse_button = 0;
13081 	if (!(flags & ImGuiDragDropFlags_SourceExtern))
13082 	{
13083 		source_id = window->DC.LastItemId;
13084 		if (source_id != 0 && g.ActiveId != source_id)  // Early out for most common case
13085 			return false;
13086 		if (g.IO.MouseDown[mouse_button] == false)
13087 			return false;
13088 
13089 		if (source_id == 0)
13090 		{
13091 			// If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
13092 			// A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
13093 			if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
13094 			{
13095 				IM_ASSERT(0);
13096 				return false;
13097 			}
13098 
13099 			// Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
13100 			// We build a throwaway ID based on current ID stack + relative AABB of items in window.
13101 			// THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
13102 			// We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
13103 			bool is_hovered = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) != 0;
13104 			if (!is_hovered && (g.ActiveId == 0 || g.ActiveIdWindow != window))
13105 				return false;
13106 			source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
13107 			if (is_hovered)
13108 				SetHoveredID(source_id);
13109 			if (is_hovered && g.IO.MouseClicked[mouse_button])
13110 			{
13111 				SetActiveID(source_id, window);
13112 				FocusWindow(window);
13113 			}
13114 			if (g.ActiveId == source_id)  // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
13115 				g.ActiveIdAllowOverlap = is_hovered;
13116 		}
13117 		if (g.ActiveId != source_id)
13118 			return false;
13119 		source_parent_id = window->IDStack.back();
13120 		source_drag_active = IsMouseDragging(mouse_button);
13121 	}
13122 	else
13123 	{
13124 		window = NULL;
13125 		source_id = ImHash("#SourceExtern", 0);
13126 		source_drag_active = true;
13127 	}
13128 
13129 	if (source_drag_active)
13130 	{
13131 		if (!g.DragDropActive)
13132 		{
13133 			IM_ASSERT(source_id != 0);
13134 			ClearDragDrop();
13135 			ImGuiPayload& payload = g.DragDropPayload;
13136 			payload.SourceId = source_id;
13137 			payload.SourceParentId = source_parent_id;
13138 			g.DragDropActive = true;
13139 			g.DragDropSourceFlags = flags;
13140 			g.DragDropMouseButton = mouse_button;
13141 		}
13142 
13143 		if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
13144 		{
13145 			// FIXME-DRAG
13146 			//SetNextWindowPos(g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding);
13147 			//PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This is better but e.g ColorButton with checkboard has issue with transparent colors :(
13148 			SetNextWindowPos(g.IO.MousePos);
13149 			PushStyleColor(ImGuiCol_PopupBg, GetStyleColorVec4(ImGuiCol_PopupBg) * ImVec4(1.0f, 1.0f, 1.0f, 0.6f));
13150 			BeginTooltip();
13151 		}
13152 
13153 		if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
13154 			window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
13155 
13156 		return true;
13157 	}
13158 	return false;
13159 }
13160 
EndDragDropSource()13161 void ImGui::EndDragDropSource()
13162 {
13163 	ImGuiContext& g = *GImGui;
13164 	IM_ASSERT(g.DragDropActive);
13165 
13166 	if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
13167 	{
13168 		EndTooltip();
13169 		PopStyleColor();
13170 		//PopStyleVar();
13171 	}
13172 
13173 	// Discard the drag if have not called SetDragDropPayload()
13174 	if (g.DragDropPayload.DataFrameCount == -1)
13175 		ClearDragDrop();
13176 }
13177 
13178 // 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)13179 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
13180 {
13181 	ImGuiContext& g = *GImGui;
13182 	ImGuiPayload& payload = g.DragDropPayload;
13183 	if (cond == 0)
13184 		cond = ImGuiCond_Always;
13185 
13186 	IM_ASSERT(type != NULL);
13187 	IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 12 characters long");
13188 	IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
13189 	IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
13190 	IM_ASSERT(payload.SourceId != 0);  // Not called between BeginDragDropSource() and EndDragDropSource()
13191 
13192 	if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
13193 	{
13194 		// Copy payload
13195 		ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
13196 		g.DragDropPayloadBufHeap.resize(0);
13197 		if (data_size > sizeof(g.DragDropPayloadBufLocal))
13198 		{
13199 			// Store in heap
13200 			g.DragDropPayloadBufHeap.resize((int)data_size);
13201 			payload.Data = g.DragDropPayloadBufHeap.Data;
13202 			memcpy((void*)payload.Data, data, data_size);
13203 		}
13204 		else if (data_size > 0)
13205 		{
13206 			// Store locally
13207 			memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
13208 			payload.Data = g.DragDropPayloadBufLocal;
13209 			memcpy((void*)payload.Data, data, data_size);
13210 		}
13211 		else
13212 		{
13213 			payload.Data = NULL;
13214 		}
13215 		payload.DataSize = (int)data_size;
13216 	}
13217 	payload.DataFrameCount = g.FrameCount;
13218 
13219 	return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
13220 }
13221 
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)13222 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
13223 {
13224 	ImGuiContext& g = *GImGui;
13225 	if (!g.DragDropActive)
13226 		return false;
13227 
13228 	ImGuiWindow* window = g.CurrentWindow;
13229 	if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
13230 		return false;
13231 	IM_ASSERT(id != 0);
13232 	if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
13233 		return false;
13234 
13235 	g.DragDropTargetRect = bb;
13236 	g.DragDropTargetId = id;
13237 	return true;
13238 }
13239 
13240 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
13241 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
13242 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
13243 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()13244 bool ImGui::BeginDragDropTarget()
13245 {
13246 	ImGuiContext& g = *GImGui;
13247 	if (!g.DragDropActive)
13248 		return false;
13249 
13250 	ImGuiWindow* window = g.CurrentWindow;
13251 	if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
13252 		return false;
13253 	if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
13254 		return false;
13255 
13256 	const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
13257 	ImGuiID id = window->DC.LastItemId;
13258 	if (id == 0)
13259 		id = window->GetIDFromRectangle(display_rect);
13260 	if (g.DragDropPayload.SourceId == id)
13261 		return false;
13262 
13263 	g.DragDropTargetRect = display_rect;
13264 	g.DragDropTargetId = id;
13265 	return true;
13266 }
13267 
IsDragDropPayloadBeingAccepted()13268 bool ImGui::IsDragDropPayloadBeingAccepted()
13269 {
13270 	ImGuiContext& g = *GImGui;
13271 	return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
13272 }
13273 
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)13274 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
13275 {
13276 	ImGuiContext& g = *GImGui;
13277 	ImGuiWindow* window = g.CurrentWindow;
13278 	ImGuiPayload& payload = g.DragDropPayload;
13279 	IM_ASSERT(g.DragDropActive);              // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
13280 	IM_ASSERT(payload.DataFrameCount != -1);  // Forgot to call EndDragDropTarget() ?
13281 	if (type != NULL && !payload.IsDataType(type))
13282 		return NULL;
13283 
13284 	// Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
13285 	// NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
13286 	const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
13287 	ImRect r = g.DragDropTargetRect;
13288 	float r_surface = r.GetWidth() * r.GetHeight();
13289 	if (r_surface < g.DragDropAcceptIdCurrRectSurface)
13290 	{
13291 		g.DragDropAcceptIdCurr = g.DragDropTargetId;
13292 		g.DragDropAcceptIdCurrRectSurface = r_surface;
13293 	}
13294 
13295 	// Render default drop visuals
13296 	payload.Preview = was_accepted_previously;
13297 	flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect);  // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
13298 	if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
13299 	{
13300 		// FIXME-DRAG: Settle on a proper default visuals for drop target.
13301 		r.Expand(3.5f);
13302 		bool push_clip_rect = !window->ClipRect.Contains(r);
13303 		if (push_clip_rect) window->DrawList->PushClipRectFullScreen();
13304 		window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
13305 		if (push_clip_rect) window->DrawList->PopClipRect();
13306 	}
13307 
13308 	g.DragDropAcceptFrameCount = g.FrameCount;
13309 	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()
13310 	if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
13311 		return NULL;
13312 
13313 	return &payload;
13314 }
13315 
13316 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()13317 void ImGui::EndDragDropTarget()
13318 {
13319 	ImGuiContext& g = *GImGui;
13320 	(void)g;
13321 	IM_ASSERT(g.DragDropActive);
13322 }
13323 
13324 //-----------------------------------------------------------------------------
13325 // PLATFORM DEPENDENT HELPERS
13326 //-----------------------------------------------------------------------------
13327 
13328 #if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
13329 #undef WIN32_LEAN_AND_MEAN
13330 #define WIN32_LEAN_AND_MEAN
13331 #ifndef __MINGW32__
13332 #include <Windows.h>
13333 #else
13334 #include <windows.h>
13335 #endif
13336 #endif
13337 
13338 // Win32 API clipboard implementation
13339 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
13340 
13341 #ifdef _MSC_VER
13342 #pragma comment(lib, "user32")
13343 #endif
13344 
GetClipboardTextFn_DefaultImpl(void *)13345 static const char* GetClipboardTextFn_DefaultImpl(void*)
13346 {
13347 	static ImVector<char> buf_local;
13348 	buf_local.clear();
13349 	if (!OpenClipboard(NULL))
13350 		return NULL;
13351 	HANDLE wbuf_handle = GetClipboardData(CF_UNICODETEXT);
13352 	if (wbuf_handle == NULL)
13353 	{
13354 		CloseClipboard();
13355 		return NULL;
13356 	}
13357 	if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle))
13358 	{
13359 		int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
13360 		buf_local.resize(buf_len);
13361 		ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
13362 	}
13363 	GlobalUnlock(wbuf_handle);
13364 	CloseClipboard();
13365 	return buf_local.Data;
13366 }
13367 
SetClipboardTextFn_DefaultImpl(void *,const char * text)13368 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
13369 {
13370 	if (!OpenClipboard(NULL))
13371 		return;
13372 	const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
13373 	HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
13374 	if (wbuf_handle == NULL)
13375 	{
13376 		CloseClipboard();
13377 		return;
13378 	}
13379 	ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle);
13380 	ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
13381 	GlobalUnlock(wbuf_handle);
13382 	EmptyClipboard();
13383 	SetClipboardData(CF_UNICODETEXT, wbuf_handle);
13384 	CloseClipboard();
13385 }
13386 
13387 #else
13388 
13389 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
GetClipboardTextFn_DefaultImpl(void *)13390 static const char* GetClipboardTextFn_DefaultImpl(void*)
13391 {
13392 	ImGuiContext& g = *GImGui;
13393 	return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
13394 }
13395 
13396 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
SetClipboardTextFn_DefaultImpl(void *,const char * text)13397 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
13398 {
13399 	ImGuiContext& g = *GImGui;
13400 	g.PrivateClipboard.clear();
13401 	const char* text_end = text + strlen(text);
13402 	g.PrivateClipboard.resize((int)(text_end - text) + 1);
13403 	memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
13404 	g.PrivateClipboard[(int)(text_end - text)] = 0;
13405 }
13406 
13407 #endif
13408 
13409 // Win32 API IME support (for Asian languages, etc.)
13410 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
13411 
13412 #include <imm.h>
13413 #ifdef _MSC_VER
13414 #pragma comment(lib, "imm32")
13415 #endif
13416 
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)13417 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
13418 {
13419 	// Notify OS Input Method Editor of text input position
13420 	if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle)
13421 		if (HIMC himc = ImmGetContext(hwnd))
13422 		{
13423 			COMPOSITIONFORM cf;
13424 			cf.ptCurrentPos.x = x;
13425 			cf.ptCurrentPos.y = y;
13426 			cf.dwStyle = CFS_FORCE_POSITION;
13427 			ImmSetCompositionWindow(himc, &cf);
13428 		}
13429 }
13430 
13431 #else
13432 
ImeSetInputScreenPosFn_DefaultImpl(int,int)13433 static void ImeSetInputScreenPosFn_DefaultImpl(int, int)
13434 {
13435 }
13436 
13437 #endif
13438 
13439 //-----------------------------------------------------------------------------
13440 // HELP
13441 //-----------------------------------------------------------------------------
13442 
ShowMetricsWindow(bool * p_open)13443 void ImGui::ShowMetricsWindow(bool* p_open)
13444 {
13445 	if (ImGui::Begin("ImGui Metrics", p_open))
13446 	{
13447 		ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
13448 		ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
13449 		ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3);
13450 		ImGui::Text("%d allocations", (int)GImAllocatorActiveAllocationsCount);
13451 		static bool show_clip_rects = true;
13452 		ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_clip_rects);
13453 		ImGui::Separator();
13454 
13455 		struct Funcs
13456 		{
13457 			static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
13458 			{
13459 				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);
13460 				if (draw_list == ImGui::GetWindowDrawList())
13461 				{
13462 					ImGui::SameLine();
13463 					ImGui::TextColored(ImColor(255, 100, 100), "CURRENTLY APPENDING");  // Can't display stats for active draw list! (we don't have the data double-buffered)
13464 					if (node_open) ImGui::TreePop();
13465 					return;
13466 				}
13467 
13468 				ImDrawList* overlay_draw_list = ImGui::GetOverlayDrawList();  // Render additional visuals into the top-most draw list
13469 				if (window && ImGui::IsItemHovered())
13470 					overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
13471 				if (!node_open)
13472 					return;
13473 
13474 				int elem_offset = 0;
13475 				for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
13476 				{
13477 					if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
13478 						continue;
13479 					if (pcmd->UserCallback)
13480 					{
13481 						ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
13482 						continue;
13483 					}
13484 					ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
13485 					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);
13486 					if (show_clip_rects && ImGui::IsItemHovered())
13487 					{
13488 						ImRect clip_rect = pcmd->ClipRect;
13489 						ImRect vtxs_rect;
13490 						for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
13491 							vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
13492 						clip_rect.Floor();
13493 						overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255, 255, 0, 255));
13494 						vtxs_rect.Floor();
13495 						overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255, 0, 255, 255));
13496 					}
13497 					if (!pcmd_node_open)
13498 						continue;
13499 
13500 					// Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
13501 					ImGuiListClipper clipper(pcmd->ElemCount / 3);  // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
13502 					while (clipper.Step())
13503 						for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
13504 						{
13505 							char buf[300];
13506 							char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
13507 							ImVec2 triangles_pos[3];
13508 							for (int n = 0; n < 3; n++, vtx_i++)
13509 							{
13510 								ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i];
13511 								triangles_pos[n] = v.pos;
13512 								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);
13513 							}
13514 							ImGui::Selectable(buf, false);
13515 							if (ImGui::IsItemHovered())
13516 							{
13517 								ImDrawListFlags backup_flags = overlay_draw_list->Flags;
13518 								overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines;  // Disable AA on triangle outlines at is more readable for very large and thin triangles.
13519 								overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255, 255, 0, 255), true, 1.0f);
13520 								overlay_draw_list->Flags = backup_flags;
13521 							}
13522 						}
13523 					ImGui::TreePop();
13524 				}
13525 				ImGui::TreePop();
13526 			}
13527 
13528 			static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
13529 			{
13530 				if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
13531 					return;
13532 				for (int i = 0; i < windows.Size; i++)
13533 					Funcs::NodeWindow(windows[i], "Window");
13534 				ImGui::TreePop();
13535 			}
13536 
13537 			static void NodeWindow(ImGuiWindow* window, const char* label)
13538 			{
13539 				if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
13540 					return;
13541 				ImGuiWindowFlags flags = window->Flags;
13542 				NodeDrawList(window, window->DrawList, "DrawList");
13543 				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);
13544 				ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s..)", flags,
13545 								  (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
13546 								  (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "");
13547 				ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetScrollMaxX(window), window->Scroll.y, GetScrollMaxY(window));
13548 				ImGui::BulletText("Active: %d, WriteAccessed: %d", window->Active, window->WriteAccessed);
13549 				ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
13550 				ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
13551 				if (window->NavRectRel[0].IsFinite())
13552 					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);
13553 				else
13554 					ImGui::BulletText("NavRectRel[0]: <None>");
13555 				if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
13556 				if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
13557 				ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
13558 				ImGui::TreePop();
13559 			}
13560 		};
13561 
13562 		// Access private state, we are going to display the draw lists from last frame
13563 		ImGuiContext& g = *GImGui;
13564 		Funcs::NodeWindows(g.Windows, "Windows");
13565 		if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
13566 		{
13567 			for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
13568 				Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
13569 			ImGui::TreePop();
13570 		}
13571 		if (ImGui::TreeNode("Popups", "Open Popups Stack (%d)", g.OpenPopupStack.Size))
13572 		{
13573 			for (int i = 0; i < g.OpenPopupStack.Size; i++)
13574 			{
13575 				ImGuiWindow* window = g.OpenPopupStack[i].Window;
13576 				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" : "");
13577 			}
13578 			ImGui::TreePop();
13579 		}
13580 		if (ImGui::TreeNode("Internal state"))
13581 		{
13582 			const char* input_source_names[] = {"None", "Mouse", "Nav", "NavGamepad", "NavKeyboard"};
13583 			IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_Count_);
13584 			ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
13585 			ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
13586 			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
13587 			ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), ActiveIdSource: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, input_source_names[g.ActiveIdSource]);
13588 			ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
13589 			ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
13590 			ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
13591 			ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
13592 			ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
13593 			ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
13594 			ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
13595 			ImGui::TreePop();
13596 		}
13597 	}
13598 	ImGui::End();
13599 }
13600 
13601 //-----------------------------------------------------------------------------
13602 
13603 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
13604 // 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.
13605 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
13606 #include "imgui_user.inl"
13607 #endif
13608 
13609 //-----------------------------------------------------------------------------
13610