1 // dear imgui, v1.53 WIP
2 // (main code and documentation)
4 // See ImGui::ShowTestWindow() in imgui_demo.cpp for demo code.
5 // Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
6 // Get latest version at https://github.com/ocornut/imgui
7 // Releases change-log at https://github.com/ocornut/imgui/releases
8 // Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/1269
9 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
10 // This library is free but I need your support to sustain development and maintenance.
11 // If you work for a company, please consider financial support, e.g: https://www.patreon.com/imgui
18 - PROGRAMMER GUIDE (read me!)
20 - How to update to a newer version of Dear ImGui
21 - Getting started with integrating Dear ImGui in your code/engine
22 - API BREAKING CHANGES (read me when you update!)
24 - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
26 - What is ImTextureID and how do I display an image?
27 - I integrated Dear ImGui in my engine and the text or lines are blurry..
28 - I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
29 - How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on labels/IDs.
30 - How can I tell when Dear ImGui wants my mouse/keyboard inputs VS when I can pass them to my application?
31 - How can I load a different font than the default?
32 - How can I easily use icons in my application?
33 - How can I load multiple fonts?
34 - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
35 - How can I preserve my Dear ImGui context across reloading a DLL? (loss of the global/static variables)
36 - How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
44 - Easy to use to create code-driven and data-driven tools
45 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools
46 - Easy to hack and improve
47 - Minimize screen real-estate usage
48 - Minimize setup and maintenance
49 - Minimize state storage on user side
50 - Portable, minimize dependencies, run on target (consoles, phones, etc.)
51 - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window, opening a tree node
52 for the first time, etc. but a typical frame won't allocate anything)
54 Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
55 - Doesn't look fancy, doesn't animate
56 - Limited layout features, intricate layouts are typically crafted in code
62 - Double-click on title bar to collapse window.
63 - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
64 - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
65 - Click and drag on any empty space to move window.
66 - TAB/SHIFT+TAB to cycle through keyboard editable fields.
67 - CTRL+Click on a slider or drag box to input value as text.
68 - Use mouse wheel to scroll.
70 - Hold SHIFT or use mouse to select text.
71 - CTRL+Left/Right to word jump.
72 - CTRL+Shift+Left/Right to select words.
73 - CTRL+A our Double-Click to select all.
74 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
75 - CTRL+Z,CTRL+Y to undo/redo.
76 - ESCAPE to revert text to its original value.
77 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
78 - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
86 - Read the FAQ below this section!
87 - Your code creates the UI, if your code doesn't run the UI is gone! == very dynamic UI, no construction/destructions steps, less data retention
88 on your side, no state duplication, less sync, less bugs.
89 - Call and read ImGui::ShowTestWindow() for demo code demonstrating most features.
90 - You can learn about immediate-mode gui principles at http://www.johno.se/book/imgui.html or watch http://mollyrocket.com/861
92 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
94 - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
95 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
96 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed from the public API.
97 If you have a problem with a missing function/symbols, search for its name in the code, there will likely be a comment about it.
98 Please report any issue to the GitHub page!
99 - Try to keep your copy of dear imgui reasonably up to date.
101 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
103 - Add the Dear ImGui source files to your projects, using your preferred build system.
104 It is recommended you build the .cpp files as part of your project and not as a library.
105 - You can later customize the imconfig.h file to tweak some compilation time behavior, such as integrating imgui types with your own maths types.
106 - See examples/ folder for standalone sample applications.
107 - You may be able to grab and copy a ready made imgui_impl_*** file from the examples/.
108 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
110 - Init: retrieve the ImGuiIO structure with ImGui::GetIO() and fill the fields marked 'Settings': at minimum you need to set io.DisplaySize
111 (application resolution). Later on you will fill your keyboard mapping, clipboard handlers, and other advanced features but for a basic
112 integration you don't need to worry about it all.
113 - Init: call io.Fonts->GetTexDataAsRGBA32(...), it will build the font atlas texture, then load the texture pixels into graphics memory.
115 - In your main loop as early a possible, fill the IO fields marked 'Input' (e.g. mouse position, buttons, keyboard info, etc.)
116 - Call ImGui::NewFrame() to begin the frame
117 - You can use any ImGui function you want between NewFrame() and Render()
118 - Call ImGui::Render() as late as you can to end the frame and finalize render data. it will call your io.RenderDrawListFn handler.
119 (Even if you don't render, call Render() and ignore the callback, or call EndFrame() instead. Otherwhise some features will break)
120 - All rendering information are stored into command-lists until ImGui::Render() is called.
121 - Dear ImGui never touches or knows about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide.
122 - Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases
123 of your own application.
124 - Refer to the examples applications in the examples/ folder for instruction on how to setup your code.
125 - A minimal application skeleton may be:
128 ImGuiIO& io = ImGui::GetIO();
129 io.DisplaySize.x = 1920.0f;
130 io.DisplaySize.y = 1280.0f;
131 io.RenderDrawListsFn = MyRenderFunction; // Setup a render function, or set to NULL and call GetDrawData() after Render() to access render data.
132 // TODO: Fill others settings of the io structure later.
134 // Load texture atlas (there is a default font so you don't need to care about choosing a font yet)
135 unsigned char* pixels;
137 io.Fonts->GetTexDataAsRGBA32(pixels, &width, &height);
138 // TODO: At this points you've got the texture data and you need to upload that your your graphic system:
139 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA)
140 // TODO: Store your texture pointer/identifier (whatever your engine uses) in 'io.Fonts->TexID'. This will be passed back to your via the renderer.
141 io.Fonts->TexID = (void*)texture;
143 // Application main loop
146 // Setup low-level inputs (e.g. on Win32, GetKeyboardState(), or write to those fields from your Windows message loop handlers, etc.)
147 ImGuiIO& io = ImGui::GetIO();
148 io.DeltaTime = 1.0f/60.0f;
149 io.MousePos = mouse_pos;
150 io.MouseDown[0] = mouse_button_0;
151 io.MouseDown[1] = mouse_button_1;
153 // Call NewFrame(), after this point you can use ImGui::* functions anytime
156 // Most of your application code here
157 MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
158 MyGameRender(); // may use any ImGui functions as well!
160 // Render & swap video buffers
165 - A minimal render function skeleton may be:
167 void void MyRenderFunction(ImDrawData* draw_data)(ImDrawData* draw_data)
169 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
170 // TODO: Setup viewport, orthographic projection matrix
171 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
172 for (int n = 0; n < draw_data->CmdListsCount; n++)
174 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by ImGui
175 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by ImGui
176 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
178 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
179 if (pcmd->UserCallback)
181 pcmd->UserCallback(cmd_list, pcmd);
185 // The texture for the draw call is specified by pcmd->TextureId.
186 // The vast majority of draw calls with use the imgui texture atlas, which value you have set yourself during initialization.
187 MyEngineBindTexture(pcmd->TextureId);
189 // We are using scissoring to clip some objects. All low-level graphics API supports it.
190 // If your engine doesn't support scissoring yet, you will get some small glitches (some elements outside their bounds) which you can fix later.
191 MyEngineScissor((int)pcmd->ClipRect.x, (int)pcmd->ClipRect.y, (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y));
193 // Render 'pcmd->ElemCount/3' indexed triangles.
194 // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits if your engine doesn't support 16-bits indices.
195 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
197 idx_buffer += pcmd->ElemCount;
202 - The examples/ folders contains many functional implementation of the pseudo-code above.
203 - When calling NewFrame(), the 'io.WantCaptureMouse'/'io.WantCaptureKeyboard'/'io.WantTextInput' flags are updated.
204 They tell you if ImGui intends to use your inputs. So for example, if 'io.WantCaptureMouse' is set you would typically want to hide
205 mouse inputs from the rest of your application. Read the FAQ below for more information about those flags.
212 Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix.
213 Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code.
214 Also read releases logs https://github.com/ocornut/imgui/releases for more details.
216 - 2017/11/18 (1.53) - Style, Begin: removed ImGuiWindowFlags_ShowBorders window flag. Borders are now fully set up in the ImGuiStyle structure (see e.g. style.FrameBorderSize, style.WindowBorderSize). Use ImGui::ShowStyleEditor() to look them up.
217 Please note that the style system will keep evolving (hopefully stabilizing in Q1 2018), and so custom styles will probably subtly break over time. It is recommended you use the StyleColorsClassic(), StyleColorsDark(), StyleColorsLight() functions.
218 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
219 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
220 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
221 - 2017/11/02 (1.53) - marked IsRootWindowOrAnyChildHovered() as obsolete is favor of using IsWindowHovered(ImGuiHoveredFlags_FlattenChilds);
222 - 2017/10/24 (1.52) - renamed IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS to IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS for consistency.
223 - 2017/10/20 (1.52) - changed IsWindowHovered() default parameters behavior to return false if an item is active in another window (e.g. click-dragging item from another window to this window). You can use the newly introduced IsWindowHovered() flags to requests this specific behavior if you need it.
224 - 2017/10/20 (1.52) - marked IsItemHoveredRect()/IsMouseHoveringWindow() as obsolete, in favor of using the newly introduced flags for IsItemHovered() and IsWindowHovered(). See https://github.com/ocornut/imgui/issues/1382 for details.
225 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
226 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
227 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
228 - 2017/09/25 (1.52) - removed SetNextWindowPosCenter() because SetNextWindowPos() now has the optional pivot information to do the same and more. Kept redirection function (will obsolete).
229 - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your binding if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
230 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
231 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
232 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
233 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
234 - 2017/08/20 (1.51) - added PushStyleColor(ImGuiCol idx, ImU32 col) overload, which _might_ cause an "ambiguous call" compilation error if you are using ImColor() with implicit cast. Cast to ImU32 or ImVec4 explicily to fix.
235 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame.
236 - 2017/08/15 (1.51) - changed parameter order for BeginPopupContextWindow() from (const char*,int buttons,bool also_over_items) to (const char*,int buttons,bool also_over_items). Note that most calls relied on default parameters completely.
237 - 2017/08/13 (1.51) - renamed ImGuiCol_Columns*** to ImGuiCol_Separator***. Kept redirection enums (will obsolete).
238 - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete).
239 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
240 - 2017/08/08 (1.51) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to the various Color*() functions. The SetColorEditOptions() allows to initialize default but the user can still change them with right-click context menu.
241 - changed prototype of 'ColorEdit4(const char* label, float col[4], bool show_alpha = true)' to 'ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)', where passing flags = 0x01 is a safe no-op (hello dodgy backward compatibility!). - check and run the demo window, under "Color/Picker Widgets", to understand the various new options.
242 - changed prototype of rarely used 'ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)' to 'ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0,0))'
243 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
244 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
245 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
246 - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetId() and use it instead of passing string to BeginChild().
247 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
248 - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc.
249 - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully breakage should be minimal.
250 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
251 If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you.
252 However if your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
253 This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color.
254 ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
256 float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
257 return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a);
259 If this is confusing, pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color.
260 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
261 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
262 - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen).
263 - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDraw::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer.
264 - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref github issue #337).
265 - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337)
266 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
267 - 2016/03/02 (1.48) - InputText() completion/history/always callbacks: if you modify the text buffer manually (without using DeleteChars()/InsertChars() helper) you need to maintain the BufTextLen field. added an assert.
268 - 2016/01/23 (1.48) - fixed not honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. if you had manual pixel-perfect alignment in place it might affect you.
269 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
270 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
271 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
272 GetCursorPos()/SetCursorPos() functions now include the scrolled amount. It shouldn't affect the majority of users, but take note that SetCursorPosX(100.0f) puts you at +100 from the starting x position which may include scrolling, not at +100 from the window left side.
273 GetContentRegionMax()/GetWindowContentRegionMin()/GetWindowContentRegionMax() functions allow include the scrolled amount. Typically those were used in cases where no scrolling would happen so it may not be a problem, but watch out!
274 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
275 - 2015/08/05 (1.44) - split imgui.cpp into extra files: imgui_demo.cpp imgui_draw.cpp imgui_internal.h that you need to add to your project.
276 - 2015/07/18 (1.44) - fixed angles in ImDrawList::PathArcTo(), PathArcToFast() (introduced in 1.43) being off by an extra PI for no justifiable reason
277 - 2015/07/14 (1.43) - add new ImFontAtlas::AddFont() API. For the old AddFont***, moved the 'font_no' parameter of ImFontAtlas::AddFont** functions to the ImFontConfig structure.
278 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
279 - 2015/07/08 (1.43) - switched rendering data to use indexed rendering. this is saving a fair amount of CPU/GPU and enables us to get anti-aliasing for a marginal cost.
280 this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
281 - if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
282 - the signature of the io.RenderDrawListsFn handler has changed!
283 ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
285 ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
286 argument 'cmd_lists' -> 'draw_data->CmdLists'
287 argument 'cmd_lists_count' -> 'draw_data->CmdListsCount'
288 ImDrawList 'commands' -> 'CmdBuffer'
289 ImDrawList 'vtx_buffer' -> 'VtxBuffer'
290 ImDrawList n/a -> 'IdxBuffer' (new)
291 ImDrawCmd 'vtx_count' -> 'ElemCount'
292 ImDrawCmd 'clip_rect' -> 'ClipRect'
293 ImDrawCmd 'user_callback' -> 'UserCallback'
294 ImDrawCmd 'texture_id' -> 'TextureId'
295 - each ImDrawList now contains both a vertex buffer and an index buffer. For each command, render ElemCount/3 triangles using indices from the index buffer.
296 - if you REALLY cannot render indexed primitives, you can call the draw_data->DeIndexAllBuffers() method to de-index the buffers. This is slow and a waste of CPU/GPU. Prefer using indexed rendering!
297 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
298 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
299 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
300 - 2015/07/02 (1.42) - renamed GetScrollPosY() to GetScrollY(). Necessary to reduce confusion along with other scrolling functions, because positions (e.g. cursor position) are not equivalent to scrolling amount.
301 - 2015/06/14 (1.41) - changed ImageButton() default bg_col parameter from (0,0,0,1) (black) to (0,0,0,0) (transparent) - makes a difference when texture have transparence
302 - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry!
303 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
304 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
305 - 2015/05/27 (1.40) - removed the third 'repeat_if_held' parameter from Button() - sorry! it was rarely used and inconsistent. Use PushButtonRepeat(true) / PopButtonRepeat() to enable repeat on desired buttons.
306 - 2015/05/11 (1.40) - changed BeginPopup() API, takes a string identifier instead of a bool. ImGui needs to manage the open/closed state of popups. Call OpenPopup() to actually set the "open" state of a popup. BeginPopup() returns true if the popup is opened.
307 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
308 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
309 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
310 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
311 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
312 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
313 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
314 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
315 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
316 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
317 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
318 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
319 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
320 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
321 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
322 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
323 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
324 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
326 const void* png_data;
327 unsigned int png_size;
328 ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size);
331 unsigned char* pixels;
333 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
335 io.Fonts->TexID = (your_texture_identifier);
336 you now have much more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
337 it is now recommended that you sample the font texture with bilinear interpolation.
338 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
339 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
340 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
341 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
342 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
343 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
344 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
345 - 2014/10/02 (1.14) - renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL and imgui_user.cpp to imgui_user.inl (more IDE friendly)
346 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
347 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
348 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
349 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
350 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
351 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
359 FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
360 ======================================
363 A: - If you are experienced enough with Dear ImGui and with C/C++, look at the todo list and see how you want/can help!
364 - Become a Patron/donate! Convince your company to become a Patron or provide serious funding for development time! See http://www.patreon.com/imgui
366 Q: What is ImTextureID and how do I display an image?
367 A: ImTextureID is a void* used to pass renderer-agnostic texture references around until it hits your render function.
368 Dear ImGui knows nothing about what those bits represent, it just passes them around. It is up to you to decide what you want the void* to carry!
369 It could be an identifier to your OpenGL texture (cast GLuint to void*), a pointer to your custom engine material (cast MyMaterial* to void*), etc.
370 At the end of the chain, your renderer takes this void* to cast it back into whatever it needs to select a current texture to render.
371 Refer to examples applications, where each renderer (in a imgui_impl_xxxx.cpp file) is treating ImTextureID as a different thing.
372 (c++ tip: OpenGL uses integers to identify textures. You can safely store an integer into a void*, just cast it to void*, don't take it's address!)
373 To display a custom image/texture within an ImGui window, you may use ImGui::Image(), ImGui::ImageButton(), ImDrawList::AddImage() functions.
374 Dear ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use.
375 It is your responsibility to get textures uploaded to your GPU.
377 Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
378 A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
379 Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
381 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
382 A: You are probably mishandling the clipping rectangles in your render function.
383 Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
385 Q: Can I have multiple widgets with the same label? Can I have widget without a label?
386 A: Yes. A primer on the use of labels/IDs in Dear ImGui..
388 - Elements that are not clickable, such as Text() items don't need an ID.
390 - Interactive widgets require state to be carried over multiple frames (most typically Dear ImGui often needs to remember what is
391 the "active" widget). to do so they need a unique ID. unique ID are typically derived from a string label, an integer index or a pointer.
393 Button("OK"); // Label = "OK", ID = hash of "OK"
394 Button("Cancel"); // Label = "Cancel", ID = hash of "Cancel"
396 - ID are uniquely scoped within windows, tree nodes, etc. so no conflict can happen if you have two buttons called "OK"
397 in two different windows or in two different locations of a tree.
399 - If you have a same ID twice in the same location, you'll have a conflict:
402 Button("OK"); // ID collision! Both buttons will be treated as the same.
404 Fear not! this is easy to solve and there are many ways to solve it!
406 - When passing a label you can optionally specify extra unique ID information within string itself.
407 This helps solving the simpler collision cases. Use "##" to pass a complement to the ID that won't be visible to the end-user:
409 Button("Play"); // Label = "Play", ID = hash of "Play"
410 Button("Play##foo1"); // Label = "Play", ID = hash of "Play##foo1" (different from above)
411 Button("Play##foo2"); // Label = "Play", ID = hash of "Play##foo2" (different from above)
413 - If you want to completely hide the label, but still need an ID:
415 Checkbox("##On", &b); // Label = "", ID = hash of "##On" (no label!)
417 - Occasionally/rarely you might want change a label while preserving a constant ID. This allows you to animate labels.
418 For example you may want to include varying information in a window title bar (and windows are uniquely identified by their ID.. obviously)
419 Use "###" to pass a label that isn't part of ID:
421 Button("Hello###ID"; // Label = "Hello", ID = hash of "ID"
422 Button("World###ID"; // Label = "World", ID = hash of "ID" (same as above)
424 sprintf(buf, "My game (%f FPS)###MyGame");
425 Begin(buf); // Variable label, ID = hash of "MyGame"
427 - Use PushID() / PopID() to create scopes and avoid ID conflicts within the same Window.
428 This is the most convenient way of distinguishing ID if you are iterating and creating many UI elements.
429 You can push a pointer, a string or an integer value. Remember that ID are formed from the concatenation of everything in the ID stack!
431 for (int i = 0; i < 100; i++)
434 Button("Click"); // Label = "Click", ID = hash of integer + "label" (unique)
438 for (int i = 0; i < 100; i++)
440 MyObject* obj = Objects[i];
442 Button("Click"); // Label = "Click", ID = hash of pointer + "label" (unique)
446 for (int i = 0; i < 100; i++)
448 MyObject* obj = Objects[i];
450 Button("Click"); // Label = "Click", ID = hash of string + "label" (unique)
454 - More example showing that you can stack multiple prefixes into the ID stack:
456 Button("Click"); // Label = "Click", ID = hash of "Click"
458 Button("Click"); // Label = "Click", ID = hash of "node" + "Click"
460 Button("Click"); // Label = "Click", ID = hash of "node" + ptr + "Click"
464 - Tree nodes implicitly creates a scope for you by calling PushID().
466 Button("Click"); // Label = "Click", ID = hash of "Click"
467 if (TreeNode("node"))
469 Button("Click"); // Label = "Click", ID = hash of "node" + "Click"
473 - When working with trees, ID are used to preserve the open/close state of each tree node.
474 Depending on your use cases you may want to use strings, indices or pointers as ID.
475 e.g. when displaying a single object that may change over time (dynamic 1-1 relationship), using a static string as ID will preserve your
476 node open/closed state when the targeted object change.
477 e.g. when displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state differently.
478 experiment and see what makes more sense!
480 Q: How can I tell when Dear ImGui wants my mouse/keyboard inputs VS when I can pass them to my application?
481 A: You can read the 'io.WantCaptureMouse'/'io.WantCaptureKeyboard'/'ioWantTextInput' flags from the ImGuiIO structure.
482 - When 'io.WantCaptureMouse' or 'io.WantCaptureKeyboard' flags are set you may want to discard/hide the inputs from the rest of your application.
483 - When 'io.WantTextInput' is set to may want to notify your OS to popup an on-screen keyboard, if available (e.g. on a mobile phone, or console OS).
484 Preferably read the flags after calling ImGui::NewFrame() to avoid them lagging by one frame. But reading those flags before calling NewFrame() is
485 also generally ok, as the bool toggles fairly rarely and you don't generally expect to interact with either Dear ImGui or your application during
486 the same frame when that transition occurs. Dear ImGui is tracking dragging and widget activity that may occur outside the boundary of a window,
487 so 'io.WantCaptureMouse' is more accurate and correct than checking if a window is hovered.
488 (Advanced note: text input releases focus on Return 'KeyDown', so the following Return 'KeyUp' event that your application receive will typically
489 have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
490 were for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
492 Q: How can I load a different font than the default? (default is an embedded version of ProggyClean.ttf, rendered at size 13)
493 A: Use the font atlas to load the TTF/OTF file you want:
495 ImGuiIO& io = ImGui::GetIO();
496 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
497 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
499 Q: How can I easily use icons in my application?
500 A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you main font. Then you can refer to icons within your
501 strings. Read 'How can I load multiple fonts?' and the file 'extra_fonts/README.txt' for instructions and useful header files.
503 Q: How can I load multiple fonts?
504 A: Use the font atlas to pack them into a single texture:
505 (Read extra_fonts/README.txt and the code in ImFontAtlas for more details.)
507 ImGuiIO& io = ImGui::GetIO();
508 ImFont* font0 = io.Fonts->AddFontDefault();
509 ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
510 ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
511 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
512 // the first loaded font gets used by default
513 // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
517 config.OversampleH = 3;
518 config.OversampleV = 1;
519 config.GlyphOffset.y -= 2.0f; // Move everything by 2 pixels up
520 config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters
521 io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config);
523 // Combine multiple fonts into one (e.g. for icon fonts)
524 ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
526 config.MergeMode = true;
527 io.Fonts->AddFontDefault();
528 io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font
529 io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs
531 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
532 A: When loading a font, pass custom Unicode ranges to specify the glyphs to load.
534 // Add default Japanese ranges
535 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese());
537 // Or create your own custom ranges (e.g. for a game you can feed your entire game script and only build the characters the game need)
538 ImVector<ImWchar> ranges;
539 ImFontAtlas::GlyphRangesBuilder builder;
540 builder.AddText("Hello world"); // Add a string (here "Hello world" contains 7 unique characters)
541 builder.AddChar(0x7262); // Add a specific character
542 builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges
543 builder.BuildRanges(&ranges); // Build the final result (ordered ranges with all the unique characters submitted)
544 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data);
546 All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8 by using the u8"hello" syntax.
547 Specifying literal in your source code using a local code page (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work!
548 Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
550 Text input: it is up to your application to pass the right character code to io.AddInputCharacter(). The applications in examples/ are doing that.
551 For languages using IME, on Windows you can copy the Hwnd of your application to io.ImeWindowHandle.
552 The default implementation of io.ImeSetInputScreenPosFn() on Windows will set your IME position correctly.
554 Q: How can I preserve my Dear ImGui context across reloading a DLL? (loss of the global/static variables)
555 A: Create your own context 'ctx = CreateContext()' + 'SetCurrentContext(ctx)' and your own font atlas 'ctx->GetIO().Fonts = new ImFontAtlas()'
556 so you don't rely on the default globals.
558 Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
559 A: The easiest way is to create a dummy window. Call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flag,
560 zero background alpha, then retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
561 You can also perfectly create a standalone ImDrawList instance _but_ you need ImGui to be initialized because ImDrawList pulls from ImGui
562 data to retrieve the coordinates of the white pixel.
564 - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window.
565 this is also useful to set yourself in the context of another window (to get/set other settings)
566 - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug".
567 - tip: the ImGuiOnceUponAFrame helper will allow run the block of code only once a frame. You can use it to quickly add custom UI in the middle
568 of a deep nested inner loop in your code.
569 - tip: you can call Render() multiple times (e.g for VR renders).
570 - tip: call and read the ShowTestWindow() code in imgui_demo.cpp for more example of how to use ImGui!
574 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
575 #define _CRT_SECURE_NO_WARNINGS
579 #define IMGUI_DEFINE_MATH_OPERATORS
580 #define IMGUI_DEFINE_PLACEMENT_NEW
581 #include "imgui_internal.h"
583 #include <ctype.h> // toupper, isprint
584 #include <stdlib.h> // NULL, malloc, free, qsort, atoi
585 #include <stdio.h> // vsnprintf, sscanf, printf
586 #include <limits.h> // INT_MIN, INT_MAX
587 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
588 #include <stddef.h> // intptr_t
590 #include <stdint.h> // intptr_t
594 #pragma warning (disable: 4127) // condition expression is constant
595 #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
596 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
599 // Clang warnings with -Weverything
601 #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning : unknown warning group '-Wformat-pedantic *' // not all warnings are known by all clang versions.. so ignoring warnings triggers new warnings on some configuration. great!
602 #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
603 #pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
604 #pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
605 #pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.
606 #pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it.
607 #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
608 #pragma clang diagnostic ignored "-Wformat-pedantic" // warning : format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic.
609 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' //
610 #elif defined(__GNUC__)
611 #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
612 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
613 #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
614 #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
615 #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
616 #pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'xxxx' to type 'xxxx' casts away qualifiers
617 #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
620 //-------------------------------------------------------------------------
621 // Forward Declarations
622 //-------------------------------------------------------------------------
624 static float GetDraggedColumnOffset(int column_index);
626 static bool IsKeyPressedMap(ImGuiKey key, bool repeat = true);
628 static ImFont* GetDefaultFont();
629 static void SetCurrentFont(ImFont* font);
630 static void SetCurrentWindow(ImGuiWindow* window);
631 static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y);
632 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond);
633 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond);
634 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond);
635 static ImGuiWindow* FindHoveredWindow(ImVec2 pos);
636 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
637 static void ClearSetNextWindowData();
638 static void CheckStacksSize(ImGuiWindow* window, bool write);
639 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
641 static void AddDrawListToRenderList(ImVector<ImDrawList*>& out_render_list, ImDrawList* draw_list);
642 static void AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiWindow* window);
643 static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>& out_sorted_windows, ImGuiWindow* window);
645 static ImGuiIniData* FindWindowSettings(const char* name);
646 static ImGuiIniData* AddWindowSettings(const char* name);
647 static void LoadIniSettingsFromDisk(const char* ini_filename);
648 static void SaveIniSettingsToDisk(const char* ini_filename);
649 static void MarkIniSettingsDirty(ImGuiWindow* window);
651 static ImRect GetVisibleRect();
653 static void CloseInactivePopups(ImGuiWindow* ref_window);
654 static void ClosePopupToLevel(int remaining);
655 static ImGuiWindow* GetFrontMostModalRootWindow();
656 static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& rect_to_avoid);
658 static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data);
659 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
660 static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false);
662 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size);
663 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size);
664 static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2);
665 static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format);
669 static void FocusPreviousWindow();
672 //-----------------------------------------------------------------------------
673 // Platform dependent default implementations
674 //-----------------------------------------------------------------------------
676 static const char* GetClipboardTextFn_DefaultImpl(void* user_data);
677 static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
678 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
680 //-----------------------------------------------------------------------------
682 //-----------------------------------------------------------------------------
684 // Default font atlas storage.
685 // New contexts always point by default to this font atlas. It can be changed by reassigning the GetIO().Fonts variable.
686 static ImFontAtlas GImDefaultFontAtlas;
688 // Default context storage + current context pointer.
689 // Implicitely used by all ImGui functions. Always assumed to be != NULL. Change to a different context by calling ImGui::SetCurrentContext()
690 // If you are hot-reloading this code in a DLL you will lose the static/global variables. Create your own context+font atlas instead of relying on those default (see FAQ entry "How can I preserve my ImGui context across reloading a DLL?").
691 // ImGui is currently not thread-safe because of this variable. If you want thread-safety to allow N threads to access N different contexts, you might work around it by:
692 // - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts)
693 // - or: Changing this variable to be TLS. You may #define GImGui in imconfig.h for further custom hackery. Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
695 static ImGuiContext GImDefaultContext;
696 ImGuiContext* GImGui = &GImDefaultContext;
699 //-----------------------------------------------------------------------------
700 // User facing structures
701 //-----------------------------------------------------------------------------
703 ImGuiStyle::ImGuiStyle()
705 Alpha = 1.0f; // Global alpha applies to everything in ImGui
706 WindowPadding = ImVec2(8,8); // Padding within a window
707 WindowRounding = 9.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows
708 WindowBorderSize = 0.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
709 WindowMinSize = ImVec2(32,32); // Minimum window size
710 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
711 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
712 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
713 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
714 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
715 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
716 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
717 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
718 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
719 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
720 TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!
721 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
722 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns
723 ScrollbarSize = 16.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
724 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
725 GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
726 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
727 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
728 DisplayWindowPadding = ImVec2(22,22); // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows.
729 DisplaySafeAreaPadding = ImVec2(4,4); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
730 AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
731 AntiAliasedShapes = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
732 CurveTessellationTol = 1.25f; // Tessellation tolerance. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
734 ImGui::StyleColorsClassic(this);
737 // To scale your entire UI (e.g. if you want your app to use High DPI or generally be DPI aware) you may use this helper function. Scaling the fonts is done separately and is up to you.
738 // Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times.
739 void ImGuiStyle::ScaleAllSizes(float scale_factor)
741 WindowPadding = ImFloor(WindowPadding * scale_factor);
742 WindowRounding = ImFloor(WindowRounding * scale_factor);
743 WindowMinSize = ImFloor(WindowMinSize * scale_factor);
744 ChildRounding = ImFloor(ChildRounding * scale_factor);
745 PopupRounding = ImFloor(PopupRounding * scale_factor);
746 FramePadding = ImFloor(FramePadding * scale_factor);
747 FrameRounding = ImFloor(FrameRounding * scale_factor);
748 ItemSpacing = ImFloor(ItemSpacing * scale_factor);
749 ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
750 TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
751 IndentSpacing = ImFloor(IndentSpacing * scale_factor);
752 ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
753 ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
754 ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
755 GrabMinSize = ImFloor(GrabMinSize * scale_factor);
756 GrabRounding = ImFloor(GrabRounding * scale_factor);
757 DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
758 DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
763 // Most fields are initialized with zero
764 memset(this, 0, sizeof(*this));
767 DisplaySize = ImVec2(-1.0f, -1.0f);
768 DeltaTime = 1.0f/60.0f;
769 IniSavingRate = 5.0f;
770 IniFilename = "imgui.ini";
771 LogFilename = "imgui_log.txt";
772 MouseDoubleClickTime = 0.30f;
773 MouseDoubleClickMaxDist = 6.0f;
774 for (int i = 0; i < ImGuiKey_COUNT; i++)
776 KeyRepeatDelay = 0.250f;
777 KeyRepeatRate = 0.050f;
780 Fonts = &GImDefaultFontAtlas;
781 FontGlobalScale = 1.0f;
783 FontAllowUserScaling = false;
784 DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
785 DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f);
787 // Advanced/subtle behaviors
789 OptMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
791 OptMacOSXBehaviors = false;
793 OptCursorBlink = true;
795 // Settings (User Functions)
796 RenderDrawListsFn = NULL;
799 GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
800 SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
801 ClipboardUserData = NULL;
802 ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
803 ImeWindowHandle = NULL;
805 // Input (NB: we already have memset zero the entire structure)
806 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
807 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
808 MouseDragThreshold = 6.0f;
809 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
810 for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
813 // Pass in translated ASCII characters for text input.
814 // - with glfw you can get those from the callback set in glfwSetCharCallback()
815 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
816 void ImGuiIO::AddInputCharacter(ImWchar c)
818 const int n = ImStrlenW(InputCharacters);
819 if (n + 1 < IM_ARRAYSIZE(InputCharacters))
821 InputCharacters[n] = c;
822 InputCharacters[n+1] = '\0';
826 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
828 // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more
829 const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar);
830 ImWchar wchars[wchars_buf_len];
831 ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL);
832 for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++)
833 AddInputCharacter(wchars[i]);
836 //-----------------------------------------------------------------------------
838 //-----------------------------------------------------------------------------
840 #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose
841 #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255
843 // Play it nice with Windows users. Notepad in 2015 still doesn't display text data with Unix-style \n.
845 #define IM_NEWLINE "\r\n"
847 #define IM_NEWLINE "\n"
850 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
853 ImVec2 ab_dir = b - a;
854 float ab_len = sqrtf(ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y);
855 ab_dir *= 1.0f / ab_len;
856 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
861 return a + ab_dir * dot;
864 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
866 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
867 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
868 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
869 return ((b1 == b2) && (b2 == b3));
872 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
877 const float denom = v0.x * v1.y - v1.x * v0.y;
878 out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
879 out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
880 out_u = 1.0f - out_v - out_w;
883 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
885 ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
886 ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
887 ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
888 float dist2_ab = ImLengthSqr(p - proj_ab);
889 float dist2_bc = ImLengthSqr(p - proj_bc);
890 float dist2_ca = ImLengthSqr(p - proj_ca);
891 float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
899 int ImStricmp(const char* str1, const char* str2)
902 while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
906 int ImStrnicmp(const char* str1, const char* str2, int count)
909 while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
913 void ImStrncpy(char* dst, const char* src, int count)
915 if (count < 1) return;
916 strncpy(dst, src, (size_t)count);
920 char* ImStrdup(const char *str)
922 size_t len = strlen(str) + 1;
923 void* buff = ImGui::MemAlloc(len);
924 return (char*)memcpy(buff, (const void*)str, len);
927 int ImStrlenW(const ImWchar* str)
934 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
936 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
941 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
944 needle_end = needle + strlen(needle);
946 const char un0 = (char)toupper(*needle);
947 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
949 if (toupper(*haystack) == un0)
951 const char* b = needle + 1;
952 for (const char* a = haystack + 1; b < needle_end; a++, b++)
953 if (toupper(*a) != toupper(*b))
963 static const char* ImAtoi(const char* src, int* output)
966 if (*src == '-') { negative = 1; src++; }
967 if (*src == '+') { src++; }
969 while (*src >= '0' && *src <= '9')
970 v = (v * 10) + (*src++ - '0');
971 *output = negative ? -v : v;
975 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
976 // Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm.
977 // B) When buf==NULL vsnprintf() will return the output size.
978 #ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
979 int ImFormatString(char* buf, int buf_size, const char* fmt, ...)
983 int w = vsnprintf(buf, buf_size, fmt, args);
987 if (w == -1 || w >= buf_size)
993 int ImFormatStringV(char* buf, int buf_size, const char* fmt, va_list args)
995 int w = vsnprintf(buf, buf_size, fmt, args);
998 if (w == -1 || w >= buf_size)
1003 #endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1005 // Pass data_size==0 for zero-terminated strings
1006 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
1007 ImU32 ImHash(const void* data, int data_size, ImU32 seed)
1009 static ImU32 crc32_lut[256] = { 0 };
1012 const ImU32 polynomial = 0xEDB88320;
1013 for (ImU32 i = 0; i < 256; i++)
1016 for (ImU32 j = 0; j < 8; j++)
1017 crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial);
1024 const unsigned char* current = (const unsigned char*)data;
1030 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++];
1034 // Zero-terminated string
1035 while (unsigned char c = *current++)
1037 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1038 // Because this syntax is rarely used we are optimizing for the common case.
1039 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1040 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller.
1041 if (c == '#' && current[0] == '#' && current[1] == '#')
1043 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1049 //-----------------------------------------------------------------------------
1051 //-----------------------------------------------------------------------------
1053 // Convert UTF-8 to 32-bits character, process single character input.
1054 // Based on stb_from_utf8() from github.com/nothings/stb/
1055 // We handle UTF-8 decoding error by skipping forward.
1056 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1058 unsigned int c = (unsigned int)-1;
1059 const unsigned char* str = (const unsigned char*)in_text;
1062 c = (unsigned int)(*str++);
1066 if ((*str & 0xe0) == 0xc0)
1068 *out_char = 0xFFFD; // will be invalid but not end of string
1069 if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1070 if (*str < 0xc2) return 2;
1071 c = (unsigned int)((*str++ & 0x1f) << 6);
1072 if ((*str & 0xc0) != 0x80) return 2;
1073 c += (*str++ & 0x3f);
1077 if ((*str & 0xf0) == 0xe0)
1079 *out_char = 0xFFFD; // will be invalid but not end of string
1080 if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1081 if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1082 if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1083 c = (unsigned int)((*str++ & 0x0f) << 12);
1084 if ((*str & 0xc0) != 0x80) return 3;
1085 c += (unsigned int)((*str++ & 0x3f) << 6);
1086 if ((*str & 0xc0) != 0x80) return 3;
1087 c += (*str++ & 0x3f);
1091 if ((*str & 0xf8) == 0xf0)
1093 *out_char = 0xFFFD; // will be invalid but not end of string
1094 if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1095 if (*str > 0xf4) return 4;
1096 if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1097 if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1098 c = (unsigned int)((*str++ & 0x07) << 18);
1099 if ((*str & 0xc0) != 0x80) return 4;
1100 c += (unsigned int)((*str++ & 0x3f) << 12);
1101 if ((*str & 0xc0) != 0x80) return 4;
1102 c += (unsigned int)((*str++ & 0x3f) << 6);
1103 if ((*str & 0xc0) != 0x80) return 4;
1104 c += (*str++ & 0x3f);
1105 // utf-8 encodings of values used in surrogate pairs are invalid
1106 if ((c & 0xFFFFF800) == 0xD800) return 4;
1114 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1116 ImWchar* buf_out = buf;
1117 ImWchar* buf_end = buf + buf_size;
1118 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1121 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1124 if (c < 0x10000) // FIXME: Losing characters that don't fit in 2 bytes
1125 *buf_out++ = (ImWchar)c;
1128 if (in_text_remaining)
1129 *in_text_remaining = in_text;
1130 return (int)(buf_out - buf);
1133 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1136 while ((!in_text_end || in_text < in_text_end) && *in_text)
1139 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1148 // Based on stb_to_utf8() from github.com/nothings/stb/
1149 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1158 if (buf_size < 2) return 0;
1159 buf[0] = (char)(0xc0 + (c >> 6));
1160 buf[1] = (char)(0x80 + (c & 0x3f));
1163 if (c >= 0xdc00 && c < 0xe000)
1167 if (c >= 0xd800 && c < 0xdc00)
1169 if (buf_size < 4) return 0;
1170 buf[0] = (char)(0xf0 + (c >> 18));
1171 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1172 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1173 buf[3] = (char)(0x80 + ((c ) & 0x3f));
1176 //else if (c < 0x10000)
1178 if (buf_size < 3) return 0;
1179 buf[0] = (char)(0xe0 + (c >> 12));
1180 buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1181 buf[2] = (char)(0x80 + ((c ) & 0x3f));
1186 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1188 if (c < 0x80) return 1;
1189 if (c < 0x800) return 2;
1190 if (c >= 0xdc00 && c < 0xe000) return 0;
1191 if (c >= 0xd800 && c < 0xdc00) return 4;
1195 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1197 char* buf_out = buf;
1198 const char* buf_end = buf + buf_size;
1199 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1201 unsigned int c = (unsigned int)(*in_text++);
1203 *buf_out++ = (char)c;
1205 buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1208 return (int)(buf_out - buf);
1211 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1213 int bytes_count = 0;
1214 while ((!in_text_end || in_text < in_text_end) && *in_text)
1216 unsigned int c = (unsigned int)(*in_text++);
1220 bytes_count += ImTextCountUtf8BytesFromChar(c);
1225 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1227 float s = 1.0f/255.0f;
1229 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1230 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1231 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1232 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1235 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1238 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1239 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1240 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1241 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1245 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1247 ImGuiStyle& style = GImGui->Style;
1248 ImVec4 c = style.Colors[idx];
1249 c.w *= style.Alpha * alpha_mul;
1250 return ColorConvertFloat4ToU32(c);
1253 ImU32 ImGui::GetColorU32(const ImVec4& col)
1255 ImGuiStyle& style = GImGui->Style;
1258 return ColorConvertFloat4ToU32(c);
1261 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
1263 ImGuiStyle& style = GImGui->Style;
1264 return style.Colors[idx];
1267 ImU32 ImGui::GetColorU32(ImU32 col)
1269 float style_alpha = GImGui->Style.Alpha;
1270 if (style_alpha >= 1.0f)
1272 int a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
1273 a = (int)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
1274 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
1277 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1278 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
1279 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1293 const float chroma = r - (g < b ? g : b);
1294 out_h = fabsf(K + (g - b) / (6.f * chroma + 1e-20f));
1295 out_s = chroma / (r + 1e-20f);
1299 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1300 // also http://en.wikipedia.org/wiki/HSL_and_HSV
1301 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1306 out_r = out_g = out_b = v;
1310 h = fmodf(h, 1.0f) / (60.0f/360.0f);
1312 float f = h - (float)i;
1313 float p = v * (1.0f - s);
1314 float q = v * (1.0f - s * f);
1315 float t = v * (1.0f - s * (1.0f - f));
1319 case 0: out_r = v; out_g = t; out_b = p; break;
1320 case 1: out_r = q; out_g = v; out_b = p; break;
1321 case 2: out_r = p; out_g = v; out_b = t; break;
1322 case 3: out_r = p; out_g = q; out_b = v; break;
1323 case 4: out_r = t; out_g = p; out_b = v; break;
1324 case 5: default: out_r = v; out_g = p; out_b = q; break;
1328 FILE* ImFileOpen(const char* filename, const char* mode)
1330 #if defined(_WIN32) && !defined(__CYGWIN__)
1331 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can)
1332 const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1333 const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1334 ImVector<ImWchar> buf;
1335 buf.resize(filename_wsize + mode_wsize);
1336 ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1337 ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1338 return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1340 return fopen(filename, mode);
1344 // Load file content into memory
1345 // Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree()
1346 void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, int* out_file_size, int padding_bytes)
1348 IM_ASSERT(filename && file_open_mode);
1353 if ((f = ImFileOpen(filename, file_open_mode)) == NULL)
1356 long file_size_signed;
1357 if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
1363 int file_size = (int)file_size_signed;
1364 void* file_data = ImGui::MemAlloc(file_size + padding_bytes);
1365 if (file_data == NULL)
1370 if (fread(file_data, 1, (size_t)file_size, f) != (size_t)file_size)
1373 ImGui::MemFree(file_data);
1376 if (padding_bytes > 0)
1377 memset((void *)(((char*)file_data) + file_size), 0, padding_bytes);
1381 *out_file_size = file_size;
1386 //-----------------------------------------------------------------------------
1388 // Helper: Key->value storage
1389 //-----------------------------------------------------------------------------
1391 // std::lower_bound but without the bullshit
1392 static ImVector<ImGuiStorage::Pair>::iterator LowerBound(ImVector<ImGuiStorage::Pair>& data, ImGuiID key)
1394 ImVector<ImGuiStorage::Pair>::iterator first = data.begin();
1395 ImVector<ImGuiStorage::Pair>::iterator last = data.end();
1396 size_t count = (size_t)(last - first);
1399 size_t count2 = count >> 1;
1400 ImVector<ImGuiStorage::Pair>::iterator mid = first + count2;
1404 count -= count2 + 1;
1414 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
1415 void ImGuiStorage::BuildSortByKey()
1419 static int PairCompareByID(const void* lhs, const void* rhs)
1421 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1422 if (((const Pair*)lhs)->key > ((const Pair*)rhs)->key) return +1;
1423 if (((const Pair*)lhs)->key < ((const Pair*)rhs)->key) return -1;
1428 qsort(Data.Data, (size_t)Data.Size, sizeof(Pair), StaticFunc::PairCompareByID);
1431 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1433 ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1434 if (it == Data.end() || it->key != key)
1439 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1441 return GetInt(key, default_val ? 1 : 0) != 0;
1444 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1446 ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1447 if (it == Data.end() || it->key != key)
1452 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1454 ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1455 if (it == Data.end() || it->key != key)
1460 // References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer.
1461 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1463 ImVector<Pair>::iterator it = LowerBound(Data, key);
1464 if (it == Data.end() || it->key != key)
1465 it = Data.insert(it, Pair(key, default_val));
1469 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1471 return (bool*)GetIntRef(key, default_val ? 1 : 0);
1474 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1476 ImVector<Pair>::iterator it = LowerBound(Data, key);
1477 if (it == Data.end() || it->key != key)
1478 it = Data.insert(it, Pair(key, default_val));
1482 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1484 ImVector<Pair>::iterator it = LowerBound(Data, key);
1485 if (it == Data.end() || it->key != key)
1486 it = Data.insert(it, Pair(key, default_val));
1490 // FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame)
1491 void ImGuiStorage::SetInt(ImGuiID key, int val)
1493 ImVector<Pair>::iterator it = LowerBound(Data, key);
1494 if (it == Data.end() || it->key != key)
1496 Data.insert(it, Pair(key, val));
1502 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1504 SetInt(key, val ? 1 : 0);
1507 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1509 ImVector<Pair>::iterator it = LowerBound(Data, key);
1510 if (it == Data.end() || it->key != key)
1512 Data.insert(it, Pair(key, val));
1518 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1520 ImVector<Pair>::iterator it = LowerBound(Data, key);
1521 if (it == Data.end() || it->key != key)
1523 Data.insert(it, Pair(key, val));
1529 void ImGuiStorage::SetAllInt(int v)
1531 for (int i = 0; i < Data.Size; i++)
1535 //-----------------------------------------------------------------------------
1537 //-----------------------------------------------------------------------------
1539 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
1540 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1544 ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1554 bool ImGuiTextFilter::Draw(const char* label, float width)
1557 ImGui::PushItemWidth(width);
1558 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1560 ImGui::PopItemWidth();
1563 return value_changed;
1566 void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>& out)
1570 const char* we = wb;
1573 if (*we == separator)
1575 out.push_back(TextRange(wb, we));
1581 out.push_back(TextRange(wb, we));
1584 void ImGuiTextFilter::Build()
1587 TextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
1588 input_range.split(',', Filters);
1591 for (int i = 0; i != Filters.Size; i++)
1593 Filters[i].trim_blanks();
1594 if (Filters[i].empty())
1596 if (Filters[i].front() != '-')
1601 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
1603 if (Filters.empty())
1609 for (int i = 0; i != Filters.Size; i++)
1611 const TextRange& f = Filters[i];
1614 if (f.front() == '-')
1617 if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL)
1623 if (ImStristr(text, text_end, f.begin(), f.end()) != NULL)
1635 //-----------------------------------------------------------------------------
1637 //-----------------------------------------------------------------------------
1639 // On some platform vsnprintf() takes va_list by reference and modifies it.
1640 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1642 #define va_copy(dest, src) (dest = src)
1645 // Helper: Text buffer for logging/accumulating text
1646 void ImGuiTextBuffer::appendv(const char* fmt, va_list args)
1649 va_copy(args_copy, args);
1651 int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
1655 const int write_off = Buf.Size;
1656 const int needed_sz = write_off + len;
1657 if (write_off + len >= Buf.Capacity)
1659 int double_capacity = Buf.Capacity * 2;
1660 Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity);
1663 Buf.resize(needed_sz);
1664 ImFormatStringV(&Buf[write_off - 1], len + 1, fmt, args_copy);
1667 void ImGuiTextBuffer::append(const char* fmt, ...)
1670 va_start(args, fmt);
1675 //-----------------------------------------------------------------------------
1676 // ImGuiSimpleColumns (internal use only)
1677 //-----------------------------------------------------------------------------
1679 ImGuiSimpleColumns::ImGuiSimpleColumns()
1682 Spacing = Width = NextWidth = 0.0f;
1683 memset(Pos, 0, sizeof(Pos));
1684 memset(NextWidths, 0, sizeof(NextWidths));
1687 void ImGuiSimpleColumns::Update(int count, float spacing, bool clear)
1689 IM_ASSERT(Count <= IM_ARRAYSIZE(Pos));
1691 Width = NextWidth = 0.0f;
1693 if (clear) memset(NextWidths, 0, sizeof(NextWidths));
1694 for (int i = 0; i < Count; i++)
1696 if (i > 0 && NextWidths[i] > 0.0f)
1698 Pos[i] = (float)(int)Width;
1699 Width += NextWidths[i];
1700 NextWidths[i] = 0.0f;
1704 float ImGuiSimpleColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double
1707 NextWidths[0] = ImMax(NextWidths[0], w0);
1708 NextWidths[1] = ImMax(NextWidths[1], w1);
1709 NextWidths[2] = ImMax(NextWidths[2], w2);
1710 for (int i = 0; i < 3; i++)
1711 NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f);
1712 return ImMax(Width, NextWidth);
1715 float ImGuiSimpleColumns::CalcExtraSpace(float avail_w)
1717 return ImMax(0.0f, avail_w - Width);
1720 //-----------------------------------------------------------------------------
1722 //-----------------------------------------------------------------------------
1724 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
1726 // Set cursor position and a few other things so that SetScrollHere() and Columns() can work when seeking cursor.
1727 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. Consider moving within SetCursorXXX functions?
1728 ImGui::SetCursorPosY(pos_y);
1729 ImGuiWindow* window = ImGui::GetCurrentWindow();
1730 window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHere() can properly function after the end of our clipper usage.
1731 window->DC.PrevLineHeight = (line_height - GImGui->Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list.
1732 if (window->DC.ColumnsCount > 1)
1733 window->DC.ColumnsCellMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
1736 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
1737 // Use case B: Begin() called from constructor with items_height>0
1738 // FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style.
1739 void ImGuiListClipper::Begin(int count, float items_height)
1741 StartPosY = ImGui::GetCursorPosY();
1742 ItemsHeight = items_height;
1745 DisplayEnd = DisplayStart = -1;
1746 if (ItemsHeight > 0.0f)
1748 ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
1749 if (DisplayStart > 0)
1750 SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
1755 void ImGuiListClipper::End()
1759 // In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user.
1760 if (ItemsCount < INT_MAX)
1761 SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
1766 bool ImGuiListClipper::Step()
1768 if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems)
1773 if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height.
1777 StartPosY = ImGui::GetCursorPosY();
1781 if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element.
1783 if (ItemsCount == 1) { ItemsCount = -1; return false; }
1784 float items_height = ImGui::GetCursorPosY() - StartPosY;
1785 IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically
1786 Begin(ItemsCount-1, items_height);
1792 if (StepNo == 2) // Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3.
1794 IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
1798 if (StepNo == 3) // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop.
1803 //-----------------------------------------------------------------------------
1805 //-----------------------------------------------------------------------------
1807 ImGuiWindow::ImGuiWindow(const char* name)
1809 Name = ImStrdup(name);
1810 ID = ImHash(name, 0);
1811 IDStack.push_back(ID);
1813 OrderWithinParent = 0;
1814 PosFloat = Pos = ImVec2(0.0f, 0.0f);
1815 Size = SizeFull = ImVec2(0.0f, 0.0f);
1816 SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
1817 WindowPadding = ImVec2(0.0f, 0.0f);
1818 WindowRounding = 0.0f;
1819 WindowBorderSize = 0.0f;
1820 MoveId = GetID("#MOVE");
1821 Scroll = ImVec2(0.0f, 0.0f);
1822 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
1823 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
1824 ScrollbarX = ScrollbarY = false;
1825 ScrollbarSizes = ImVec2(0.0f, 0.0f);
1826 Active = WasActive = false;
1827 WriteAccessed = false;
1831 CloseButton = false;
1834 AutoFitFramesX = AutoFitFramesY = -1;
1835 AutoFitOnlyGrows = false;
1836 AutoFitChildAxises = 0x00;
1837 AutoPosLastDirection = -1;
1839 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
1840 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
1842 LastFrameActive = -1;
1843 ItemWidthDefault = 0.0f;
1844 FontWindowScale = 1.0f;
1846 DrawList = (ImDrawList*)ImGui::MemAlloc(sizeof(ImDrawList));
1847 IM_PLACEMENT_NEW(DrawList) ImDrawList();
1848 DrawList->_OwnerName = Name;
1849 ParentWindow = NULL;
1851 RootNonPopupWindow = NULL;
1853 FocusIdxAllCounter = FocusIdxTabCounter = -1;
1854 FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX;
1855 FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX;
1858 ImGuiWindow::~ImGuiWindow()
1860 DrawList->~ImDrawList();
1861 ImGui::MemFree(DrawList);
1863 ImGui::MemFree(Name);
1867 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
1869 ImGuiID seed = IDStack.back();
1870 ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
1871 ImGui::KeepAliveID(id);
1875 ImGuiID ImGuiWindow::GetID(const void* ptr)
1877 ImGuiID seed = IDStack.back();
1878 ImGuiID id = ImHash(&ptr, sizeof(void*), seed);
1879 ImGui::KeepAliveID(id);
1883 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
1885 ImGuiID seed = IDStack.back();
1886 return ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
1889 //-----------------------------------------------------------------------------
1890 // Internal API exposed in imgui_internal.h
1891 //-----------------------------------------------------------------------------
1893 static void SetCurrentWindow(ImGuiWindow* window)
1895 ImGuiContext& g = *GImGui;
1896 g.CurrentWindow = window;
1898 g.FontSize = window->CalcFontSize();
1901 ImGuiWindow* ImGui::GetParentWindow()
1903 ImGuiContext& g = *GImGui;
1904 IM_ASSERT(g.CurrentWindowStack.Size >= 2);
1905 return g.CurrentWindowStack[(unsigned int)g.CurrentWindowStack.Size - 2];
1908 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
1910 ImGuiContext& g = *GImGui;
1911 g.ActiveIdIsJustActivated = (g.ActiveId != id);
1912 if (g.ActiveIdIsJustActivated)
1913 g.ActiveIdTimer = 0.0f;
1915 g.ActiveIdAllowOverlap = false;
1916 g.ActiveIdIsAlive |= (id != 0);
1917 g.ActiveIdWindow = window;
1920 void ImGui::ClearActiveID()
1922 SetActiveID(0, NULL);
1925 void ImGui::SetHoveredID(ImGuiID id)
1927 ImGuiContext& g = *GImGui;
1929 g.HoveredIdAllowOverlap = false;
1930 g.HoveredIdTimer = (id != 0 && g.HoveredIdPreviousFrame == id) ? (g.HoveredIdTimer + g.IO.DeltaTime) : 0.0f;
1933 void ImGui::KeepAliveID(ImGuiID id)
1935 ImGuiContext& g = *GImGui;
1936 if (g.ActiveId == id)
1937 g.ActiveIdIsAlive = true;
1940 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
1942 // An active popup disable hovering on other windows (apart from its own children)
1943 // FIXME-OPT: This could be cached/stored within the window.
1944 ImGuiContext& g = *GImGui;
1946 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
1947 if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
1949 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
1950 // NB: The order of those two tests is important because Modal windows are also Popups.
1951 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
1953 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
1960 // Advance cursor given item size for layout.
1961 void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
1963 ImGuiContext& g = *GImGui;
1964 ImGuiWindow* window = g.CurrentWindow;
1965 if (window->SkipItems)
1968 // Always align ourselves on pixel boundaries
1969 const float line_height = ImMax(window->DC.CurrentLineHeight, size.y);
1970 const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y);
1971 //if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG]
1972 window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
1973 window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y));
1974 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
1975 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
1976 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
1978 window->DC.PrevLineHeight = line_height;
1979 window->DC.PrevLineTextBaseOffset = text_base_offset;
1980 window->DC.CurrentLineHeight = window->DC.CurrentLineTextBaseOffset = 0.0f;
1982 // Horizontal layout mode
1983 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
1987 void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
1989 ItemSize(bb.GetSize(), text_offset_y);
1992 // Declare item bounding box for clipping and interaction.
1993 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
1994 // declares their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd().
1995 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id)
1997 ImGuiContext& g = *GImGui;
1998 ImGuiWindow* window = g.CurrentWindow;
1999 const bool is_clipped = IsClippedEx(bb, id, false);
2000 window->DC.LastItemId = id;
2001 window->DC.LastItemRect = bb;
2002 window->DC.LastItemRectHoveredRect = false;
2005 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2007 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2008 window->DC.LastItemRectHoveredRect = IsMouseHoveringRect(bb.Min, bb.Max);
2012 // This is roughly matching the behavior of internal-facing ItemHoverable()
2013 // - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered()
2014 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
2015 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2017 ImGuiContext& g = *GImGui;
2019 ImGuiWindow* window = g.CurrentWindow;
2020 if (!window->DC.LastItemRectHoveredRect)
2022 IM_ASSERT((flags & ImGuiHoveredFlags_FlattenChilds) == 0); // Flags not supported by this function
2024 // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable to use IsItemHovered() after EndChild() itself.
2025 // Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while.
2026 //if (g.HoveredWindow != window)
2028 if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2030 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2031 if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2033 if (!IsWindowContentHoverable(window, flags))
2035 if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2037 // Special handling for the 1st item after Begin() which represent the title bar. When the window is collapsed (SkipItems==true) that last item will never be overwritten.
2038 if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2043 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
2044 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2046 ImGuiContext& g = *GImGui;
2047 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
2050 ImGuiWindow* window = g.CurrentWindow;
2051 if (g.HoveredWindow != window)
2053 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
2055 if (!IsMouseHoveringRect(bb.Min, bb.Max))
2057 if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_Default))
2059 if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2066 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
2068 ImGuiContext& g = *GImGui;
2069 ImGuiWindow* window = g.CurrentWindow;
2070 if (!bb.Overlaps(window->ClipRect))
2071 if (id == 0 || id != g.ActiveId)
2072 if (clip_even_when_logged || !g.LogEnabled)
2077 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop)
2079 ImGuiContext& g = *GImGui;
2081 const bool allow_keyboard_focus = (window->DC.ItemFlags & (ImGuiItemFlags_AllowKeyboardFocus | ImGuiItemFlags_Disabled)) == ImGuiItemFlags_AllowKeyboardFocus;
2082 window->FocusIdxAllCounter++;
2083 if (allow_keyboard_focus)
2084 window->FocusIdxTabCounter++;
2086 // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item.
2087 // Note that we can always TAB out of a widget that doesn't allow tabbing in.
2088 if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab))
2089 window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items.
2091 if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
2094 if (allow_keyboard_focus)
2095 if (window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent)
2101 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
2103 window->FocusIdxAllCounter--;
2104 window->FocusIdxTabCounter--;
2107 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y)
2109 ImGuiContext& g = *GImGui;
2111 if (size.x < 0.0f || size.y < 0.0f)
2112 content_max = g.CurrentWindow->Pos + GetContentRegionMax();
2114 size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x;
2116 size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y;
2120 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
2122 if (wrap_pos_x < 0.0f)
2125 ImGuiWindow* window = GetCurrentWindowRead();
2126 if (wrap_pos_x == 0.0f)
2127 wrap_pos_x = GetContentRegionMax().x + window->Pos.x;
2128 else if (wrap_pos_x > 0.0f)
2129 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
2131 return ImMax(wrap_pos_x - pos.x, 1.0f);
2134 //-----------------------------------------------------------------------------
2136 void* ImGui::MemAlloc(size_t sz)
2138 GImGui->IO.MetricsAllocs++;
2139 return GImGui->IO.MemAllocFn(sz);
2142 void ImGui::MemFree(void* ptr)
2144 if (ptr) GImGui->IO.MetricsAllocs--;
2145 return GImGui->IO.MemFreeFn(ptr);
2148 const char* ImGui::GetClipboardText()
2150 return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : "";
2153 void ImGui::SetClipboardText(const char* text)
2155 if (GImGui->IO.SetClipboardTextFn)
2156 GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text);
2159 const char* ImGui::GetVersion()
2161 return IMGUI_VERSION;
2164 // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
2165 // Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module
2166 ImGuiContext* ImGui::GetCurrentContext()
2171 void ImGui::SetCurrentContext(ImGuiContext* ctx)
2173 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
2174 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
2180 ImGuiContext* ImGui::CreateContext(void* (*malloc_fn)(size_t), void (*free_fn)(void*))
2182 if (!malloc_fn) malloc_fn = malloc;
2183 ImGuiContext* ctx = (ImGuiContext*)malloc_fn(sizeof(ImGuiContext));
2184 IM_PLACEMENT_NEW(ctx) ImGuiContext();
2185 ctx->IO.MemAllocFn = malloc_fn;
2186 ctx->IO.MemFreeFn = free_fn ? free_fn : free;
2190 void ImGui::DestroyContext(ImGuiContext* ctx)
2192 void (*free_fn)(void*) = ctx->IO.MemFreeFn;
2193 ctx->~ImGuiContext();
2196 SetCurrentContext(NULL);
2199 ImGuiIO& ImGui::GetIO()
2204 ImGuiStyle& ImGui::GetStyle()
2206 return GImGui->Style;
2209 // Same value as passed to your RenderDrawListsFn() function. valid after Render() and until the next call to NewFrame()
2210 ImDrawData* ImGui::GetDrawData()
2212 return GImGui->RenderDrawData.Valid ? &GImGui->RenderDrawData : NULL;
2215 float ImGui::GetTime()
2217 return GImGui->Time;
2220 int ImGui::GetFrameCount()
2222 return GImGui->FrameCount;
2225 void ImGui::NewFrame()
2227 ImGuiContext& g = *GImGui;
2230 // (We pass an error message in the assert expression as a trick to get it visible to programmers who are not using a debugger, as most assert handlers display their argument)
2231 IM_ASSERT(g.IO.DeltaTime >= 0.0f && "Need a positive DeltaTime (zero is tolerated but will cause some timing issues)");
2232 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value");
2233 IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?");
2234 IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?");
2235 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting");
2236 IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)");
2237 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
2239 // Initialize on first frame
2241 ImGui::Initialize();
2243 SetCurrentFont(GetDefaultFont());
2244 IM_ASSERT(g.Font->IsLoaded());
2246 g.Time += g.IO.DeltaTime;
2248 g.TooltipOverrideCount = 0;
2249 g.OverlayDrawList.Clear();
2250 g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID);
2251 g.OverlayDrawList.PushClipRectFullScreen();
2253 // Mark rendering data as invalid to prevent user who may have a handle on it to use it
2254 g.RenderDrawData.Valid = false;
2255 g.RenderDrawData.CmdLists = NULL;
2256 g.RenderDrawData.CmdListsCount = g.RenderDrawData.TotalVtxCount = g.RenderDrawData.TotalIdxCount = 0;
2258 // Clear reference to active widget if the widget isn't alive anymore
2259 if (!g.HoveredIdPreviousFrame)
2260 g.HoveredIdTimer = 0.0f;
2261 g.HoveredIdPreviousFrame = g.HoveredId;
2263 g.HoveredIdAllowOverlap = false;
2264 if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
2267 g.ActiveIdTimer += g.IO.DeltaTime;
2268 g.ActiveIdPreviousFrame = g.ActiveId;
2269 g.ActiveIdIsAlive = false;
2270 g.ActiveIdIsJustActivated = false;
2271 if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
2272 g.ScalarAsInputTextId = 0;
2274 // Update keyboard input state
2275 memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
2276 for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
2277 g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f;
2279 // Update mouse input state
2280 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX component, but in reality we test for -256000.0f) we cancel out movement in MouseDelta
2281 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
2282 g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
2284 g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
2285 g.IO.MousePosPrev = g.IO.MousePos;
2286 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
2288 g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
2289 g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
2290 g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
2291 g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f;
2292 g.IO.MouseDoubleClicked[i] = false;
2293 if (g.IO.MouseClicked[i])
2295 if (g.Time - g.IO.MouseClickedTime[i] < g.IO.MouseDoubleClickTime)
2297 if (ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
2298 g.IO.MouseDoubleClicked[i] = true;
2299 g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click
2303 g.IO.MouseClickedTime[i] = g.Time;
2305 g.IO.MouseClickedPos[i] = g.IO.MousePos;
2306 g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
2307 g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
2309 else if (g.IO.MouseDown[i])
2311 ImVec2 mouse_delta = g.IO.MousePos - g.IO.MouseClickedPos[i];
2312 g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, mouse_delta.x < 0.0f ? -mouse_delta.x : mouse_delta.x);
2313 g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, mouse_delta.y < 0.0f ? -mouse_delta.y : mouse_delta.y);
2314 g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(mouse_delta));
2318 // Calculate frame-rate for the user, as a purely luxurious feature
2319 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
2320 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
2321 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
2322 g.IO.Framerate = 1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame));
2324 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows.
2325 if (g.MovingWindowMoveId && g.MovingWindowMoveId == g.ActiveId)
2327 KeepAliveID(g.MovingWindowMoveId);
2328 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
2329 IM_ASSERT(g.MovingWindow->MoveId == g.MovingWindowMoveId);
2330 if (g.IO.MouseDown[0])
2332 g.MovingWindow->RootWindow->PosFloat += g.IO.MouseDelta;
2333 if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
2334 MarkIniSettingsDirty(g.MovingWindow->RootWindow);
2335 FocusWindow(g.MovingWindow);
2340 g.MovingWindow = NULL;
2341 g.MovingWindowMoveId = 0;
2346 g.MovingWindow = NULL;
2347 g.MovingWindowMoveId = 0;
2350 // Delay saving settings so we don't spam disk too much
2351 if (g.SettingsDirtyTimer > 0.0f)
2353 g.SettingsDirtyTimer -= g.IO.DeltaTime;
2354 if (g.SettingsDirtyTimer <= 0.0f)
2355 SaveIniSettingsToDisk(g.IO.IniFilename);
2358 // Find the window we are hovering
2359 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
2360 // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point.
2361 // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms.
2362 g.HoveredWindow = (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) ? g.MovingWindow : FindHoveredWindow(g.IO.MousePos);
2363 g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
2365 if (ImGuiWindow* modal_window = GetFrontMostModalRootWindow())
2367 g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f);
2368 ImGuiWindow* window = g.HoveredRootWindow;
2369 while (window && window != modal_window)
2370 window = window->ParentWindow;
2372 g.HoveredRootWindow = g.HoveredWindow = NULL;
2376 g.ModalWindowDarkeningRatio = 0.0f;
2379 // Update the WantCaptureMouse/WantCAptureKeyboard flags, so user can capture/discard the inputs away from the rest of their application.
2380 // When clicking outside of a window we assume the click is owned by the application and won't request capture. We need to track click ownership.
2381 int mouse_earliest_button_down = -1;
2382 bool mouse_any_down = false;
2383 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
2385 if (g.IO.MouseClicked[i])
2386 g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
2387 mouse_any_down |= g.IO.MouseDown[i];
2388 if (g.IO.MouseDown[i])
2389 if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
2390 mouse_earliest_button_down = i;
2392 bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
2393 if (g.WantCaptureMouseNextFrame != -1)
2394 g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
2396 g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
2397 g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != -1) ? (g.WantCaptureKeyboardNextFrame != 0) : (g.ActiveId != 0);
2398 g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : 0;
2399 g.MouseCursor = ImGuiMouseCursor_Arrow;
2400 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
2401 g.OsImePosRequest = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
2403 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
2404 // FIXME: For patterns of drag and drop between "application" and "imgui" we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
2405 if (!mouse_avail_to_imgui)
2406 g.HoveredWindow = g.HoveredRootWindow = NULL;
2408 // Scale & Scrolling
2409 if (g.HoveredWindow && g.IO.MouseWheel != 0.0f && !g.HoveredWindow->Collapsed)
2411 ImGuiWindow* window = g.HoveredWindow;
2412 if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
2414 // Zoom / Scale window
2415 const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
2416 const float scale = new_font_scale / window->FontWindowScale;
2417 window->FontWindowScale = new_font_scale;
2419 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
2420 window->Pos += offset;
2421 window->PosFloat += offset;
2422 window->Size *= scale;
2423 window->SizeFull *= scale;
2425 else if (!g.IO.KeyCtrl && !(window->Flags & ImGuiWindowFlags_NoScrollWithMouse))
2428 const int scroll_lines = (window->Flags & ImGuiWindowFlags_ComboBox) ? 3 : 5;
2429 SetWindowScrollY(window, window->Scroll.y - g.IO.MouseWheel * window->CalcFontSize() * scroll_lines);
2433 // Pressing TAB activate widget focus
2434 if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && IsKeyPressedMap(ImGuiKey_Tab, false))
2435 g.NavWindow->FocusIdxTabRequestNext = 0;
2437 // Mark all windows as not visible
2438 for (int i = 0; i != g.Windows.Size; i++)
2440 ImGuiWindow* window = g.Windows[i];
2441 window->WasActive = window->Active;
2442 window->Active = false;
2443 window->WriteAccessed = false;
2446 // Closing the focused window restore focus to the first active root window in descending z-order
2447 if (g.NavWindow && !g.NavWindow->WasActive)
2448 FocusPreviousWindow();
2450 // No window should be open at the beginning of the frame.
2451 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
2452 g.CurrentWindowStack.resize(0);
2453 g.CurrentPopupStack.resize(0);
2454 CloseInactivePopups(g.NavWindow);
2456 // Create implicit window - we will only render it if the user has added something to it.
2457 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
2458 ImGui::SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
2459 ImGui::Begin("Debug##Default");
2462 void ImGui::Initialize()
2464 ImGuiContext& g = *GImGui;
2465 g.LogClipboard = (ImGuiTextBuffer*)ImGui::MemAlloc(sizeof(ImGuiTextBuffer));
2466 IM_PLACEMENT_NEW(g.LogClipboard) ImGuiTextBuffer();
2468 IM_ASSERT(g.Settings.empty());
2469 LoadIniSettingsFromDisk(g.IO.IniFilename);
2470 g.Initialized = true;
2473 // This function is merely here to free heap allocations.
2474 void ImGui::Shutdown()
2476 ImGuiContext& g = *GImGui;
2478 // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame)
2479 if (g.IO.Fonts) // Testing for NULL to allow user to NULLify in case of running Shutdown() on multiple contexts. Bit hacky.
2480 g.IO.Fonts->Clear();
2482 // Cleanup of other data are conditional on actually having initialize ImGui.
2486 SaveIniSettingsToDisk(g.IO.IniFilename);
2488 for (int i = 0; i < g.Windows.Size; i++)
2490 g.Windows[i]->~ImGuiWindow();
2491 ImGui::MemFree(g.Windows[i]);
2494 g.WindowsSortBuffer.clear();
2495 g.CurrentWindow = NULL;
2496 g.CurrentWindowStack.clear();
2497 g.WindowsById.Clear();
2499 g.HoveredWindow = NULL;
2500 g.HoveredRootWindow = NULL;
2501 g.ActiveIdWindow = NULL;
2502 g.MovingWindow = NULL;
2503 for (int i = 0; i < g.Settings.Size; i++)
2504 ImGui::MemFree(g.Settings[i].Name);
2506 g.ColorModifiers.clear();
2507 g.StyleModifiers.clear();
2508 g.FontStack.clear();
2509 g.OpenPopupStack.clear();
2510 g.CurrentPopupStack.clear();
2511 g.SetNextWindowSizeConstraintCallback = NULL;
2512 g.SetNextWindowSizeConstraintCallbackUserData = NULL;
2513 for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2514 g.RenderDrawLists[i].clear();
2515 g.OverlayDrawList.ClearFreeMemory();
2516 g.PrivateClipboard.clear();
2517 g.InputTextState.Text.clear();
2518 g.InputTextState.InitialText.clear();
2519 g.InputTextState.TempTextBuffer.clear();
2521 if (g.LogFile && g.LogFile != stdout)
2528 g.LogClipboard->~ImGuiTextBuffer();
2529 ImGui::MemFree(g.LogClipboard);
2532 g.Initialized = false;
2535 static ImGuiIniData* FindWindowSettings(const char* name)
2537 ImGuiContext& g = *GImGui;
2538 ImGuiID id = ImHash(name, 0);
2539 for (int i = 0; i != g.Settings.Size; i++)
2541 ImGuiIniData* ini = &g.Settings[i];
2548 static ImGuiIniData* AddWindowSettings(const char* name)
2550 GImGui->Settings.resize(GImGui->Settings.Size + 1);
2551 ImGuiIniData* ini = &GImGui->Settings.back();
2552 ini->Name = ImStrdup(name);
2553 ini->Id = ImHash(name, 0);
2554 ini->Collapsed = false;
2555 ini->Pos = ImVec2(FLT_MAX,FLT_MAX);
2556 ini->Size = ImVec2(0,0);
2560 // Zero-tolerance, poor-man .ini parsing
2561 // FIXME: Write something less rubbish
2562 static void LoadIniSettingsFromDisk(const char* ini_filename)
2564 ImGuiContext& g = *GImGui;
2569 char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_size, 1);
2573 ImGuiIniData* settings = NULL;
2574 const char* buf_end = file_data + file_size;
2575 for (const char* line_start = file_data; line_start < buf_end; )
2577 const char* line_end = line_start;
2578 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
2581 if (line_start[0] == '[' && line_end > line_start && line_end[-1] == ']')
2584 ImFormatString(name, IM_ARRAYSIZE(name), "%.*s", (int)(line_end-line_start-2), line_start+1);
2585 settings = FindWindowSettings(name);
2587 settings = AddWindowSettings(name);
2593 if (sscanf(line_start, "Pos=%f,%f", &x, &y) == 2)
2594 settings->Pos = ImVec2(x, y);
2595 else if (sscanf(line_start, "Size=%f,%f", &x, &y) == 2)
2596 settings->Size = ImMax(ImVec2(x, y), g.Style.WindowMinSize);
2597 else if (sscanf(line_start, "Collapsed=%d", &i) == 1)
2598 settings->Collapsed = (i != 0);
2601 line_start = line_end+1;
2604 ImGui::MemFree(file_data);
2607 static void SaveIniSettingsToDisk(const char* ini_filename)
2609 ImGuiContext& g = *GImGui;
2610 g.SettingsDirtyTimer = 0.0f;
2614 // Gather data from windows that were active during this session
2615 for (int i = 0; i != g.Windows.Size; i++)
2617 ImGuiWindow* window = g.Windows[i];
2618 if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
2620 ImGuiIniData* settings = FindWindowSettings(window->Name);
2621 if (!settings) // This will only return NULL in the rare instance where the window was first created with ImGuiWindowFlags_NoSavedSettings then had the flag disabled later on. We don't bind settings in this case (bug #1000).
2623 settings->Pos = window->Pos;
2624 settings->Size = window->SizeFull;
2625 settings->Collapsed = window->Collapsed;
2629 // If a window wasn't opened in this session we preserve its settings
2630 FILE* f = ImFileOpen(ini_filename, "wt");
2633 for (int i = 0; i != g.Settings.Size; i++)
2635 const ImGuiIniData* settings = &g.Settings[i];
2636 if (settings->Pos.x == FLT_MAX)
2638 const char* name = settings->Name;
2639 if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
2641 fprintf(f, "[%s]\n", name);
2642 fprintf(f, "Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
2643 fprintf(f, "Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
2644 fprintf(f, "Collapsed=%d\n", settings->Collapsed);
2651 static void MarkIniSettingsDirty(ImGuiWindow* window)
2653 ImGuiContext& g = *GImGui;
2654 if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
2655 if (g.SettingsDirtyTimer <= 0.0f)
2656 g.SettingsDirtyTimer = g.IO.IniSavingRate;
2659 // FIXME: Add a more explicit sort order in the window structure.
2660 static int ChildWindowComparer(const void* lhs, const void* rhs)
2662 const ImGuiWindow* a = *(const ImGuiWindow**)lhs;
2663 const ImGuiWindow* b = *(const ImGuiWindow**)rhs;
2664 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
2666 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
2668 if (int d = (a->Flags & ImGuiWindowFlags_ComboBox) - (b->Flags & ImGuiWindowFlags_ComboBox))
2670 return (a->OrderWithinParent - b->OrderWithinParent);
2673 static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>& out_sorted_windows, ImGuiWindow* window)
2675 out_sorted_windows.push_back(window);
2678 int count = window->DC.ChildWindows.Size;
2680 qsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
2681 for (int i = 0; i < count; i++)
2683 ImGuiWindow* child = window->DC.ChildWindows[i];
2685 AddWindowToSortedBuffer(out_sorted_windows, child);
2690 static void AddDrawListToRenderList(ImVector<ImDrawList*>& out_render_list, ImDrawList* draw_list)
2692 if (draw_list->CmdBuffer.empty())
2695 // Remove trailing command if unused
2696 ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
2697 if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
2699 draw_list->CmdBuffer.pop_back();
2700 if (draw_list->CmdBuffer.empty())
2704 // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. May trigger for you if you are using PrimXXX functions incorrectly.
2705 IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
2706 IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
2707 IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
2709 // Check that draw_list doesn't use more vertices than indexable in a single draw call (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per window)
2710 // If this assert triggers because you are drawing lots of stuff manually, you can:
2711 // A) Add '#define ImDrawIdx unsigned int' in imconfig.h to set the index size to 4 bytes. You'll need to handle the 4-bytes indices to your renderer.
2712 // For example, the OpenGL example code detect index size at compile-time by doing:
2713 // 'glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);'
2714 // Your own engine or render API may use different parameters or function calls to specify index sizes. 2 and 4 bytes indices are generally supported by most API.
2715 // B) If for some reason you cannot use 4 bytes indices or don't want to, a workaround is to call BeginChild()/EndChild() before reaching the 64K limit to split your draw commands in multiple draw lists.
2716 IM_ASSERT(((ImU64)draw_list->_VtxCurrentIdx >> (sizeof(ImDrawIdx)*8)) == 0); // Too many vertices in same ImDrawList. See comment above.
2718 out_render_list.push_back(draw_list);
2719 GImGui->IO.MetricsRenderVertices += draw_list->VtxBuffer.Size;
2720 GImGui->IO.MetricsRenderIndices += draw_list->IdxBuffer.Size;
2723 static void AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiWindow* window)
2725 AddDrawListToRenderList(out_render_list, window->DrawList);
2726 for (int i = 0; i < window->DC.ChildWindows.Size; i++)
2728 ImGuiWindow* child = window->DC.ChildWindows[i];
2729 if (!child->Active) // clipped children may have been marked not active
2731 if ((child->Flags & ImGuiWindowFlags_Popup) && child->HiddenFrames > 0)
2733 AddWindowToRenderList(out_render_list, child);
2737 static void AddWindowToRenderListSelectLayer(ImGuiWindow* window)
2739 // FIXME: Generalize this with a proper layering system so e.g. user can draw in specific layers, below text, ..
2740 ImGuiContext& g = *GImGui;
2741 g.IO.MetricsActiveWindows++;
2742 if (window->Flags & ImGuiWindowFlags_Popup)
2743 AddWindowToRenderList(g.RenderDrawLists[1], window);
2744 else if (window->Flags & ImGuiWindowFlags_Tooltip)
2745 AddWindowToRenderList(g.RenderDrawLists[2], window);
2747 AddWindowToRenderList(g.RenderDrawLists[0], window);
2750 // When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result.
2751 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
2753 ImGuiWindow* window = GetCurrentWindow();
2754 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
2755 window->ClipRect = window->DrawList->_ClipRectStack.back();
2758 void ImGui::PopClipRect()
2760 ImGuiWindow* window = GetCurrentWindow();
2761 window->DrawList->PopClipRect();
2762 window->ClipRect = window->DrawList->_ClipRectStack.back();
2765 // This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal.
2766 void ImGui::EndFrame()
2768 ImGuiContext& g = *GImGui;
2769 IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame()
2770 if (g.FrameCountEnded == g.FrameCount) // Don't process EndFrame() multiple times.
2773 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
2774 if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.OsImePosRequest - g.OsImePosSet) > 0.0001f)
2776 g.IO.ImeSetInputScreenPosFn((int)g.OsImePosRequest.x, (int)g.OsImePosRequest.y);
2777 g.OsImePosSet = g.OsImePosRequest;
2780 // Hide implicit "Debug" window if it hasn't been used
2781 IM_ASSERT(g.CurrentWindowStack.Size == 1); // Mismatched Begin()/End() calls
2782 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
2783 g.CurrentWindow->Active = false;
2786 if (g.ActiveId == 0 && g.HoveredId == 0)
2788 if (!g.NavWindow || !g.NavWindow->Appearing) // Unless we just made a window/popup appear
2790 // Click to focus window and start moving (after we're done with all our widgets)
2791 if (g.IO.MouseClicked[0])
2793 if (g.HoveredRootWindow != NULL)
2795 FocusWindow(g.HoveredWindow);
2796 if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove) && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoMove))
2798 g.MovingWindow = g.HoveredWindow;
2799 g.MovingWindowMoveId = g.MovingWindow->MoveId;
2800 SetActiveID(g.MovingWindowMoveId, g.HoveredRootWindow);
2803 else if (g.NavWindow != NULL && GetFrontMostModalRootWindow() == NULL)
2805 // Clicking on void disable focus
2810 // With right mouse button we close popups without changing focus
2811 // (The left mouse button path calls FocusWindow which will lead NewFrame->CloseInactivePopups to trigger)
2812 if (g.IO.MouseClicked[1])
2814 // Find the top-most window between HoveredWindow and the front most Modal Window.
2815 // This is where we can trim the popup stack.
2816 ImGuiWindow* modal = GetFrontMostModalRootWindow();
2817 bool hovered_window_above_modal = false;
2819 hovered_window_above_modal = true;
2820 for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
2822 ImGuiWindow* window = g.Windows[i];
2823 if (window == modal)
2825 if (window == g.HoveredWindow)
2826 hovered_window_above_modal = true;
2828 CloseInactivePopups(hovered_window_above_modal ? g.HoveredWindow : modal);
2833 // Sort the window list so that all child windows are after their parent
2834 // We cannot do that on FocusWindow() because childs may not exist yet
2835 g.WindowsSortBuffer.resize(0);
2836 g.WindowsSortBuffer.reserve(g.Windows.Size);
2837 for (int i = 0; i != g.Windows.Size; i++)
2839 ImGuiWindow* window = g.Windows[i];
2840 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
2842 AddWindowToSortedBuffer(g.WindowsSortBuffer, window);
2845 IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong
2846 g.Windows.swap(g.WindowsSortBuffer);
2848 // Clear Input data for next frame
2849 g.IO.MouseWheel = 0.0f;
2850 memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
2852 g.FrameCountEnded = g.FrameCount;
2855 void ImGui::Render()
2857 ImGuiContext& g = *GImGui;
2858 IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame()
2860 if (g.FrameCountEnded != g.FrameCount)
2862 g.FrameCountRendered = g.FrameCount;
2864 // Skip render altogether if alpha is 0.0
2865 // Note that vertex buffers have been created and are wasted, so it is best practice that you don't create windows in the first place, or consistently respond to Begin() returning false.
2866 if (g.Style.Alpha > 0.0f)
2868 // Gather windows to render
2869 g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0;
2870 for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2871 g.RenderDrawLists[i].resize(0);
2872 for (int i = 0; i != g.Windows.Size; i++)
2874 ImGuiWindow* window = g.Windows[i];
2875 if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0)
2876 AddWindowToRenderListSelectLayer(window);
2880 int n = g.RenderDrawLists[0].Size;
2881 int flattened_size = n;
2882 for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2883 flattened_size += g.RenderDrawLists[i].Size;
2884 g.RenderDrawLists[0].resize(flattened_size);
2885 for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2887 ImVector<ImDrawList*>& layer = g.RenderDrawLists[i];
2890 memcpy(&g.RenderDrawLists[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
2894 // Draw software mouse cursor if requested
2895 if (g.IO.MouseDrawCursor)
2897 const ImGuiMouseCursorData& cursor_data = g.MouseCursorData[g.MouseCursor];
2898 const ImVec2 pos = g.IO.MousePos - cursor_data.HotOffset;
2899 const ImVec2 size = cursor_data.Size;
2900 const ImTextureID tex_id = g.IO.Fonts->TexID;
2901 g.OverlayDrawList.PushTextureID(tex_id);
2902 g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(1,0), pos+ImVec2(1,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,48)); // Shadow
2903 g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(2,0), pos+ImVec2(2,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,48)); // Shadow
2904 g.OverlayDrawList.AddImage(tex_id, pos, pos + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,255)); // Black border
2905 g.OverlayDrawList.AddImage(tex_id, pos, pos + size, cursor_data.TexUvMin[0], cursor_data.TexUvMax[0], IM_COL32(255,255,255,255)); // White fill
2906 g.OverlayDrawList.PopTextureID();
2908 if (!g.OverlayDrawList.VtxBuffer.empty())
2909 AddDrawListToRenderList(g.RenderDrawLists[0], &g.OverlayDrawList);
2912 g.RenderDrawData.Valid = true;
2913 g.RenderDrawData.CmdLists = (g.RenderDrawLists[0].Size > 0) ? &g.RenderDrawLists[0][0] : NULL;
2914 g.RenderDrawData.CmdListsCount = g.RenderDrawLists[0].Size;
2915 g.RenderDrawData.TotalVtxCount = g.IO.MetricsRenderVertices;
2916 g.RenderDrawData.TotalIdxCount = g.IO.MetricsRenderIndices;
2918 // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData()
2919 if (g.RenderDrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
2920 g.IO.RenderDrawListsFn(&g.RenderDrawData);
2924 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2926 const char* text_display_end = text;
2928 text_end = (const char*)-1;
2930 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2932 return text_display_end;
2935 // Pass text data straight to log (without being displayed)
2936 void ImGui::LogText(const char* fmt, ...)
2938 ImGuiContext& g = *GImGui;
2943 va_start(args, fmt);
2946 vfprintf(g.LogFile, fmt, args);
2950 g.LogClipboard->appendv(fmt, args);
2955 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
2956 // We split text into individual lines to add current tree level padding
2957 static void LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL)
2959 ImGuiContext& g = *GImGui;
2960 ImGuiWindow* window = g.CurrentWindow;
2963 text_end = ImGui::FindRenderedTextEnd(text, text_end);
2965 const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1);
2967 window->DC.LogLinePosY = ref_pos->y;
2969 const char* text_remaining = text;
2970 if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth
2971 g.LogStartDepth = window->DC.TreeDepth;
2972 const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth);
2975 // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
2976 const char* line_end = text_remaining;
2977 while (line_end < text_end)
2978 if (*line_end == '\n')
2982 if (line_end >= text_end)
2985 const bool is_first_line = (text == text_remaining);
2986 bool is_last_line = false;
2987 if (line_end == NULL)
2989 is_last_line = true;
2990 line_end = text_end;
2992 if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0))
2994 const int char_count = (int)(line_end - text_remaining);
2995 if (log_new_line || !is_first_line)
2996 ImGui::LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining);
2998 ImGui::LogText(" %.*s", char_count, text_remaining);
3003 text_remaining = line_end + 1;
3007 // Internal ImGui functions to render text
3008 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
3009 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
3011 ImGuiContext& g = *GImGui;
3012 ImGuiWindow* window = g.CurrentWindow;
3014 // Hide anything after a '##' string
3015 const char* text_display_end;
3016 if (hide_text_after_hash)
3018 text_display_end = FindRenderedTextEnd(text, text_end);
3023 text_end = text + strlen(text); // FIXME-OPT
3024 text_display_end = text_end;
3027 const int text_len = (int)(text_display_end - text);
3030 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
3032 LogRenderedText(&pos, text, text_display_end);
3036 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
3038 ImGuiContext& g = *GImGui;
3039 ImGuiWindow* window = g.CurrentWindow;
3042 text_end = text + strlen(text); // FIXME-OPT
3044 const int text_len = (int)(text_end - text);
3047 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
3049 LogRenderedText(&pos, text, text_end);
3053 // Default clip_rect uses (pos_min,pos_max)
3054 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
3055 void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
3057 // Hide anything after a '##' string
3058 const char* text_display_end = FindRenderedTextEnd(text, text_end);
3059 const int text_len = (int)(text_display_end - text);
3063 ImGuiContext& g = *GImGui;
3064 ImGuiWindow* window = g.CurrentWindow;
3066 // Perform CPU side clipping for single clipped element to avoid using scissor state
3067 ImVec2 pos = pos_min;
3068 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
3070 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
3071 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
3072 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
3073 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
3074 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
3076 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
3077 if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
3078 if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
3083 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
3084 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
3088 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
3091 LogRenderedText(&pos, text, text_display_end);
3094 // Render a rectangle shaped with optional rounding and borders
3095 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
3097 ImGuiContext& g = *GImGui;
3098 ImGuiWindow* window = g.CurrentWindow;
3099 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
3100 const float border_size = g.Style.FrameBorderSize;
3101 if (border && border_size > 0.0f)
3103 window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
3104 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
3108 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
3110 ImGuiContext& g = *GImGui;
3111 ImGuiWindow* window = g.CurrentWindow;
3112 const float border_size = g.Style.FrameBorderSize;
3113 if (border_size > 0.0f)
3115 window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
3116 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
3120 // Render a triangle to denote expanded/collapsed state
3121 void ImGui::RenderTriangle(ImVec2 p_min, ImGuiDir dir, float scale)
3123 ImGuiContext& g = *GImGui;
3124 ImGuiWindow* window = g.CurrentWindow;
3126 const float h = g.FontSize * 1.00f;
3127 float r = h * 0.40f * scale;
3128 ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale);
3134 r = -r; // ...fall through, no break!
3136 center.y -= r * 0.25f;
3137 a = ImVec2(0,1) * r;
3138 b = ImVec2(-0.866f,-0.5f) * r;
3139 c = ImVec2(+0.866f,-0.5f) * r;
3142 r = -r; // ...fall through, no break!
3143 case ImGuiDir_Right:
3144 center.x -= r * 0.25f;
3145 a = ImVec2(1,0) * r;
3146 b = ImVec2(-0.500f,+0.866f) * r;
3147 c = ImVec2(-0.500f,-0.866f) * r;
3154 window->DrawList->AddTriangleFilled(center + a, center + b, center + c, GetColorU32(ImGuiCol_Text));
3157 void ImGui::RenderBullet(ImVec2 pos)
3159 ImGuiContext& g = *GImGui;
3160 ImGuiWindow* window = g.CurrentWindow;
3161 window->DrawList->AddCircleFilled(pos, GImGui->FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8);
3164 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
3166 ImGuiContext& g = *GImGui;
3167 ImGuiWindow* window = g.CurrentWindow;
3169 float thickness = ImMax(sz / 5.0f, 1.0f);
3170 sz -= thickness*0.5f;
3171 pos += ImVec2(thickness*0.25f, thickness*0.25f);
3173 float third = sz / 3.0f;
3174 float bx = pos.x + third;
3175 float by = pos.y + sz - third*0.5f;
3176 window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
3177 window->DrawList->PathLineTo(ImVec2(bx, by));
3178 window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2));
3179 window->DrawList->PathStroke(col, false, thickness);
3182 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
3183 // CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize)
3184 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
3186 ImGuiContext& g = *GImGui;
3188 const char* text_display_end;
3189 if (hide_text_after_double_hash)
3190 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
3192 text_display_end = text_end;
3194 ImFont* font = g.Font;
3195 const float font_size = g.FontSize;
3196 if (text == text_display_end)
3197 return ImVec2(0.0f, font_size);
3198 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
3200 // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field)
3201 const float font_scale = font_size / font->FontSize;
3202 const float character_spacing_x = 1.0f * font_scale;
3203 if (text_size.x > 0.0f)
3204 text_size.x -= character_spacing_x;
3205 text_size.x = (float)(int)(text_size.x + 0.95f);
3210 // Helper to calculate coarse clipping of large list of evenly sized items.
3211 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
3212 // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX
3213 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
3215 ImGuiContext& g = *GImGui;
3216 ImGuiWindow* window = g.CurrentWindow;
3219 // If logging is active, do not perform any clipping
3220 *out_items_display_start = 0;
3221 *out_items_display_end = items_count;
3224 if (window->SkipItems)
3226 *out_items_display_start = *out_items_display_end = 0;
3230 const ImVec2 pos = window->DC.CursorPos;
3231 int start = (int)((window->ClipRect.Min.y - pos.y) / items_height);
3232 int end = (int)((window->ClipRect.Max.y - pos.y) / items_height);
3233 start = ImClamp(start, 0, items_count);
3234 end = ImClamp(end + 1, start, items_count);
3235 *out_items_display_start = start;
3236 *out_items_display_end = end;
3239 // Find window given position, search front-to-back
3240 // FIXME: Note that we have a lag here because WindowRectClipped is updated in Begin() so windows moved by user via SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is called, aka before the next Begin(). Moving window thankfully isn't affected.
3241 static ImGuiWindow* FindHoveredWindow(ImVec2 pos)
3243 ImGuiContext& g = *GImGui;
3244 for (int i = g.Windows.Size-1; i >= 0; i--)
3246 ImGuiWindow* window = g.Windows[i];
3247 if (!window->Active)
3249 if (window->Flags & ImGuiWindowFlags_NoInputs)
3252 // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
3253 ImRect bb(window->WindowRectClipped.Min - g.Style.TouchExtraPadding, window->WindowRectClipped.Max + g.Style.TouchExtraPadding);
3254 if (bb.Contains(pos))
3260 // Test if mouse cursor is hovering given rectangle
3261 // NB- Rectangle is clipped by our current clip setting
3262 // NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
3263 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
3265 ImGuiContext& g = *GImGui;
3266 ImGuiWindow* window = g.CurrentWindow;
3269 ImRect rect_clipped(r_min, r_max);
3271 rect_clipped.ClipWith(window->ClipRect);
3273 // Expand for touch input
3274 const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
3275 return rect_for_touch.Contains(g.IO.MousePos);
3278 bool ImGui::IsAnyWindowHovered()
3280 ImGuiContext& g = *GImGui;
3281 return g.HoveredWindow != NULL;
3284 static bool IsKeyPressedMap(ImGuiKey key, bool repeat)
3286 const int key_index = GImGui->IO.KeyMap[key];
3287 return (key_index >= 0) ? ImGui::IsKeyPressed(key_index, repeat) : false;
3290 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
3292 IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
3293 return GImGui->IO.KeyMap[imgui_key];
3296 // Note that imgui doesn't know the semantic of each entry of io.KeyDown[]. Use your own indices/enums according to how your backend/engine stored them into KeyDown[]!
3297 bool ImGui::IsKeyDown(int user_key_index)
3299 if (user_key_index < 0) return false;
3300 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown));
3301 return GImGui->IO.KeysDown[user_key_index];
3304 int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate)
3308 if (t <= repeat_delay || repeat_rate <= 0.0f)
3310 const int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t_prev - repeat_delay) / repeat_rate);
3311 return (count > 0) ? count : 0;
3314 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
3316 ImGuiContext& g = *GImGui;
3317 if (key_index < 0) return false;
3318 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3319 const float t = g.IO.KeysDownDuration[key_index];
3320 return CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, repeat_delay, repeat_rate);
3323 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
3325 ImGuiContext& g = *GImGui;
3326 if (user_key_index < 0) return false;
3327 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3328 const float t = g.IO.KeysDownDuration[user_key_index];
3331 if (repeat && t > g.IO.KeyRepeatDelay)
3332 return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
3336 bool ImGui::IsKeyReleased(int user_key_index)
3338 ImGuiContext& g = *GImGui;
3339 if (user_key_index < 0) return false;
3340 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3341 if (g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index])
3346 bool ImGui::IsMouseDown(int button)
3348 ImGuiContext& g = *GImGui;
3349 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3350 return g.IO.MouseDown[button];
3353 bool ImGui::IsMouseClicked(int button, bool repeat)
3355 ImGuiContext& g = *GImGui;
3356 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3357 const float t = g.IO.MouseDownDuration[button];
3361 if (repeat && t > g.IO.KeyRepeatDelay)
3363 float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
3364 if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
3371 bool ImGui::IsMouseReleased(int button)
3373 ImGuiContext& g = *GImGui;
3374 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3375 return g.IO.MouseReleased[button];
3378 bool ImGui::IsMouseDoubleClicked(int button)
3380 ImGuiContext& g = *GImGui;
3381 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3382 return g.IO.MouseDoubleClicked[button];
3385 bool ImGui::IsMouseDragging(int button, float lock_threshold)
3387 ImGuiContext& g = *GImGui;
3388 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3389 if (!g.IO.MouseDown[button])
3391 if (lock_threshold < 0.0f)
3392 lock_threshold = g.IO.MouseDragThreshold;
3393 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
3396 ImVec2 ImGui::GetMousePos()
3398 return GImGui->IO.MousePos;
3401 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
3402 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
3404 ImGuiContext& g = *GImGui;
3405 if (g.CurrentPopupStack.Size > 0)
3406 return g.OpenPopupStack[g.CurrentPopupStack.Size-1].MousePosOnOpen;
3407 return g.IO.MousePos;
3410 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position
3411 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
3413 if (mouse_pos == NULL)
3414 mouse_pos = &GImGui->IO.MousePos;
3415 const float MOUSE_INVALID = -256000.0f;
3416 return mouse_pos->x >= MOUSE_INVALID && mouse_pos->y >= MOUSE_INVALID;
3419 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
3421 ImGuiContext& g = *GImGui;
3422 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3423 if (lock_threshold < 0.0f)
3424 lock_threshold = g.IO.MouseDragThreshold;
3425 if (g.IO.MouseDown[button])
3426 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
3427 return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment).
3428 return ImVec2(0.0f, 0.0f);
3431 void ImGui::ResetMouseDragDelta(int button)
3433 ImGuiContext& g = *GImGui;
3434 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3435 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
3436 g.IO.MouseClickedPos[button] = g.IO.MousePos;
3439 ImGuiMouseCursor ImGui::GetMouseCursor()
3441 return GImGui->MouseCursor;
3444 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
3446 GImGui->MouseCursor = cursor_type;
3449 void ImGui::CaptureKeyboardFromApp(bool capture)
3451 GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
3454 void ImGui::CaptureMouseFromApp(bool capture)
3456 GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
3459 bool ImGui::IsItemActive()
3461 ImGuiContext& g = *GImGui;
3464 ImGuiWindow* window = g.CurrentWindow;
3465 return g.ActiveId == window->DC.LastItemId;
3470 bool ImGui::IsItemClicked(int mouse_button)
3472 return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_Default);
3475 bool ImGui::IsAnyItemHovered()
3477 return GImGui->HoveredId != 0 || GImGui->HoveredIdPreviousFrame != 0;
3480 bool ImGui::IsAnyItemActive()
3482 return GImGui->ActiveId != 0;
3485 bool ImGui::IsItemVisible()
3487 ImGuiWindow* window = GetCurrentWindowRead();
3488 return window->ClipRect.Overlaps(window->DC.LastItemRect);
3491 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
3492 void ImGui::SetItemAllowOverlap()
3494 ImGuiContext& g = *GImGui;
3495 if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
3496 g.HoveredIdAllowOverlap = true;
3497 if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
3498 g.ActiveIdAllowOverlap = true;
3501 ImVec2 ImGui::GetItemRectMin()
3503 ImGuiWindow* window = GetCurrentWindowRead();
3504 return window->DC.LastItemRect.Min;
3507 ImVec2 ImGui::GetItemRectMax()
3509 ImGuiWindow* window = GetCurrentWindowRead();
3510 return window->DC.LastItemRect.Max;
3513 ImVec2 ImGui::GetItemRectSize()
3515 ImGuiWindow* window = GetCurrentWindowRead();
3516 return window->DC.LastItemRect.GetSize();
3519 ImVec2 ImGui::CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge, float outward)
3521 ImGuiWindow* window = GetCurrentWindowRead();
3522 ImRect rect = window->DC.LastItemRect;
3523 rect.Expand(outward);
3524 return rect.GetClosestPoint(pos, on_edge);
3527 static ImRect GetVisibleRect()
3529 ImGuiContext& g = *GImGui;
3530 if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y)
3531 return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax);
3532 return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3535 // Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first.
3536 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip)
3538 ImGuiContext& g = *GImGui;
3539 char window_name[16];
3540 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip%02d", g.TooltipOverrideCount);
3541 if (override_previous_tooltip)
3542 if (ImGuiWindow* window = ImGui::FindWindowByName(window_name))
3545 // Hide previous tooltips. We can't easily "reset" the content of a window so we create a new one.
3546 window->HiddenFrames = 1;
3547 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip%02d", ++g.TooltipOverrideCount);
3549 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
3550 ImGui::Begin(window_name, NULL, flags | extra_flags);
3553 void ImGui::SetTooltipV(const char* fmt, va_list args)
3555 BeginTooltipEx(0, true);
3560 void ImGui::SetTooltip(const char* fmt, ...)
3563 va_start(args, fmt);
3564 SetTooltipV(fmt, args);
3568 void ImGui::BeginTooltip()
3570 BeginTooltipEx(0, false);
3573 void ImGui::EndTooltip()
3575 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
3579 // Mark popup as open (toggle toward open state).
3580 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
3581 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
3582 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
3583 void ImGui::OpenPopupEx(ImGuiID id, bool reopen_existing)
3585 ImGuiContext& g = *GImGui;
3586 ImGuiWindow* window = g.CurrentWindow;
3587 int current_stack_size = g.CurrentPopupStack.Size;
3588 ImGuiPopupRef popup_ref = ImGuiPopupRef(id, window, window->GetID("##menus"), g.IO.MousePos); // Tagged as new ref because constructor sets Window to NULL (we are passing the ParentWindow info here)
3589 if (g.OpenPopupStack.Size < current_stack_size + 1)
3590 g.OpenPopupStack.push_back(popup_ref);
3591 else if (reopen_existing || g.OpenPopupStack[current_stack_size].PopupId != id)
3593 g.OpenPopupStack.resize(current_stack_size+1);
3594 g.OpenPopupStack[current_stack_size] = popup_ref;
3596 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by CloseInactivePopups().
3597 // This is equivalent to what ClosePopupToLevel() does.
3598 if (g.OpenPopupStack[current_stack_size].PopupId == id)
3599 FocusWindow(window);
3603 void ImGui::OpenPopup(const char* str_id)
3605 ImGuiContext& g = *GImGui;
3606 OpenPopupEx(g.CurrentWindow->GetID(str_id), false);
3609 static void CloseInactivePopups(ImGuiWindow* ref_window)
3611 ImGuiContext& g = *GImGui;
3612 if (g.OpenPopupStack.empty())
3615 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
3616 // Don't close our own child popup windows.
3620 for (n = 0; n < g.OpenPopupStack.Size; n++)
3622 ImGuiPopupRef& popup = g.OpenPopupStack[n];
3625 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
3626 if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
3629 // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow)
3630 bool has_focus = false;
3631 for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++)
3632 has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow);
3637 if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the block below
3638 ClosePopupToLevel(n);
3641 static ImGuiWindow* GetFrontMostModalRootWindow()
3643 ImGuiContext& g = *GImGui;
3644 for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
3645 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
3646 if (popup->Flags & ImGuiWindowFlags_Modal)
3651 static void ClosePopupToLevel(int remaining)
3653 ImGuiContext& g = *GImGui;
3655 ImGui::FocusWindow(g.OpenPopupStack[remaining-1].Window);
3657 ImGui::FocusWindow(g.OpenPopupStack[0].ParentWindow);
3658 g.OpenPopupStack.resize(remaining);
3661 void ImGui::ClosePopup(ImGuiID id)
3663 if (!IsPopupOpen(id))
3665 ImGuiContext& g = *GImGui;
3666 ClosePopupToLevel(g.OpenPopupStack.Size - 1);
3669 // Close the popup we have begin-ed into.
3670 void ImGui::CloseCurrentPopup()
3672 ImGuiContext& g = *GImGui;
3673 int popup_idx = g.CurrentPopupStack.Size - 1;
3674 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
3676 while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu))
3678 ClosePopupToLevel(popup_idx);
3681 static inline void ClearSetNextWindowData()
3684 ImGuiContext& g = *GImGui;
3685 g.SetNextWindowPosCond = g.SetNextWindowSizeCond = g.SetNextWindowContentSizeCond = g.SetNextWindowCollapsedCond = 0;
3686 g.SetNextWindowSizeConstraint = g.SetNextWindowFocus = false;
3689 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
3691 ImGuiContext& g = *GImGui;
3692 if (!IsPopupOpen(id))
3694 ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3698 ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings;
3701 if (flags & ImGuiWindowFlags_ChildMenu)
3702 ImFormatString(name, IM_ARRAYSIZE(name), "##menu_%d", g.CurrentPopupStack.Size); // Recycle windows based on depth
3704 ImFormatString(name, IM_ARRAYSIZE(name), "##popup_%08x", id); // Not recycling, so we can close/open during the same frame
3706 bool is_open = Begin(name, NULL, flags);
3707 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
3713 bool ImGui::BeginPopup(const char* str_id)
3715 ImGuiContext& g = *GImGui;
3716 if (g.OpenPopupStack.Size <= g.CurrentPopupStack.Size) // Early out for performance
3718 ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3721 return BeginPopupEx(g.CurrentWindow->GetID(str_id), ImGuiWindowFlags_AlwaysAutoResize);
3724 bool ImGui::IsPopupOpen(ImGuiID id)
3726 ImGuiContext& g = *GImGui;
3727 return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id;
3730 bool ImGui::IsPopupOpen(const char* str_id)
3732 ImGuiContext& g = *GImGui;
3733 return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
3736 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags extra_flags)
3738 ImGuiContext& g = *GImGui;
3739 ImGuiWindow* window = g.CurrentWindow;
3740 const ImGuiID id = window->GetID(name);
3741 if (!IsPopupOpen(id))
3743 ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3747 // Center modal windows by default
3748 if ((window->SetWindowPosAllowFlags & g.SetNextWindowPosCond) == 0)
3749 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
3751 ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoSavedSettings;
3752 bool is_open = Begin(name, p_open, flags);
3753 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
3764 void ImGui::EndPopup()
3766 ImGuiContext& g = *GImGui; (void)g;
3767 IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
3768 IM_ASSERT(g.CurrentPopupStack.Size > 0);
3772 bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
3774 ImGuiWindow* window = GImGui->CurrentWindow;
3775 if (IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
3777 ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
3778 IM_ASSERT(id != 0); // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
3779 OpenPopupEx(id, true);
3785 // This is a helper to handle the simplest case of associating one named popup to one given widget.
3786 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
3787 // You can pass a NULL str_id to use the identifier of the last item.
3788 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
3790 ImGuiWindow* window = GImGui->CurrentWindow;
3791 ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
3792 IM_ASSERT(id != 0); // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
3793 if (IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
3794 OpenPopupEx(id, true);
3795 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize);
3798 bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
3801 str_id = "window_context";
3802 ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
3803 if (IsMouseClicked(mouse_button))
3804 if (IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
3805 if (also_over_items || !IsAnyItemHovered())
3806 OpenPopupEx(id, true);
3807 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize);
3810 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
3813 str_id = "void_context";
3814 ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
3815 if (!IsAnyWindowHovered() && IsMouseClicked(mouse_button))
3816 OpenPopupEx(id, true);
3817 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize);
3820 static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
3822 ImGuiContext& g = *GImGui;
3823 ImGuiWindow* parent_window = ImGui::GetCurrentWindow();
3824 ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
3825 flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
3827 const ImVec2 content_avail = ImGui::GetContentRegionAvail();
3828 ImVec2 size = ImFloor(size_arg);
3829 const int auto_fit_axises = ((size.x == 0.0f) ? 0x01 : 0x00) | ((size.y == 0.0f) ? 0x02 : 0x00);
3831 size.x = ImMax(content_avail.x, 4.0f) - fabsf(size.x); // Arbitrary minimum zero-ish child size of 4.0f (0.0f causing too much issues)
3833 size.y = ImMax(content_avail.y, 4.0f) - fabsf(size.y);
3835 const float backup_border_size = g.Style.ChildBorderSize;
3837 g.Style.ChildBorderSize = 0.0f;
3838 flags |= extra_flags;
3842 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
3844 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
3846 ImGui::SetNextWindowSize(size);
3847 bool ret = ImGui::Begin(title, NULL, flags);
3848 ImGuiWindow* child_window = ImGui::GetCurrentWindow();
3849 child_window->AutoFitChildAxises = auto_fit_axises;
3850 g.Style.ChildBorderSize = backup_border_size;
3855 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
3857 ImGuiWindow* window = GetCurrentWindow();
3858 return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
3861 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
3863 return BeginChildEx(NULL, id, size_arg, border, extra_flags);
3866 void ImGui::EndChild()
3868 ImGuiWindow* window = GetCurrentWindow();
3870 IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() callss
3871 if ((window->Flags & ImGuiWindowFlags_ComboBox) || window->BeginCount > 1)
3877 // When using auto-filling child window, we don't provide full width/height to ItemSize so that it doesn't feed back into automatic size-fitting.
3878 ImVec2 sz = GetWindowSize();
3879 if (window->AutoFitChildAxises & 0x01) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
3880 sz.x = ImMax(4.0f, sz.x);
3881 if (window->AutoFitChildAxises & 0x02)
3882 sz.y = ImMax(4.0f, sz.y);
3885 ImGuiWindow* parent_window = GetCurrentWindow();
3886 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
3892 // Helper to create a child window / scrolling region that looks like a normal widget frame.
3893 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
3895 ImGuiContext& g = *GImGui;
3896 const ImGuiStyle& style = g.Style;
3897 PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
3898 PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
3899 PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
3900 PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
3901 return BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
3904 void ImGui::EndChildFrame()
3911 // Save and compare stack sizes on Begin()/End() to detect usage errors
3912 static void CheckStacksSize(ImGuiWindow* window, bool write)
3914 // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
3915 ImGuiContext& g = *GImGui;
3916 int* p_backup = &window->DC.StackSizesBackup[0];
3917 { int current = window->IDStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!"); p_backup++; } // Too few or too many PopID()/TreePop()
3918 { int current = window->DC.GroupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!"); p_backup++; } // Too few or too many EndGroup()
3919 { int current = g.CurrentPopupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup()
3920 { int current = g.ColorModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleColor/PopStyleColor Mismatch!"); p_backup++; } // Too few or too many PopStyleColor()
3921 { int current = g.StyleModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleVar/PopStyleVar Mismatch!"); p_backup++; } // Too few or too many PopStyleVar()
3922 { int current = g.FontStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushFont/PopFont Mismatch!"); p_backup++; } // Too few or too many PopFont()
3923 IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
3926 static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& r_inner)
3928 const ImGuiStyle& style = GImGui->Style;
3930 // Clamp into visible area while not overlapping the cursor. Safety padding is optional if our popup size won't fit without it.
3931 ImVec2 safe_padding = style.DisplaySafeAreaPadding;
3932 ImRect r_outer(GetVisibleRect());
3933 r_outer.Expand(ImVec2((size.x - r_outer.GetWidth() > safe_padding.x*2) ? -safe_padding.x : 0.0f, (size.y - r_outer.GetHeight() > safe_padding.y*2) ? -safe_padding.y : 0.0f));
3934 ImVec2 base_pos_clamped = ImClamp(base_pos, r_outer.Min, r_outer.Max - size);
3936 for (int n = (*last_dir != -1) ? -1 : 0; n < 4; n++) // Last, Right, down, up, left. (Favor last used direction).
3938 const int dir = (n == -1) ? *last_dir : n;
3939 ImRect rect(dir == 0 ? r_inner.Max.x : r_outer.Min.x, dir == 1 ? r_inner.Max.y : r_outer.Min.y, dir == 3 ? r_inner.Min.x : r_outer.Max.x, dir == 2 ? r_inner.Min.y : r_outer.Max.y);
3940 if (rect.GetWidth() < size.x || rect.GetHeight() < size.y)
3943 return ImVec2(dir == 0 ? r_inner.Max.x : dir == 3 ? r_inner.Min.x - size.x : base_pos_clamped.x, dir == 1 ? r_inner.Max.y : dir == 2 ? r_inner.Min.y - size.y : base_pos_clamped.y);
3946 // Fallback, try to keep within display
3948 ImVec2 pos = base_pos;
3949 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
3950 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
3954 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
3956 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
3957 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
3958 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
3961 ImGuiWindow* ImGui::FindWindowByName(const char* name)
3963 ImGuiContext& g = *GImGui;
3964 ImGuiID id = ImHash(name, 0);
3965 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
3968 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
3970 ImGuiContext& g = *GImGui;
3972 // Create window the first time
3973 ImGuiWindow* window = (ImGuiWindow*)ImGui::MemAlloc(sizeof(ImGuiWindow));
3974 IM_PLACEMENT_NEW(window) ImGuiWindow(name);
3975 window->Flags = flags;
3976 g.WindowsById.SetVoidPtr(window->ID, window);
3978 if (flags & ImGuiWindowFlags_NoSavedSettings)
3980 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
3981 window->Size = window->SizeFull = size;
3985 // Retrieve settings from .ini file
3986 // Use SetWindowPos() or SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
3987 window->PosFloat = ImVec2(60, 60);
3988 window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
3990 ImGuiIniData* settings = FindWindowSettings(name);
3992 settings = AddWindowSettings(name);
3994 SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
3996 if (settings->Pos.x != FLT_MAX)
3998 window->PosFloat = settings->Pos;
3999 window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
4000 window->Collapsed = settings->Collapsed;
4003 if (ImLengthSqr(settings->Size) > 0.00001f)
4004 size = settings->Size;
4005 window->Size = window->SizeFull = size;
4008 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4010 window->AutoFitFramesX = window->AutoFitFramesY = 2;
4011 window->AutoFitOnlyGrows = false;
4015 if (window->Size.x <= 0.0f)
4016 window->AutoFitFramesX = 2;
4017 if (window->Size.y <= 0.0f)
4018 window->AutoFitFramesY = 2;
4019 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
4022 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
4023 g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once
4025 g.Windows.push_back(window);
4029 static ImVec2 CalcSizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size)
4031 ImGuiContext& g = *GImGui;
4032 if (g.SetNextWindowSizeConstraint)
4034 // Using -1,-1 on either X/Y axis to preserve the current size.
4035 ImRect cr = g.SetNextWindowSizeConstraintRect;
4036 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
4037 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
4038 if (g.SetNextWindowSizeConstraintCallback)
4040 ImGuiSizeConstraintCallbackData data;
4041 data.UserData = g.SetNextWindowSizeConstraintCallbackUserData;
4042 data.Pos = window->Pos;
4043 data.CurrentSize = window->SizeFull;
4044 data.DesiredSize = new_size;
4045 g.SetNextWindowSizeConstraintCallback(&data);
4046 new_size = data.DesiredSize;
4049 if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
4051 new_size = ImMax(new_size, g.Style.WindowMinSize);
4052 new_size.y = ImMax(new_size.y, window->TitleBarHeight() + window->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
4057 static ImVec2 CalcSizeAutoFit(ImGuiWindow* window)
4059 ImGuiContext& g = *GImGui;
4060 ImGuiStyle& style = g.Style;
4061 ImGuiWindowFlags flags = window->Flags;
4062 ImVec2 size_auto_fit;
4063 if ((flags & ImGuiWindowFlags_Tooltip) != 0)
4065 // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose.
4066 size_auto_fit = window->SizeContents + window->WindowPadding - ImVec2(0.0f, style.ItemSpacing.y);
4070 // Handling case of auto fit window not fitting on the screen (on either axis): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
4071 size_auto_fit = ImClamp(window->SizeContents + window->WindowPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding));
4072 ImVec2 size_auto_fit_after_constraint = CalcSizeFullWithConstraint(window, size_auto_fit);
4073 if (size_auto_fit_after_constraint.x < window->SizeContents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar))
4074 size_auto_fit.y += style.ScrollbarSize;
4075 if (size_auto_fit_after_constraint.y < window->SizeContents.y && !(flags & ImGuiWindowFlags_NoScrollbar))
4076 size_auto_fit.x += style.ScrollbarSize;
4077 size_auto_fit.y = ImMax(size_auto_fit.y - style.ItemSpacing.y, 0.0f);
4079 return size_auto_fit;
4082 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
4084 ImVec2 scroll = window->Scroll;
4085 float cr_x = window->ScrollTargetCenterRatio.x;
4086 float cr_y = window->ScrollTargetCenterRatio.y;
4087 if (window->ScrollTarget.x < FLT_MAX)
4088 scroll.x = window->ScrollTarget.x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
4089 if (window->ScrollTarget.y < FLT_MAX)
4090 scroll.y = window->ScrollTarget.y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y);
4091 scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
4092 if (!window->Collapsed && !window->SkipItems)
4094 scroll.x = ImMin(scroll.x, ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x))); // == GetScrollMaxX for that window
4095 scroll.y = ImMin(scroll.y, ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y))); // == GetScrollMaxY for that window
4100 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
4102 if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
4103 return ImGuiCol_PopupBg;
4104 if (flags & ImGuiWindowFlags_ChildWindow)
4105 return ImGuiCol_ChildBg;
4106 return ImGuiCol_WindowBg;
4109 // Push a new ImGui window to add widgets to.
4110 // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair.
4111 // - Begin/End can be called multiple times during the frame with the same window name to append content.
4112 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
4113 // You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file.
4114 // - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned.
4115 // - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed.
4116 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
4118 ImGuiContext& g = *GImGui;
4119 const ImGuiStyle& style = g.Style;
4120 IM_ASSERT(name != NULL); // Window name required
4121 IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame()
4122 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
4124 if (flags & ImGuiWindowFlags_NoInputs)
4125 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
4128 bool window_is_new = false;
4129 ImGuiWindow* window = FindWindowByName(name);
4132 ImVec2 size_on_first_use = (g.SetNextWindowSizeCond != 0) ? g.SetNextWindowSizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here.
4133 window = CreateNewWindow(name, size_on_first_use, flags);
4134 window_is_new = true;
4137 const int current_frame = g.FrameCount;
4138 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
4139 if (first_begin_of_the_frame)
4140 window->Flags = (ImGuiWindowFlags)flags;
4142 flags = window->Flags;
4144 // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack
4145 ImGuiWindow* parent_window = first_begin_of_the_frame ? (!g.CurrentWindowStack.empty() ? g.CurrentWindowStack.back() : NULL) : window->ParentWindow;
4146 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
4149 g.CurrentWindowStack.push_back(window);
4150 SetCurrentWindow(window);
4151 CheckStacksSize(window, true);
4153 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
4154 if (flags & ImGuiWindowFlags_Popup)
4156 ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
4157 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
4158 window_just_activated_by_user |= (window != popup_ref.Window);
4159 popup_ref.Window = window;
4160 g.CurrentPopupStack.push_back(popup_ref);
4161 window->PopupId = popup_ref.PopupId;
4164 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames == 1);
4165 window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
4166 window->CloseButton = (p_open != NULL);
4168 // Process SetNextWindow***() calls
4169 if (window->Appearing)
4170 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
4171 bool window_pos_set_by_api = false, window_size_set_by_api = false;
4172 if (g.SetNextWindowPosCond)
4174 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.SetNextWindowPosCond) != 0;
4175 if (window_pos_set_by_api && ImLengthSqr(g.SetNextWindowPosPivot) > 0.00001f)
4177 // May be processed on the next frame if this is our first frame and we are measuring size
4178 // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
4179 window->SetWindowPosVal = g.SetNextWindowPosVal;
4180 window->SetWindowPosPivot = g.SetNextWindowPosPivot;
4181 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
4185 SetWindowPos(window, g.SetNextWindowPosVal, g.SetNextWindowPosCond);
4187 g.SetNextWindowPosCond = 0;
4189 if (g.SetNextWindowSizeCond)
4191 window_size_set_by_api = (window->SetWindowSizeAllowFlags & g.SetNextWindowSizeCond) != 0;
4192 SetWindowSize(window, g.SetNextWindowSizeVal, g.SetNextWindowSizeCond);
4193 g.SetNextWindowSizeCond = 0;
4195 if (g.SetNextWindowContentSizeCond)
4197 window->SizeContentsExplicit = g.SetNextWindowContentSizeVal;
4198 g.SetNextWindowContentSizeCond = 0;
4200 else if (first_begin_of_the_frame)
4202 window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
4204 if (g.SetNextWindowCollapsedCond)
4206 SetWindowCollapsed(window, g.SetNextWindowCollapsedVal, g.SetNextWindowCollapsedCond);
4207 g.SetNextWindowCollapsedCond = 0;
4209 if (g.SetNextWindowFocus)
4212 g.SetNextWindowFocus = false;
4214 if (window->Appearing)
4215 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
4217 // When reusing window again multiple times a frame, just append content (don't need to setup again)
4218 if (first_begin_of_the_frame)
4221 window->ParentWindow = parent_window;
4222 window->RootWindow = !(flags & ImGuiWindowFlags_ChildWindow) ? window : parent_window->RootWindow;
4223 window->RootNonPopupWindow = !(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) || (flags & ImGuiWindowFlags_Modal) ? window : parent_window->RootNonPopupWindow; // Used to display TitleBgActive color and for selecting which window to use for NavWindowing
4224 //window->RootNavWindow = window;
4225 //while (window->RootNavWindow->Flags & ImGuiWindowFlags_NavFlattened)
4226 // window->RootNavWindow = window->RootNavWindow->ParentWindow;
4228 window->Active = true;
4229 window->OrderWithinParent = 0;
4230 window->BeginCount = 0;
4231 window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
4232 window->LastFrameActive = current_frame;
4233 window->IDStack.resize(1);
4235 // Clear draw list, setup texture, outer clipping rectangle
4236 window->DrawList->Clear();
4237 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
4238 ImRect fullscreen_rect(GetVisibleRect());
4239 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_ComboBox|ImGuiWindowFlags_Popup)))
4240 PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
4242 PushClipRect(fullscreen_rect.Min, fullscreen_rect.Max, true);
4244 if (window_just_activated_by_user)
4246 // Popup first latch mouse position, will position itself when it appears next frame
4247 window->AutoPosLastDirection = -1;
4248 if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
4249 window->PosFloat = g.IO.MousePos;
4252 // Collapse window by double-clicking on title bar
4253 // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing
4254 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
4256 ImRect title_bar_rect = window->TitleBarRect();
4257 if (g.HoveredWindow == window && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
4259 window->Collapsed = !window->Collapsed;
4260 MarkIniSettingsDirty(window);
4261 FocusWindow(window);
4266 window->Collapsed = false;
4271 // Update contents size from last frame for auto-fitting (unless explicitly specified)
4272 window->SizeContents.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.x - window->Pos.x) + window->Scroll.x));
4273 window->SizeContents.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.y - window->Pos.y) + window->Scroll.y));
4275 // Hide popup/tooltip window when first appearing while we measure size (because we recycle them)
4276 if (window->HiddenFrames > 0)
4277 window->HiddenFrames--;
4278 if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && window_just_activated_by_user)
4280 window->HiddenFrames = 1;
4281 if (flags & ImGuiWindowFlags_AlwaysAutoResize)
4283 if (!window_size_set_by_api)
4284 window->Size = window->SizeFull = ImVec2(0.f, 0.f);
4285 window->SizeContents = ImVec2(0.f, 0.f);
4289 // Lock window rounding, border size and rounding so that altering the border sizes for children doesn't have side-effects.
4290 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
4291 window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
4292 window->WindowPadding = style.WindowPadding;
4293 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_ComboBox | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
4294 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
4295 const float window_rounding = window->WindowRounding;
4296 const float window_border_size = window->WindowBorderSize;
4298 // Calculate auto-fit size, handle automatic resize
4299 const ImVec2 size_auto_fit = CalcSizeAutoFit(window);
4300 if (window->Collapsed)
4302 // We still process initial auto-fit on collapsed windows to get a window width,
4303 // But otherwise we don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
4304 if (window->AutoFitFramesX > 0)
4305 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
4306 if (window->AutoFitFramesY > 0)
4307 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
4309 else if (!window_size_set_by_api)
4311 if (flags & ImGuiWindowFlags_AlwaysAutoResize)
4313 window->SizeFull = size_auto_fit;
4315 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4317 // Auto-fit only grows during the first few frames
4318 if (window->AutoFitFramesX > 0)
4319 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
4320 if (window->AutoFitFramesY > 0)
4321 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
4322 MarkIniSettingsDirty(window);
4326 // Apply minimum/maximum window size constraints and final size
4327 window->SizeFull = CalcSizeFullWithConstraint(window, window->SizeFull);
4328 window->Size = window->Collapsed ? window->TitleBarRect().GetSize() : window->SizeFull;
4329 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup))
4331 IM_ASSERT(window_size_set_by_api); // Submitted by BeginChild()
4332 window->Size = window->SizeFull;
4337 // Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size). We need to do this before manual resize (below) is effective.
4338 if (!window->Collapsed)
4340 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > window->SizeFull.y + style.ItemSpacing.y) && !(flags & ImGuiWindowFlags_NoScrollbar));
4341 window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > window->SizeFull.x - (window->ScrollbarY ? style.ScrollbarSize : 0.0f) - window->WindowPadding.x) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
4342 if (window->ScrollbarX && !window->ScrollbarY)
4343 window->ScrollbarY = (window->SizeContents.y > window->SizeFull.y + style.ItemSpacing.y - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
4344 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
4349 // Position child window
4350 if (flags & ImGuiWindowFlags_ChildWindow)
4352 window->OrderWithinParent = parent_window->DC.ChildWindows.Size;
4353 parent_window->DC.ChildWindows.push_back(window);
4355 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api)
4356 window->Pos = window->PosFloat = parent_window->DC.CursorPos;
4358 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFrames == 0);
4359 if (window_pos_with_pivot)
4361 // Position given a pivot (e.g. for centering)
4362 SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0);
4364 else if (flags & ImGuiWindowFlags_ChildMenu)
4366 // Child menus typically request _any_ position within the parent menu item, and then our FindBestPopupWindowPos() function will move the new menu outside the parent bounds.
4367 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
4368 IM_ASSERT(window_pos_set_by_api);
4369 float horizontal_overlap = style.ItemSpacing.x; // We want some overlap to convey the relative depth of each popup (currently the amount of overlap it is hard-coded to style.ItemSpacing.x, may need to introduce another style value).
4370 ImRect rect_to_avoid;
4371 if (parent_window->DC.MenuBarAppending)
4372 rect_to_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
4374 rect_to_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX);
4375 window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
4377 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
4379 ImRect rect_to_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1);
4380 window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
4383 // Position tooltip (always follows mouse)
4384 if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api)
4386 ImVec2 ref_pos = g.IO.MousePos;
4387 ImRect rect_to_avoid(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24, ref_pos.y + 24); // FIXME: Completely hard-coded. Perhaps center on cursor hit-point instead?
4388 window->PosFloat = FindBestPopupWindowPos(ref_pos, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
4389 if (window->AutoPosLastDirection == -1)
4390 window->PosFloat = ref_pos + ImVec2(2,2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
4393 // Clamp position so it stays visible
4394 if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
4396 if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
4398 ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
4399 window->PosFloat = ImMax(window->PosFloat + window->Size, padding) - window->Size;
4400 window->PosFloat = ImMin(window->PosFloat, g.IO.DisplaySize - padding);
4403 window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
4405 // Default item width. Make it proportional to window size if window manually resizes
4406 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
4407 window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
4409 window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
4411 // Prepare for focus requests
4412 window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1);
4413 window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1);
4414 window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1;
4415 window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX;
4418 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
4419 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
4421 // Modal window darkens what is behind them
4422 if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow())
4423 window->DrawList->AddRectFilled(fullscreen_rect.Min, fullscreen_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio));
4425 // Draw window + handle manual resize
4426 ImRect title_bar_rect = window->TitleBarRect();
4427 if (window->Collapsed)
4430 float backup_border_size = style.FrameBorderSize;
4431 g.Style.FrameBorderSize = window->WindowBorderSize;
4432 RenderFrame(title_bar_rect.Min, title_bar_rect.Max, GetColorU32(ImGuiCol_TitleBgCollapsed), true, window_rounding);
4433 g.Style.FrameBorderSize = backup_border_size;
4437 ImU32 resize_col = 0;
4438 const float resize_corner_size = ImMax(g.FontSize * 1.35f, window_rounding + 1.0f + g.FontSize * 0.2f);
4439 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && !(flags & ImGuiWindowFlags_NoResize))
4442 // Using the FlattenChilds button flag, we make the resize button accessible even if we are hovering over a child window
4443 const ImVec2 br = window->Rect().GetBR();
4444 const ImRect resize_rect(br - ImFloor(ImVec2(resize_corner_size * 0.75f, resize_corner_size * 0.75f)), br);
4445 const ImGuiID resize_id = window->GetID("#RESIZE");
4447 ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds);
4448 resize_col = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
4449 if (hovered || held)
4450 g.MouseCursor = ImGuiMouseCursor_ResizeNWSE;
4452 ImVec2 size_target(FLT_MAX,FLT_MAX);
4453 if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0])
4455 // Manual auto-fit when double-clicking
4456 size_target = size_auto_fit;
4461 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
4462 size_target = (g.IO.MousePos - g.ActiveIdClickOffset - window->Pos) + resize_rect.GetSize();
4465 if (size_target.x != FLT_MAX && size_target.y != FLT_MAX)
4467 window->SizeFull = CalcSizeFullWithConstraint(window, size_target);
4468 MarkIniSettingsDirty(window);
4470 window->Size = window->SizeFull;
4471 title_bar_rect = window->TitleBarRect();
4474 // Window background, Default Alpha
4475 ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
4476 window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
4479 const bool window_is_focused = g.NavWindow && window->RootNonPopupWindow == g.NavWindow->RootNonPopupWindow;
4480 if (!(flags & ImGuiWindowFlags_NoTitleBar))
4481 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, ImDrawCornerFlags_Top);
4484 if (flags & ImGuiWindowFlags_MenuBar)
4486 ImRect menu_bar_rect = window->MenuBarRect();
4487 menu_bar_rect.ClipWith(window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them.
4488 window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top);
4489 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
4490 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
4494 if (window->ScrollbarX)
4495 Scrollbar(ImGuiLayoutType_Horizontal);
4496 if (window->ScrollbarY)
4497 Scrollbar(ImGuiLayoutType_Vertical);
4499 // Render resize grip
4500 // (after the input handling so we don't have a frame of latency)
4501 if (!(flags & ImGuiWindowFlags_NoResize))
4503 const ImVec2 br = window->Rect().GetBR();
4504 window->DrawList->PathLineTo(br + ImVec2(-resize_corner_size, -window_border_size));
4505 window->DrawList->PathLineTo(br + ImVec2(-window_border_size, -resize_corner_size));
4506 window->DrawList->PathArcToFast(ImVec2(br.x - window_rounding - window_border_size, br.y - window_rounding - window_border_size), window_rounding, 0, 3);
4507 window->DrawList->PathFillConvex(resize_col);
4511 if (window_border_size > 0.0f)
4512 window->DrawList->AddRect(window->Pos, window->Pos+window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size);
4513 if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar))
4514 window->DrawList->AddLine(title_bar_rect.GetBL()+ImVec2(1,-1), title_bar_rect.GetBR()+ImVec2(-1,-1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
4517 // Update ContentsRegionMax. All the variable it depends on are set above in this function.
4518 window->ContentsRegionRect.Min.x = -window->Scroll.x + window->WindowPadding.x;
4519 window->ContentsRegionRect.Min.y = -window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
4520 window->ContentsRegionRect.Max.x = -window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x));
4521 window->ContentsRegionRect.Max.y = -window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y));
4523 // Setup drawing context
4524 // (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.)
4525 window->DC.IndentX = 0.0f + window->WindowPadding.x - window->Scroll.x;
4526 window->DC.GroupOffsetX = 0.0f;
4527 window->DC.ColumnsOffsetX = 0.0f;
4528 window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.IndentX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y);
4529 window->DC.CursorPos = window->DC.CursorStartPos;
4530 window->DC.CursorPosPrevLine = window->DC.CursorPos;
4531 window->DC.CursorMaxPos = window->DC.CursorStartPos;
4532 window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f;
4533 window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
4534 window->DC.MenuBarAppending = false;
4535 window->DC.MenuBarOffsetX = ImMax(window->WindowPadding.x, style.ItemSpacing.x);
4536 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
4537 window->DC.ChildWindows.resize(0);
4538 window->DC.LayoutType = ImGuiLayoutType_Vertical;
4539 window->DC.ItemFlags = ImGuiItemFlags_Default_;
4540 window->DC.ItemWidth = window->ItemWidthDefault;
4541 window->DC.TextWrapPos = -1.0f; // disabled
4542 window->DC.ItemFlagsStack.resize(0);
4543 window->DC.ItemWidthStack.resize(0);
4544 window->DC.TextWrapPosStack.resize(0);
4545 window->DC.ColumnsCurrent = 0;
4546 window->DC.ColumnsCount = 1;
4547 window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
4548 window->DC.ColumnsStartMaxPosX = window->DC.CursorMaxPos.x;
4549 window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.ColumnsStartPosY;
4550 window->DC.TreeDepth = 0;
4551 window->DC.StateStorage = &window->StateStorage;
4552 window->DC.GroupStack.resize(0);
4553 window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
4555 if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
4557 window->DC.ItemFlags = parent_window->DC.ItemFlags;
4558 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
4561 if (window->AutoFitFramesX > 0)
4562 window->AutoFitFramesX--;
4563 if (window->AutoFitFramesY > 0)
4564 window->AutoFitFramesY--;
4566 // New windows appears in front (we need to do that AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
4567 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
4568 if (!(flags & (ImGuiWindowFlags_ChildWindow|ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
4569 FocusWindow(window);
4572 if (!(flags & ImGuiWindowFlags_NoTitleBar))
4575 if (!(flags & ImGuiWindowFlags_NoCollapse))
4577 RenderTriangle(window->Pos + style.FramePadding, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f);
4583 const float PAD = 2.0f;
4584 const float rad = (window->TitleBarHeight() - PAD*2.0f) * 0.5f;
4585 if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-PAD - rad, PAD + rad), rad))
4589 // Title text (FIXME: refactor text alignment facilities along with RenderText helpers)
4590 const ImVec2 text_size = CalcTextSize(name, NULL, true);
4591 ImVec2 text_min = window->Pos;
4592 ImVec2 text_max = window->Pos + ImVec2(window->Size.x, style.FramePadding.y*2 + text_size.y);
4594 clip_rect.Max = ImVec2(window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x), text_max.y); // Match the size of CloseWindowButton()
4595 float pad_left = (flags & ImGuiWindowFlags_NoCollapse) == 0 ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x;
4596 float pad_right = (p_open != NULL) ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x;
4597 if (style.WindowTitleAlign.x > 0.0f) pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x);
4598 text_min.x += pad_left;
4599 text_max.x -= pad_right;
4600 clip_rect.Min = ImVec2(text_min.x, window->Pos.y);
4601 RenderTextClipped(text_min, text_max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect);
4604 // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
4605 window->WindowRectClipped = window->Rect();
4606 window->WindowRectClipped.ClipWith(window->ClipRect);
4608 // Pressing CTRL+C while holding on a window copy its content to the clipboard
4609 // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope.
4610 // Maybe we can support CTRL+C on every element?
4612 if (g.ActiveId == move_id)
4613 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
4614 ImGui::LogToClipboard();
4618 // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
4619 // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior.
4620 window->InnerRect.Min.x = title_bar_rect.Min.x;
4621 window->InnerRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight();
4622 window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
4623 window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
4624 //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE);
4626 // After Begin() we fill the last item / hovered data using the title bar data. Make that a standard behavior (to allow usage of context menus on title bar only, etc.).
4627 window->DC.LastItemId = window->MoveId;
4628 window->DC.LastItemRect = title_bar_rect;
4629 window->DC.LastItemRectHoveredRect = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false);
4632 // Inner clipping rectangle
4633 // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
4634 const float border_size = window->WindowBorderSize;
4636 clip_rect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
4637 clip_rect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + border_size);
4638 clip_rect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
4639 clip_rect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - border_size);
4640 PushClipRect(clip_rect.Min, clip_rect.Max, true);
4642 // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused)
4643 if (first_begin_of_the_frame)
4644 window->WriteAccessed = false;
4646 window->BeginCount++;
4647 g.SetNextWindowSizeConstraint = false;
4649 // Child window can be out of sight and have "negative" clip windows.
4650 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar).
4651 if (flags & ImGuiWindowFlags_ChildWindow)
4653 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
4654 window->Collapsed = parent_window && parent_window->Collapsed;
4656 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
4657 window->Collapsed |= (window->WindowRectClipped.Min.x >= window->WindowRectClipped.Max.x || window->WindowRectClipped.Min.y >= window->WindowRectClipped.Max.y);
4659 // We also hide the window from rendering because we've already added its border to the command list.
4660 // (we could perform the check earlier in the function but it is simpler at this point)
4661 if (window->Collapsed)
4662 window->Active = false;
4664 if (style.Alpha <= 0.0f)
4665 window->Active = false;
4667 // Return false if we don't intend to display anything to allow user to perform an early out optimization
4668 window->SkipItems = (window->Collapsed || !window->Active) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0;
4669 return !window->SkipItems;
4672 // Old Begin() API with 5 parameters, avoid calling this version directly! Use SetNextWindowSize()+Begin() instead.
4673 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4674 bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha_override, ImGuiWindowFlags flags)
4676 // Old API feature: we could pass the initial window size as a parameter, however this was very misleading because in most cases it would only affect the window when it didn't have storage in the .ini file.
4677 if (size_on_first_use.x != 0.0f || size_on_first_use.y != 0.0f)
4678 SetNextWindowSize(size_on_first_use, ImGuiCond_FirstUseEver);
4680 // Old API feature: we could override the window background alpha with a parameter. This is actually tricky to reproduce manually because:
4681 // (1) there are multiple variants of WindowBg (popup, tooltip, etc.) and (2) you can't call PushStyleColor before Begin and PopStyleColor just after Begin() because of how CheckStackSizes() behave.
4682 // The user-side solution is to do backup = GetStyleColorVec4(ImGuiCol_xxxBG), PushStyleColor(ImGuiCol_xxxBg), Begin, PushStyleColor(ImGuiCol_xxxBg, backup), [...], PopStyleColor(), End(); PopStyleColor() - which is super awkward.
4683 // The alpha override was rarely used but for now we'll leave the Begin() variant around for a bit. We may either lift the constraint on CheckStackSizes() either add a SetNextWindowBgAlpha() helper that does it magically.
4684 ImGuiContext& g = *GImGui;
4685 const ImGuiCol bg_color_idx = GetWindowBgColorIdxFromFlags(flags);
4686 const ImVec4 bg_color_backup = g.Style.Colors[bg_color_idx];
4687 if (bg_alpha_override >= 0.0f)
4688 g.Style.Colors[bg_color_idx].w = bg_alpha_override;
4690 bool ret = Begin(name, p_open, flags);
4692 if (bg_alpha_override >= 0.0f)
4693 g.Style.Colors[bg_color_idx] = bg_color_backup;
4696 #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4700 ImGuiContext& g = *GImGui;
4701 ImGuiWindow* window = g.CurrentWindow;
4703 if (window->DC.ColumnsCount != 1) // close columns set if any is open
4705 PopClipRect(); // inner window clip rectangle
4708 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
4712 // NB: we don't clear 'window->RootWindow'. The pointer is allowed to live until the next call to Begin().
4713 g.CurrentWindowStack.pop_back();
4714 if (window->Flags & ImGuiWindowFlags_Popup)
4715 g.CurrentPopupStack.pop_back();
4716 CheckStacksSize(window, false);
4717 SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
4720 // Vertical scrollbar
4721 // The entire piece of code below is rather confusing because:
4722 // - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
4723 // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar
4724 // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.
4725 void ImGui::Scrollbar(ImGuiLayoutType direction)
4727 ImGuiContext& g = *GImGui;
4728 ImGuiWindow* window = g.CurrentWindow;
4730 const bool horizontal = (direction == ImGuiLayoutType_Horizontal);
4731 const ImGuiStyle& style = g.Style;
4732 const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY");
4734 // Render background
4735 bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX);
4736 float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f;
4737 const ImRect window_rect = window->Rect();
4738 const float border_size = window->WindowBorderSize;
4739 ImRect bb = horizontal
4740 ? ImRect(window->Pos.x + border_size, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - other_scrollbar_size_w - border_size, window_rect.Max.y - border_size)
4741 : ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size);
4743 bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f);
4744 if (bb.GetWidth() <= 0.0f || bb.GetHeight() <= 0.0f)
4747 int window_rounding_corners;
4749 window_rounding_corners = ImDrawCornerFlags_BotLeft | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight);
4751 window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0) | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight);
4752 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, window_rounding_corners);
4753 bb.Expand(ImVec2(-ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f)));
4755 // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
4756 float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight();
4757 float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y;
4758 float win_size_avail_v = (horizontal ? window->SizeFull.x : window->SizeFull.y) - other_scrollbar_size_w;
4759 float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y;
4761 // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount)
4762 // But we maintain a minimum size in pixel to allow for the user to still aim inside.
4763 IM_ASSERT(ImMax(win_size_contents_v, win_size_avail_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers.
4764 const float win_size_v = ImMax(ImMax(win_size_contents_v, win_size_avail_v), 1.0f);
4765 const float grab_h_pixels = ImClamp(scrollbar_size_v * (win_size_avail_v / win_size_v), style.GrabMinSize, scrollbar_size_v);
4766 const float grab_h_norm = grab_h_pixels / scrollbar_size_v;
4768 // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().
4770 bool hovered = false;
4771 const bool previously_held = (g.ActiveId == id);
4772 ButtonBehavior(bb, id, &hovered, &held);
4774 float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v);
4775 float scroll_ratio = ImSaturate(scroll_v / scroll_max);
4776 float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
4777 if (held && grab_h_norm < 1.0f)
4779 float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y;
4780 float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
4781 float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y;
4783 // Click position in scrollbar normalized space (0.0f->1.0f)
4784 const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
4787 bool seek_absolute = false;
4788 if (!previously_held)
4790 // On initial click calculate the distance between mouse and the center of the grab
4791 if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm)
4793 *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
4797 seek_absolute = true;
4798 *click_delta_to_grab_center_v = 0.0f;
4803 // It is ok to modify Scroll here because we are being called in Begin() after the calculation of SizeContents and before setting up our starting position
4804 const float scroll_v_norm = ImSaturate((clicked_v_norm - *click_delta_to_grab_center_v - grab_h_norm*0.5f) / (1.0f - grab_h_norm));
4805 scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v));
4807 window->Scroll.x = scroll_v;
4809 window->Scroll.y = scroll_v;
4811 // Update values for rendering
4812 scroll_ratio = ImSaturate(scroll_v / scroll_max);
4813 grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
4815 // Update distance to grab now that we have seeked and saturated
4817 *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
4821 const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab);
4824 grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImMin(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, window_rect.Max.x), bb.Max.y);
4826 grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImMin(ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels, window_rect.Max.y));
4827 window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding);
4830 void ImGui::BringWindowToFront(ImGuiWindow* window)
4832 ImGuiContext& g = *GImGui;
4833 if (g.Windows.back() == window)
4835 for (int i = 0; i < g.Windows.Size; i++)
4836 if (g.Windows[i] == window)
4838 g.Windows.erase(g.Windows.begin() + i);
4839 g.Windows.push_back(window);
4844 void ImGui::BringWindowToBack(ImGuiWindow* window)
4846 ImGuiContext& g = *GImGui;
4847 if (g.Windows[0] == window)
4849 for (int i = 0; i < g.Windows.Size; i++)
4850 if (g.Windows[i] == window)
4852 memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
4853 g.Windows[0] = window;
4858 // Moving window to front of display and set focus (which happens to be back of our sorted list)
4859 void ImGui::FocusWindow(ImGuiWindow* window)
4861 ImGuiContext& g = *GImGui;
4863 // Always mark the window we passed as focused. This is used for keyboard interactions such as tabbing.
4864 g.NavWindow = window;
4866 // Passing NULL allow to disable keyboard focus
4870 // Move the root window to the top of the pile
4871 if (window->RootWindow)
4872 window = window->RootWindow;
4874 // Steal focus on active widgets
4875 if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
4876 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
4880 if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
4881 BringWindowToFront(window);
4884 void ImGui::FocusPreviousWindow()
4886 ImGuiContext& g = *GImGui;
4887 for (int i = g.Windows.Size - 1; i >= 0; i--)
4888 if (g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow))
4890 FocusWindow(g.Windows[i]);
4895 void ImGui::PushItemWidth(float item_width)
4897 ImGuiWindow* window = GetCurrentWindow();
4898 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
4899 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
4902 void ImGui::PushMultiItemsWidths(int components, float w_full)
4904 ImGuiWindow* window = GetCurrentWindow();
4905 const ImGuiStyle& style = GImGui->Style;
4907 w_full = CalcItemWidth();
4908 const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
4909 const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
4910 window->DC.ItemWidthStack.push_back(w_item_last);
4911 for (int i = 0; i < components-1; i++)
4912 window->DC.ItemWidthStack.push_back(w_item_one);
4913 window->DC.ItemWidth = window->DC.ItemWidthStack.back();
4916 void ImGui::PopItemWidth()
4918 ImGuiWindow* window = GetCurrentWindow();
4919 window->DC.ItemWidthStack.pop_back();
4920 window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
4923 float ImGui::CalcItemWidth()
4925 ImGuiWindow* window = GetCurrentWindowRead();
4926 float w = window->DC.ItemWidth;
4929 // Align to a right-side limit. We include 1 frame padding in the calculation because this is how the width is always used (we add 2 frame padding to it), but we could move that responsibility to the widget as well.
4930 float width_to_right_edge = GetContentRegionAvail().x;
4931 w = ImMax(1.0f, width_to_right_edge + w);
4937 static ImFont* GetDefaultFont()
4939 ImGuiContext& g = *GImGui;
4940 return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0];
4943 static void SetCurrentFont(ImFont* font)
4945 ImGuiContext& g = *GImGui;
4946 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
4947 IM_ASSERT(font->Scale > 0.0f);
4949 g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale;
4950 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
4951 g.FontTexUvWhitePixel = g.Font->ContainerAtlas->TexUvWhitePixel;
4954 void ImGui::PushFont(ImFont* font)
4956 ImGuiContext& g = *GImGui;
4958 font = GetDefaultFont();
4959 SetCurrentFont(font);
4960 g.FontStack.push_back(font);
4961 g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
4964 void ImGui::PopFont()
4966 ImGuiContext& g = *GImGui;
4967 g.CurrentWindow->DrawList->PopTextureID();
4968 g.FontStack.pop_back();
4969 SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
4972 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
4974 ImGuiWindow* window = GetCurrentWindow();
4976 window->DC.ItemFlags |= option;
4978 window->DC.ItemFlags &= ~option;
4979 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
4982 void ImGui::PopItemFlag()
4984 ImGuiWindow* window = GetCurrentWindow();
4985 window->DC.ItemFlagsStack.pop_back();
4986 window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
4989 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
4991 PushItemFlag(ImGuiItemFlags_AllowKeyboardFocus, allow_keyboard_focus);
4994 void ImGui::PopAllowKeyboardFocus()
4999 void ImGui::PushButtonRepeat(bool repeat)
5001 PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
5004 void ImGui::PopButtonRepeat()
5009 void ImGui::PushTextWrapPos(float wrap_pos_x)
5011 ImGuiWindow* window = GetCurrentWindow();
5012 window->DC.TextWrapPos = wrap_pos_x;
5013 window->DC.TextWrapPosStack.push_back(wrap_pos_x);
5016 void ImGui::PopTextWrapPos()
5018 ImGuiWindow* window = GetCurrentWindow();
5019 window->DC.TextWrapPosStack.pop_back();
5020 window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
5023 // FIXME: This may incur a round-trip (if the end user got their data from a float4) but eventually we aim to store the in-flight colors as ImU32
5024 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
5026 ImGuiContext& g = *GImGui;
5029 backup.BackupValue = g.Style.Colors[idx];
5030 g.ColorModifiers.push_back(backup);
5031 g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
5034 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
5036 ImGuiContext& g = *GImGui;
5039 backup.BackupValue = g.Style.Colors[idx];
5040 g.ColorModifiers.push_back(backup);
5041 g.Style.Colors[idx] = col;
5044 void ImGui::PopStyleColor(int count)
5046 ImGuiContext& g = *GImGui;
5049 ImGuiColMod& backup = g.ColorModifiers.back();
5050 g.Style.Colors[backup.Col] = backup.BackupValue;
5051 g.ColorModifiers.pop_back();
5056 struct ImGuiStyleVarInfo
5060 void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
5063 static const ImGuiStyleVarInfo GStyleVarInfo[ImGuiStyleVar_Count_] =
5065 { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
5066 { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
5067 { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
5068 { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
5069 { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
5070 { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
5071 { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
5072 { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
5073 { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
5074 { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
5075 { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
5076 { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
5077 { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
5078 { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
5079 { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
5080 { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
5081 { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
5084 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
5086 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_Count_);
5087 return &GStyleVarInfo[idx];
5090 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
5092 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5093 if (var_info->Type == ImGuiDataType_Float)
5095 ImGuiContext& g = *GImGui;
5096 float* pvar = (float*)var_info->GetVarPtr(&g.Style);
5097 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5101 IM_ASSERT(0); // Called function with wrong-type? Variable is not a float.
5104 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
5106 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5107 if (var_info->Type == ImGuiDataType_Float2)
5109 ImGuiContext& g = *GImGui;
5110 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
5111 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5115 IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2.
5118 void ImGui::PopStyleVar(int count)
5120 ImGuiContext& g = *GImGui;
5123 ImGuiStyleMod& backup = g.StyleModifiers.back();
5124 const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
5125 if (info->Type == ImGuiDataType_Float) (*(float*)info->GetVarPtr(&g.Style)) = backup.BackupFloat[0];
5126 else if (info->Type == ImGuiDataType_Float2) (*(ImVec2*)info->GetVarPtr(&g.Style)) = ImVec2(backup.BackupFloat[0], backup.BackupFloat[1]);
5127 else if (info->Type == ImGuiDataType_Int) (*(int*)info->GetVarPtr(&g.Style)) = backup.BackupInt[0];
5128 g.StyleModifiers.pop_back();
5133 const char* ImGui::GetStyleColorName(ImGuiCol idx)
5135 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
5138 case ImGuiCol_Text: return "Text";
5139 case ImGuiCol_TextDisabled: return "TextDisabled";
5140 case ImGuiCol_WindowBg: return "WindowBg";
5141 case ImGuiCol_ChildBg: return "ChildBg";
5142 case ImGuiCol_PopupBg: return "PopupBg";
5143 case ImGuiCol_Border: return "Border";
5144 case ImGuiCol_BorderShadow: return "BorderShadow";
5145 case ImGuiCol_FrameBg: return "FrameBg";
5146 case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
5147 case ImGuiCol_FrameBgActive: return "FrameBgActive";
5148 case ImGuiCol_TitleBg: return "TitleBg";
5149 case ImGuiCol_TitleBgActive: return "TitleBgActive";
5150 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
5151 case ImGuiCol_MenuBarBg: return "MenuBarBg";
5152 case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
5153 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
5154 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
5155 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
5156 case ImGuiCol_CheckMark: return "CheckMark";
5157 case ImGuiCol_SliderGrab: return "SliderGrab";
5158 case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
5159 case ImGuiCol_Button: return "Button";
5160 case ImGuiCol_ButtonHovered: return "ButtonHovered";
5161 case ImGuiCol_ButtonActive: return "ButtonActive";
5162 case ImGuiCol_Header: return "Header";
5163 case ImGuiCol_HeaderHovered: return "HeaderHovered";
5164 case ImGuiCol_HeaderActive: return "HeaderActive";
5165 case ImGuiCol_Separator: return "Separator";
5166 case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
5167 case ImGuiCol_SeparatorActive: return "SeparatorActive";
5168 case ImGuiCol_ResizeGrip: return "ResizeGrip";
5169 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
5170 case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
5171 case ImGuiCol_CloseButton: return "CloseButton";
5172 case ImGuiCol_CloseButtonHovered: return "CloseButtonHovered";
5173 case ImGuiCol_CloseButtonActive: return "CloseButtonActive";
5174 case ImGuiCol_PlotLines: return "PlotLines";
5175 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
5176 case ImGuiCol_PlotHistogram: return "PlotHistogram";
5177 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
5178 case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
5179 case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening";
5185 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
5187 IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
5188 ImGuiContext& g = *GImGui;
5189 if (flags & ImGuiHoveredFlags_FlattenChilds)
5191 if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
5196 if (g.HoveredWindow != g.CurrentWindow)
5199 if (!IsWindowContentHoverable(g.HoveredRootWindow, flags))
5201 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
5202 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
5207 bool ImGui::IsWindowFocused()
5209 ImGuiContext& g = *GImGui;
5210 IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End()
5211 return g.NavWindow == g.CurrentWindow;
5214 bool ImGui::IsRootWindowFocused()
5216 ImGuiContext& g = *GImGui;
5217 IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End()
5218 return g.NavWindow == g.CurrentWindow->RootWindow;
5221 bool ImGui::IsRootWindowOrAnyChildFocused()
5223 ImGuiContext& g = *GImGui;
5224 IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End()
5225 return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
5228 float ImGui::GetWindowWidth()
5230 ImGuiWindow* window = GImGui->CurrentWindow;
5231 return window->Size.x;
5234 float ImGui::GetWindowHeight()
5236 ImGuiWindow* window = GImGui->CurrentWindow;
5237 return window->Size.y;
5240 ImVec2 ImGui::GetWindowPos()
5242 ImGuiContext& g = *GImGui;
5243 ImGuiWindow* window = g.CurrentWindow;
5247 static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
5249 window->DC.CursorMaxPos.y += window->Scroll.y; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it.
5250 window->Scroll.y = new_scroll_y;
5251 window->DC.CursorMaxPos.y -= window->Scroll.y;
5254 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
5256 // Test condition (NB: bit 0 is always true) and clear flags for next time
5257 if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
5259 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5260 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
5263 const ImVec2 old_pos = window->Pos;
5264 window->PosFloat = pos;
5265 window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
5266 window->DC.CursorPos += (window->Pos - old_pos); // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor
5267 window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected.
5270 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
5272 ImGuiWindow* window = GetCurrentWindowRead();
5273 SetWindowPos(window, pos, cond);
5276 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
5278 if (ImGuiWindow* window = FindWindowByName(name))
5279 SetWindowPos(window, pos, cond);
5282 ImVec2 ImGui::GetWindowSize()
5284 ImGuiWindow* window = GetCurrentWindowRead();
5285 return window->Size;
5288 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
5290 // Test condition (NB: bit 0 is always true) and clear flags for next time
5291 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
5293 window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5298 window->AutoFitFramesX = 0;
5299 window->SizeFull.x = size.x;
5303 window->AutoFitFramesX = 2;
5304 window->AutoFitOnlyGrows = false;
5308 window->AutoFitFramesY = 0;
5309 window->SizeFull.y = size.y;
5313 window->AutoFitFramesY = 2;
5314 window->AutoFitOnlyGrows = false;
5318 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
5320 SetWindowSize(GImGui->CurrentWindow, size, cond);
5323 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
5325 if (ImGuiWindow* window = FindWindowByName(name))
5326 SetWindowSize(window, size, cond);
5329 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
5331 // Test condition (NB: bit 0 is always true) and clear flags for next time
5332 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
5334 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5337 window->Collapsed = collapsed;
5340 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
5342 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
5345 bool ImGui::IsWindowCollapsed()
5347 ImGuiWindow* window = GetCurrentWindowRead();
5348 return window->Collapsed;
5351 bool ImGui::IsWindowAppearing()
5353 ImGuiWindow* window = GetCurrentWindowRead();
5354 return window->Appearing;
5357 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
5359 if (ImGuiWindow* window = FindWindowByName(name))
5360 SetWindowCollapsed(window, collapsed, cond);
5363 void ImGui::SetWindowFocus()
5365 FocusWindow(GImGui->CurrentWindow);
5368 void ImGui::SetWindowFocus(const char* name)
5372 if (ImGuiWindow* window = FindWindowByName(name))
5373 FocusWindow(window);
5381 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
5383 ImGuiContext& g = *GImGui;
5384 g.SetNextWindowPosVal = pos;
5385 g.SetNextWindowPosPivot = pivot;
5386 g.SetNextWindowPosCond = cond ? cond : ImGuiCond_Always;
5389 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
5391 ImGuiContext& g = *GImGui;
5392 g.SetNextWindowSizeVal = size;
5393 g.SetNextWindowSizeCond = cond ? cond : ImGuiCond_Always;
5396 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeConstraintCallback custom_callback, void* custom_callback_user_data)
5398 ImGuiContext& g = *GImGui;
5399 g.SetNextWindowSizeConstraint = true;
5400 g.SetNextWindowSizeConstraintRect = ImRect(size_min, size_max);
5401 g.SetNextWindowSizeConstraintCallback = custom_callback;
5402 g.SetNextWindowSizeConstraintCallbackUserData = custom_callback_user_data;
5405 void ImGui::SetNextWindowContentSize(const ImVec2& size)
5407 ImGuiContext& g = *GImGui;
5408 g.SetNextWindowContentSizeVal = size;
5409 g.SetNextWindowContentSizeCond = ImGuiCond_Always;
5412 void ImGui::SetNextWindowContentWidth(float width)
5414 ImGuiContext& g = *GImGui;
5415 g.SetNextWindowContentSizeVal = ImVec2(width, g.SetNextWindowContentSizeCond ? g.SetNextWindowContentSizeVal.y : 0.0f);
5416 g.SetNextWindowContentSizeCond = ImGuiCond_Always;
5419 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
5421 ImGuiContext& g = *GImGui;
5422 g.SetNextWindowCollapsedVal = collapsed;
5423 g.SetNextWindowCollapsedCond = cond ? cond : ImGuiCond_Always;
5426 void ImGui::SetNextWindowFocus()
5428 ImGuiContext& g = *GImGui;
5429 g.SetNextWindowFocus = true;
5432 // In window space (not screen space!)
5433 ImVec2 ImGui::GetContentRegionMax()
5435 ImGuiWindow* window = GetCurrentWindowRead();
5436 ImVec2 mx = window->ContentsRegionRect.Max;
5437 if (window->DC.ColumnsCount != 1)
5438 mx.x = GetColumnOffset(window->DC.ColumnsCurrent + 1) - window->WindowPadding.x;
5442 ImVec2 ImGui::GetContentRegionAvail()
5444 ImGuiWindow* window = GetCurrentWindowRead();
5445 return GetContentRegionMax() - (window->DC.CursorPos - window->Pos);
5448 float ImGui::GetContentRegionAvailWidth()
5450 return GetContentRegionAvail().x;
5453 // In window space (not screen space!)
5454 ImVec2 ImGui::GetWindowContentRegionMin()
5456 ImGuiWindow* window = GetCurrentWindowRead();
5457 return window->ContentsRegionRect.Min;
5460 ImVec2 ImGui::GetWindowContentRegionMax()
5462 ImGuiWindow* window = GetCurrentWindowRead();
5463 return window->ContentsRegionRect.Max;
5466 float ImGui::GetWindowContentRegionWidth()
5468 ImGuiWindow* window = GetCurrentWindowRead();
5469 return window->ContentsRegionRect.Max.x - window->ContentsRegionRect.Min.x;
5472 float ImGui::GetTextLineHeight()
5474 ImGuiContext& g = *GImGui;
5478 float ImGui::GetTextLineHeightWithSpacing()
5480 ImGuiContext& g = *GImGui;
5481 return g.FontSize + g.Style.ItemSpacing.y;
5484 float ImGui::GetItemsLineHeightWithSpacing()
5486 ImGuiContext& g = *GImGui;
5487 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
5490 ImDrawList* ImGui::GetWindowDrawList()
5492 ImGuiWindow* window = GetCurrentWindow();
5493 return window->DrawList;
5496 ImFont* ImGui::GetFont()
5498 return GImGui->Font;
5501 float ImGui::GetFontSize()
5503 return GImGui->FontSize;
5506 ImVec2 ImGui::GetFontTexUvWhitePixel()
5508 return GImGui->FontTexUvWhitePixel;
5511 void ImGui::SetWindowFontScale(float scale)
5513 ImGuiContext& g = *GImGui;
5514 ImGuiWindow* window = GetCurrentWindow();
5515 window->FontWindowScale = scale;
5516 g.FontSize = window->CalcFontSize();
5519 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
5520 // Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'.
5521 ImVec2 ImGui::GetCursorPos()
5523 ImGuiWindow* window = GetCurrentWindowRead();
5524 return window->DC.CursorPos - window->Pos + window->Scroll;
5527 float ImGui::GetCursorPosX()
5529 ImGuiWindow* window = GetCurrentWindowRead();
5530 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
5533 float ImGui::GetCursorPosY()
5535 ImGuiWindow* window = GetCurrentWindowRead();
5536 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
5539 void ImGui::SetCursorPos(const ImVec2& local_pos)
5541 ImGuiWindow* window = GetCurrentWindow();
5542 window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
5543 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
5546 void ImGui::SetCursorPosX(float x)
5548 ImGuiWindow* window = GetCurrentWindow();
5549 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
5550 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
5553 void ImGui::SetCursorPosY(float y)
5555 ImGuiWindow* window = GetCurrentWindow();
5556 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
5557 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
5560 ImVec2 ImGui::GetCursorStartPos()
5562 ImGuiWindow* window = GetCurrentWindowRead();
5563 return window->DC.CursorStartPos - window->Pos;
5566 ImVec2 ImGui::GetCursorScreenPos()
5568 ImGuiWindow* window = GetCurrentWindowRead();
5569 return window->DC.CursorPos;
5572 void ImGui::SetCursorScreenPos(const ImVec2& screen_pos)
5574 ImGuiWindow* window = GetCurrentWindow();
5575 window->DC.CursorPos = screen_pos;
5576 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
5579 float ImGui::GetScrollX()
5581 return GImGui->CurrentWindow->Scroll.x;
5584 float ImGui::GetScrollY()
5586 return GImGui->CurrentWindow->Scroll.y;
5589 float ImGui::GetScrollMaxX()
5591 ImGuiWindow* window = GetCurrentWindowRead();
5592 return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x));
5595 float ImGui::GetScrollMaxY()
5597 ImGuiWindow* window = GetCurrentWindowRead();
5598 return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y));
5601 void ImGui::SetScrollX(float scroll_x)
5603 ImGuiWindow* window = GetCurrentWindow();
5604 window->ScrollTarget.x = scroll_x;
5605 window->ScrollTargetCenterRatio.x = 0.0f;
5608 void ImGui::SetScrollY(float scroll_y)
5610 ImGuiWindow* window = GetCurrentWindow();
5611 window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY
5612 window->ScrollTargetCenterRatio.y = 0.0f;
5615 void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio)
5617 // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
5618 ImGuiWindow* window = GetCurrentWindow();
5619 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
5620 window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y);
5621 if (center_y_ratio <= 0.0f && window->ScrollTarget.y <= window->WindowPadding.y) // Minor hack to make "scroll to top" take account of WindowPadding, else it would scroll to (WindowPadding.y - ItemSpacing.y)
5622 window->ScrollTarget.y = 0.0f;
5623 window->ScrollTargetCenterRatio.y = center_y_ratio;
5626 // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
5627 void ImGui::SetScrollHere(float center_y_ratio)
5629 ImGuiWindow* window = GetCurrentWindow();
5630 float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
5631 target_y += (window->DC.PrevLineHeight * center_y_ratio) + (GImGui->Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line.
5632 SetScrollFromPosY(target_y, center_y_ratio);
5635 void ImGui::SetKeyboardFocusHere(int offset)
5637 IM_ASSERT(offset >= -1); // -1 is allowed but not below
5638 ImGuiWindow* window = GetCurrentWindow();
5639 window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
5640 window->FocusIdxTabRequestNext = INT_MAX;
5643 void ImGui::SetStateStorage(ImGuiStorage* tree)
5645 ImGuiWindow* window = GetCurrentWindow();
5646 window->DC.StateStorage = tree ? tree : &window->StateStorage;
5649 ImGuiStorage* ImGui::GetStateStorage()
5651 ImGuiWindow* window = GetCurrentWindowRead();
5652 return window->DC.StateStorage;
5655 void ImGui::TextV(const char* fmt, va_list args)
5657 ImGuiWindow* window = GetCurrentWindow();
5658 if (window->SkipItems)
5661 ImGuiContext& g = *GImGui;
5662 const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5663 TextUnformatted(g.TempBuffer, text_end);
5666 void ImGui::Text(const char* fmt, ...)
5669 va_start(args, fmt);
5674 void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args)
5676 PushStyleColor(ImGuiCol_Text, col);
5681 void ImGui::TextColored(const ImVec4& col, const char* fmt, ...)
5684 va_start(args, fmt);
5685 TextColoredV(col, fmt, args);
5689 void ImGui::TextDisabledV(const char* fmt, va_list args)
5691 PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]);
5696 void ImGui::TextDisabled(const char* fmt, ...)
5699 va_start(args, fmt);
5700 TextDisabledV(fmt, args);
5704 void ImGui::TextWrappedV(const char* fmt, va_list args)
5706 bool need_wrap = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f); // Keep existing wrap position is one ia already set
5707 if (need_wrap) PushTextWrapPos(0.0f);
5709 if (need_wrap) PopTextWrapPos();
5712 void ImGui::TextWrapped(const char* fmt, ...)
5715 va_start(args, fmt);
5716 TextWrappedV(fmt, args);
5720 void ImGui::TextUnformatted(const char* text, const char* text_end)
5722 ImGuiWindow* window = GetCurrentWindow();
5723 if (window->SkipItems)
5726 ImGuiContext& g = *GImGui;
5727 IM_ASSERT(text != NULL);
5728 const char* text_begin = text;
5729 if (text_end == NULL)
5730 text_end = text + strlen(text); // FIXME-OPT
5732 const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset);
5733 const float wrap_pos_x = window->DC.TextWrapPos;
5734 const bool wrap_enabled = wrap_pos_x >= 0.0f;
5735 if (text_end - text > 2000 && !wrap_enabled)
5738 // Perform manual coarse clipping to optimize for long multi-line text
5739 // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.
5740 // We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line.
5741 const char* line = text;
5742 const float line_height = GetTextLineHeight();
5743 const ImRect clip_rect = window->ClipRect;
5744 ImVec2 text_size(0,0);
5746 if (text_pos.y <= clip_rect.Max.y)
5748 ImVec2 pos = text_pos;
5750 // Lines to skip (can't skip when logging text)
5753 int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height);
5754 if (lines_skippable > 0)
5756 int lines_skipped = 0;
5757 while (line < text_end && lines_skipped < lines_skippable)
5759 const char* line_end = strchr(line, '\n');
5761 line_end = text_end;
5762 line = line_end + 1;
5765 pos.y += lines_skipped * line_height;
5770 if (line < text_end)
5772 ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height));
5773 while (line < text_end)
5775 const char* line_end = strchr(line, '\n');
5776 if (IsClippedEx(line_rect, 0, false))
5779 const ImVec2 line_size = CalcTextSize(line, line_end, false);
5780 text_size.x = ImMax(text_size.x, line_size.x);
5781 RenderText(pos, line, line_end, false);
5783 line_end = text_end;
5784 line = line_end + 1;
5785 line_rect.Min.y += line_height;
5786 line_rect.Max.y += line_height;
5787 pos.y += line_height;
5790 // Count remaining lines
5791 int lines_skipped = 0;
5792 while (line < text_end)
5794 const char* line_end = strchr(line, '\n');
5796 line_end = text_end;
5797 line = line_end + 1;
5800 pos.y += lines_skipped * line_height;
5803 text_size.y += (pos - text_pos).y;
5806 ImRect bb(text_pos, text_pos + text_size);
5812 const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;
5813 const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);
5815 // Account of baseline offset
5816 ImRect bb(text_pos, text_pos + text_size);
5817 ItemSize(text_size);
5818 if (!ItemAdd(bb, 0))
5821 // Render (we don't hide text after ## in this end-user function)
5822 RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width);
5826 void ImGui::AlignTextToFramePadding()
5828 ImGuiWindow* window = GetCurrentWindow();
5829 if (window->SkipItems)
5832 ImGuiContext& g = *GImGui;
5833 window->DC.CurrentLineHeight = ImMax(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y * 2);
5834 window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
5837 // Add a label+text combo aligned to other label+value widgets
5838 void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
5840 ImGuiWindow* window = GetCurrentWindow();
5841 if (window->SkipItems)
5844 ImGuiContext& g = *GImGui;
5845 const ImGuiStyle& style = g.Style;
5846 const float w = CalcItemWidth();
5848 const ImVec2 label_size = CalcTextSize(label, NULL, true);
5849 const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2));
5850 const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y*2) + label_size);
5851 ItemSize(total_bb, style.FramePadding.y);
5852 if (!ItemAdd(total_bb, 0))
5856 const char* value_text_begin = &g.TempBuffer[0];
5857 const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5858 RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f,0.5f));
5859 if (label_size.x > 0.0f)
5860 RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
5863 void ImGui::LabelText(const char* label, const char* fmt, ...)
5866 va_start(args, fmt);
5867 LabelTextV(label, fmt, args);
5871 bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)
5873 ImGuiContext& g = *GImGui;
5874 ImGuiWindow* window = GetCurrentWindow();
5876 if (flags & ImGuiButtonFlags_Disabled)
5878 if (out_hovered) *out_hovered = false;
5879 if (out_held) *out_held = false;
5880 if (g.ActiveId == id) ClearActiveID();
5884 // Default behavior requires click+release on same spot
5885 if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0)
5886 flags |= ImGuiButtonFlags_PressedOnClickRelease;
5888 ImGuiWindow* backup_hovered_window = g.HoveredWindow;
5889 if ((flags & ImGuiButtonFlags_FlattenChilds) && g.HoveredRootWindow == window)
5890 g.HoveredWindow = window;
5892 bool pressed = false;
5893 bool hovered = ItemHoverable(bb, id);
5895 if ((flags & ImGuiButtonFlags_FlattenChilds) && g.HoveredRootWindow == window)
5896 g.HoveredWindow = backup_hovered_window;
5898 // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one.
5899 if (hovered && (flags & ImGuiButtonFlags_AllowOverlapMode) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0))
5904 if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
5906 // | CLICKING | HOLDING with ImGuiButtonFlags_Repeat
5907 // PressedOnClickRelease | <on release>* | <on repeat> <on repeat> .. (NOT on release) <-- MOST COMMON! (*) only if both click/release were over bounds
5908 // PressedOnClick | <on click> | <on click> <on repeat> <on repeat> ..
5909 // PressedOnRelease | <on release> | <on repeat> <on repeat> .. (NOT on release)
5910 // PressedOnDoubleClick | <on dclick> | <on dclick> <on repeat> <on repeat> ..
5911 if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0])
5913 SetActiveID(id, window); // Hold on ID
5914 FocusWindow(window);
5915 g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;
5917 if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0]))
5920 if (flags & ImGuiButtonFlags_NoHoldingActiveID)
5926 SetActiveID(id, window); // Hold on ID
5927 g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;
5929 FocusWindow(window);
5931 if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0])
5933 if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps <on release>
5938 // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above).
5939 // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings.
5940 if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true))
5946 if (g.ActiveId == id)
5948 if (g.IO.MouseDown[0])
5954 if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease))
5955 if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps <on release>
5961 if (out_hovered) *out_hovered = hovered;
5962 if (out_held) *out_held = held;
5967 bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags)
5969 ImGuiWindow* window = GetCurrentWindow();
5970 if (window->SkipItems)
5973 ImGuiContext& g = *GImGui;
5974 const ImGuiStyle& style = g.Style;
5975 const ImGuiID id = window->GetID(label);
5976 const ImVec2 label_size = CalcTextSize(label, NULL, true);
5978 ImVec2 pos = window->DC.CursorPos;
5979 if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
5980 pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y;
5981 ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);
5983 const ImRect bb(pos, pos + size);
5984 ItemSize(bb, style.FramePadding.y);
5985 if (!ItemAdd(bb, id))
5988 if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) flags |= ImGuiButtonFlags_Repeat;
5990 bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
5993 const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
5994 RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
5995 RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb);
5997 // Automatically close popups
5998 //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
5999 // CloseCurrentPopup();
6004 bool ImGui::Button(const char* label, const ImVec2& size_arg)
6006 return ButtonEx(label, size_arg, 0);
6009 // Small buttons fits within text without additional vertical spacing.
6010 bool ImGui::SmallButton(const char* label)
6012 ImGuiContext& g = *GImGui;
6013 float backup_padding_y = g.Style.FramePadding.y;
6014 g.Style.FramePadding.y = 0.0f;
6015 bool pressed = ButtonEx(label, ImVec2(0,0), ImGuiButtonFlags_AlignTextBaseLine);
6016 g.Style.FramePadding.y = backup_padding_y;
6020 // Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack.
6021 // Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id)
6022 bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg)
6024 ImGuiWindow* window = GetCurrentWindow();
6025 if (window->SkipItems)
6028 const ImGuiID id = window->GetID(str_id);
6029 ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f);
6030 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
6032 if (!ItemAdd(bb, id))
6036 bool pressed = ButtonBehavior(bb, id, &hovered, &held);
6041 // Upper-right button to close a window.
6042 bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius)
6044 ImGuiWindow* window = GetCurrentWindow();
6046 const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius));
6049 bool pressed = ButtonBehavior(bb, id, &hovered, &held);
6052 const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton);
6053 const ImVec2 center = bb.GetCenter();
6054 window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), col, 12);
6056 const float cross_extent = (radius * 0.7071f) - 1.0f;
6059 window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), GetColorU32(ImGuiCol_Text));
6060 window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), GetColorU32(ImGuiCol_Text));
6067 bool ImGui::ArrowButton(ImGuiID id, ImGuiDir dir, ImVec2 padding, ImGuiButtonFlags flags)
6069 ImGuiContext& g = *GImGui;
6070 ImGuiWindow* window = g.CurrentWindow;
6071 if (window->SkipItems)
6074 const ImGuiStyle& style = g.Style;
6076 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + padding.x * 2.0f, g.FontSize + padding.y * 2.0f));
6077 ItemSize(bb, style.FramePadding.y);
6078 if (!ItemAdd(bb, id))
6082 bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
6084 const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
6085 #ifdef IMGUI_HAS_NAV
6086 RenderNavHighlight(bb, id);
6088 RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
6089 RenderTriangle(bb.Min + padding, dir, 1.0f);
6094 void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
6096 ImGuiWindow* window = GetCurrentWindow();
6097 if (window->SkipItems)
6100 ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
6101 if (border_col.w > 0.0f)
6102 bb.Max += ImVec2(2,2);
6104 if (!ItemAdd(bb, 0))
6107 if (border_col.w > 0.0f)
6109 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f);
6110 window->DrawList->AddImage(user_texture_id, bb.Min+ImVec2(1,1), bb.Max-ImVec2(1,1), uv0, uv1, GetColorU32(tint_col));
6114 window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col));
6118 // frame_padding < 0: uses FramePadding from style (default)
6119 // frame_padding = 0: no framing
6120 // frame_padding > 0: set framing size
6121 // The color used are the button colors.
6122 bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col)
6124 ImGuiWindow* window = GetCurrentWindow();
6125 if (window->SkipItems)
6128 ImGuiContext& g = *GImGui;
6129 const ImGuiStyle& style = g.Style;
6131 // Default to using texture ID as ID. User can still push string/integer prefixes.
6132 // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV.
6133 PushID((void *)user_texture_id);
6134 const ImGuiID id = window->GetID("#image");
6137 const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding;
6138 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding*2);
6139 const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size);
6141 if (!ItemAdd(bb, id))
6145 bool pressed = ButtonBehavior(bb, id, &hovered, &held);
6148 const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
6149 RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding));
6150 if (bg_col.w > 0.0f)
6151 window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col));
6152 window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col));
6157 // Start logging ImGui output to TTY
6158 void ImGui::LogToTTY(int max_depth)
6160 ImGuiContext& g = *GImGui;
6163 ImGuiWindow* window = g.CurrentWindow;
6165 g.LogEnabled = true;
6167 g.LogStartDepth = window->DC.TreeDepth;
6169 g.LogAutoExpandMaxDepth = max_depth;
6172 // Start logging ImGui output to given file
6173 void ImGui::LogToFile(int max_depth, const char* filename)
6175 ImGuiContext& g = *GImGui;
6178 ImGuiWindow* window = g.CurrentWindow;
6182 filename = g.IO.LogFilename;
6187 g.LogFile = ImFileOpen(filename, "ab");
6190 IM_ASSERT(g.LogFile != NULL); // Consider this an error
6193 g.LogEnabled = true;
6194 g.LogStartDepth = window->DC.TreeDepth;
6196 g.LogAutoExpandMaxDepth = max_depth;
6199 // Start logging ImGui output to clipboard
6200 void ImGui::LogToClipboard(int max_depth)
6202 ImGuiContext& g = *GImGui;
6205 ImGuiWindow* window = g.CurrentWindow;
6207 g.LogEnabled = true;
6209 g.LogStartDepth = window->DC.TreeDepth;
6211 g.LogAutoExpandMaxDepth = max_depth;
6214 void ImGui::LogFinish()
6216 ImGuiContext& g = *GImGui;
6220 LogText(IM_NEWLINE);
6221 g.LogEnabled = false;
6222 if (g.LogFile != NULL)
6224 if (g.LogFile == stdout)
6230 if (g.LogClipboard->size() > 1)
6232 SetClipboardText(g.LogClipboard->begin());
6233 g.LogClipboard->clear();
6237 // Helper to display logging buttons
6238 void ImGui::LogButtons()
6240 ImGuiContext& g = *GImGui;
6242 PushID("LogButtons");
6243 const bool log_to_tty = Button("Log To TTY"); SameLine();
6244 const bool log_to_file = Button("Log To File"); SameLine();
6245 const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
6246 PushItemWidth(80.0f);
6247 PushAllowKeyboardFocus(false);
6248 SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL);
6249 PopAllowKeyboardFocus();
6253 // Start logging at the end of the function so that the buttons don't appear in the log
6255 LogToTTY(g.LogAutoExpandMaxDepth);
6257 LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename);
6258 if (log_to_clipboard)
6259 LogToClipboard(g.LogAutoExpandMaxDepth);
6262 bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags)
6264 if (flags & ImGuiTreeNodeFlags_Leaf)
6267 // We only write to the tree storage if the user clicks (or explicitely use SetNextTreeNode*** functions)
6268 ImGuiContext& g = *GImGui;
6269 ImGuiWindow* window = g.CurrentWindow;
6270 ImGuiStorage* storage = window->DC.StateStorage;
6273 if (g.SetNextTreeNodeOpenCond != 0)
6275 if (g.SetNextTreeNodeOpenCond & ImGuiCond_Always)
6277 is_open = g.SetNextTreeNodeOpenVal;
6278 storage->SetInt(id, is_open);
6282 // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently.
6283 const int stored_value = storage->GetInt(id, -1);
6284 if (stored_value == -1)
6286 is_open = g.SetNextTreeNodeOpenVal;
6287 storage->SetInt(id, is_open);
6291 is_open = stored_value != 0;
6294 g.SetNextTreeNodeOpenCond = 0;
6298 is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;
6301 // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior).
6302 // NB- If we are above max depth we still allow manually opened nodes to be logged.
6303 if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth)
6309 bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end)
6311 ImGuiWindow* window = GetCurrentWindow();
6312 if (window->SkipItems)
6315 ImGuiContext& g = *GImGui;
6316 const ImGuiStyle& style = g.Style;
6317 const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;
6318 const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f);
6321 label_end = FindRenderedTextEnd(label);
6322 const ImVec2 label_size = CalcTextSize(label, label_end, false);
6324 // We vertically grow up to current line height up the typical widget height.
6325 const float text_base_offset_y = ImMax(padding.y, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
6326 const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2);
6327 ImRect bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height));
6330 // Framed header expand a little outside the default padding
6331 bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1;
6332 bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1;
6335 const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2)); // Collapser arrow width + Spacing
6336 const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f); // Include collapser
6337 ItemSize(ImVec2(text_width, frame_height), text_base_offset_y);
6339 // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
6340 // (Ideally we'd want to add a flag for the user to specify if we want the hit test to be done up to the right side of the content or not)
6341 const ImRect interact_bb = display_frame ? bb : ImRect(bb.Min.x, bb.Min.y, bb.Min.x + text_width + style.ItemSpacing.x*2, bb.Max.y);
6342 bool is_open = TreeNodeBehaviorIsOpen(id, flags);
6343 if (!ItemAdd(interact_bb, id))
6345 if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
6350 // Flags that affects opening behavior:
6351 // - 0(default) ..................... single-click anywhere to open
6352 // - OpenOnDoubleClick .............. double-click anywhere to open
6353 // - OpenOnArrow .................... single-click on arrow to open
6354 // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open
6355 ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowOverlapMode) ? ImGuiButtonFlags_AllowOverlapMode : 0);
6356 if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
6357 button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0);
6358 bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags);
6359 if (pressed && !(flags & ImGuiTreeNodeFlags_Leaf))
6361 bool toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick));
6362 if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
6363 toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y));
6364 if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
6365 toggled |= g.IO.MouseDoubleClicked[0];
6369 window->DC.StateStorage->SetInt(id, is_open);
6372 if (flags & ImGuiTreeNodeFlags_AllowOverlapMode)
6373 SetItemAllowOverlap();
6376 const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
6377 const ImVec2 text_pos = bb.Min + ImVec2(text_offset_x, text_base_offset_y);
6381 RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
6382 RenderTriangle(bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f);
6385 // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here.
6386 const char log_prefix[] = "\n##";
6387 const char log_suffix[] = "##";
6388 LogRenderedText(&text_pos, log_prefix, log_prefix+3);
6389 RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size);
6390 LogRenderedText(&text_pos, log_suffix+1, log_suffix+3);
6394 RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size);
6399 // Unframed typed for tree nodes
6400 if (hovered || (flags & ImGuiTreeNodeFlags_Selected))
6401 RenderFrame(bb.Min, bb.Max, col, false);
6403 if (flags & ImGuiTreeNodeFlags_Bullet)
6404 RenderBullet(bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y));
6405 else if (!(flags & ImGuiTreeNodeFlags_Leaf))
6406 RenderTriangle(bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f);
6408 LogRenderedText(&text_pos, ">");
6409 RenderText(text_pos, label, label_end, false);
6412 if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
6417 // CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag).
6418 // This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode().
6419 bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags)
6421 ImGuiWindow* window = GetCurrentWindow();
6422 if (window->SkipItems)
6425 return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen, label);
6428 bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags)
6430 ImGuiWindow* window = GetCurrentWindow();
6431 if (window->SkipItems)
6434 if (p_open && !*p_open)
6437 ImGuiID id = window->GetID(label);
6438 bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen | (p_open ? ImGuiTreeNodeFlags_AllowOverlapMode : 0), label);
6441 // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.
6442 ImGuiContext& g = *GImGui;
6443 float button_sz = g.FontSize * 0.5f;
6444 ImGuiItemHoveredDataBackup last_item_backup;
6445 last_item_backup.Backup();
6446 if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_sz, window->DC.LastItemRect.Min.y + g.Style.FramePadding.y + button_sz), button_sz))
6448 last_item_backup.Restore();
6454 bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags)
6456 ImGuiWindow* window = GetCurrentWindow();
6457 if (window->SkipItems)
6460 return TreeNodeBehavior(window->GetID(label), flags, label, NULL);
6463 bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
6465 ImGuiWindow* window = GetCurrentWindow();
6466 if (window->SkipItems)
6469 ImGuiContext& g = *GImGui;
6470 const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
6471 return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end);
6474 bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
6476 ImGuiWindow* window = GetCurrentWindow();
6477 if (window->SkipItems)
6480 ImGuiContext& g = *GImGui;
6481 const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
6482 return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end);
6485 bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)
6487 return TreeNodeExV(str_id, 0, fmt, args);
6490 bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)
6492 return TreeNodeExV(ptr_id, 0, fmt, args);
6495 bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
6498 va_start(args, fmt);
6499 bool is_open = TreeNodeExV(str_id, flags, fmt, args);
6504 bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
6507 va_start(args, fmt);
6508 bool is_open = TreeNodeExV(ptr_id, flags, fmt, args);
6513 bool ImGui::TreeNode(const char* str_id, const char* fmt, ...)
6516 va_start(args, fmt);
6517 bool is_open = TreeNodeExV(str_id, 0, fmt, args);
6522 bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)
6525 va_start(args, fmt);
6526 bool is_open = TreeNodeExV(ptr_id, 0, fmt, args);
6531 bool ImGui::TreeNode(const char* label)
6533 ImGuiWindow* window = GetCurrentWindow();
6534 if (window->SkipItems)
6536 return TreeNodeBehavior(window->GetID(label), 0, label, NULL);
6539 void ImGui::TreeAdvanceToLabelPos()
6541 ImGuiContext& g = *GImGui;
6542 g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing();
6545 // Horizontal distance preceding label when using TreeNode() or Bullet()
6546 float ImGui::GetTreeNodeToLabelSpacing()
6548 ImGuiContext& g = *GImGui;
6549 return g.FontSize + (g.Style.FramePadding.x * 2.0f);
6552 void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiCond cond)
6554 ImGuiContext& g = *GImGui;
6555 if (g.CurrentWindow->SkipItems)
6557 g.SetNextTreeNodeOpenVal = is_open;
6558 g.SetNextTreeNodeOpenCond = cond ? cond : ImGuiCond_Always;
6561 void ImGui::PushID(const char* str_id)
6563 ImGuiWindow* window = GetCurrentWindowRead();
6564 window->IDStack.push_back(window->GetID(str_id));
6567 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6569 ImGuiWindow* window = GetCurrentWindowRead();
6570 window->IDStack.push_back(window->GetID(str_id_begin, str_id_end));
6573 void ImGui::PushID(const void* ptr_id)
6575 ImGuiWindow* window = GetCurrentWindowRead();
6576 window->IDStack.push_back(window->GetID(ptr_id));
6579 void ImGui::PushID(int int_id)
6581 const void* ptr_id = (void*)(intptr_t)int_id;
6582 ImGuiWindow* window = GetCurrentWindowRead();
6583 window->IDStack.push_back(window->GetID(ptr_id));
6588 ImGuiWindow* window = GetCurrentWindowRead();
6589 window->IDStack.pop_back();
6592 ImGuiID ImGui::GetID(const char* str_id)
6594 return GImGui->CurrentWindow->GetID(str_id);
6597 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6599 return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end);
6602 ImGuiID ImGui::GetID(const void* ptr_id)
6604 return GImGui->CurrentWindow->GetID(ptr_id);
6607 void ImGui::Bullet()
6609 ImGuiWindow* window = GetCurrentWindow();
6610 if (window->SkipItems)
6613 ImGuiContext& g = *GImGui;
6614 const ImGuiStyle& style = g.Style;
6615 const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
6616 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height));
6618 if (!ItemAdd(bb, 0))
6620 SameLine(0, style.FramePadding.x*2);
6624 // Render and stay on same line
6625 RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
6626 SameLine(0, style.FramePadding.x*2);
6629 // Text with a little bullet aligned to the typical tree node.
6630 void ImGui::BulletTextV(const char* fmt, va_list args)
6632 ImGuiWindow* window = GetCurrentWindow();
6633 if (window->SkipItems)
6636 ImGuiContext& g = *GImGui;
6637 const ImGuiStyle& style = g.Style;
6639 const char* text_begin = g.TempBuffer;
6640 const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
6641 const ImVec2 label_size = CalcTextSize(text_begin, text_end, false);
6642 const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
6643 const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
6644 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x*2) : 0.0f), ImMax(line_height, label_size.y))); // Empty text doesn't add padding
6646 if (!ItemAdd(bb, 0))
6650 RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
6651 RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end, false);
6654 void ImGui::BulletText(const char* fmt, ...)
6657 va_start(args, fmt);
6658 BulletTextV(fmt, args);
6662 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size)
6664 if (data_type == ImGuiDataType_Int)
6665 ImFormatString(buf, buf_size, display_format, *(int*)data_ptr);
6666 else if (data_type == ImGuiDataType_Float)
6667 ImFormatString(buf, buf_size, display_format, *(float*)data_ptr);
6670 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size)
6672 if (data_type == ImGuiDataType_Int)
6674 if (decimal_precision < 0)
6675 ImFormatString(buf, buf_size, "%d", *(int*)data_ptr);
6677 ImFormatString(buf, buf_size, "%.*d", decimal_precision, *(int*)data_ptr);
6679 else if (data_type == ImGuiDataType_Float)
6681 if (decimal_precision < 0)
6682 ImFormatString(buf, buf_size, "%f", *(float*)data_ptr); // Ideally we'd have a minimum decimal precision of 1 to visually denote that it is a float, while hiding non-significant digits?
6684 ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(float*)data_ptr);
6688 static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2)// Store into value1
6690 if (data_type == ImGuiDataType_Int)
6693 *(int*)value1 = *(int*)value1 + *(const int*)value2;
6695 *(int*)value1 = *(int*)value1 - *(const int*)value2;
6697 else if (data_type == ImGuiDataType_Float)
6700 *(float*)value1 = *(float*)value1 + *(const float*)value2;
6702 *(float*)value1 = *(float*)value1 - *(const float*)value2;
6706 // User can input math operators (e.g. +100) to edit a numerical values.
6707 static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format)
6709 while (ImCharIsSpace(*buf))
6712 // We don't support '-' op because it would conflict with inputing negative value.
6713 // Instead you can use +-100 to subtract from an existing value
6715 if (op == '+' || op == '*' || op == '/')
6718 while (ImCharIsSpace(*buf))
6728 if (data_type == ImGuiDataType_Int)
6731 scalar_format = "%d";
6732 int* v = (int*)data_ptr;
6733 const int old_v = *v;
6735 if (op && sscanf(initial_value_buf, scalar_format, &arg0i) < 1)
6738 // Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision
6740 if (op == '+') { if (sscanf(buf, "%f", &arg1f) == 1) *v = (int)(arg0i + arg1f); } // Add (use "+-" to subtract)
6741 else if (op == '*') { if (sscanf(buf, "%f", &arg1f) == 1) *v = (int)(arg0i * arg1f); } // Multiply
6742 else if (op == '/') { if (sscanf(buf, "%f", &arg1f) == 1 && arg1f != 0.0f) *v = (int)(arg0i / arg1f); }// Divide
6743 else { if (sscanf(buf, scalar_format, &arg0i) == 1) *v = arg0i; } // Assign constant (read as integer so big values are not lossy)
6744 return (old_v != *v);
6746 else if (data_type == ImGuiDataType_Float)
6748 // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in
6749 scalar_format = "%f";
6750 float* v = (float*)data_ptr;
6751 const float old_v = *v;
6753 if (op && sscanf(initial_value_buf, scalar_format, &arg0f) < 1)
6757 if (sscanf(buf, scalar_format, &arg1f) < 1)
6759 if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract)
6760 else if (op == '*') { *v = arg0f * arg1f; } // Multiply
6761 else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide
6762 else { *v = arg1f; } // Assign constant
6763 return (old_v != *v);
6769 // Create text input in place of a slider (when CTRL+Clicking on slider)
6770 // FIXME: Logic is messy and confusing.
6771 bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision)
6773 ImGuiContext& g = *GImGui;
6774 ImGuiWindow* window = GetCurrentWindow();
6776 // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen)
6777 // On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id
6778 SetActiveID(g.ScalarAsInputTextId, window);
6780 FocusableItemUnregister(window);
6783 DataTypeFormatString(data_type, data_ptr, decimal_precision, buf, IM_ARRAYSIZE(buf));
6784 bool text_value_changed = InputTextEx(label, buf, IM_ARRAYSIZE(buf), aabb.GetSize(), ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll);
6785 if (g.ScalarAsInputTextId == 0) // First frame we started displaying the InputText widget
6787 IM_ASSERT(g.ActiveId == id); // InputText ID expected to match the Slider ID (else we'd need to store them both, which is also possible)
6788 g.ScalarAsInputTextId = g.ActiveId;
6791 if (text_value_changed)
6792 return DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, NULL);
6796 // Parse display precision back from the display format string
6797 int ImGui::ParseFormatPrecision(const char* fmt, int default_precision)
6799 int precision = default_precision;
6800 while ((fmt = strchr(fmt, '%')) != NULL)
6803 if (fmt[0] == '%') { fmt++; continue; } // Ignore "%%"
6804 while (*fmt >= '0' && *fmt <= '9')
6808 fmt = ImAtoi(fmt + 1, &precision);
6809 if (precision < 0 || precision > 10)
6810 precision = default_precision;
6812 if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation
6819 static float GetMinimumStepAtDecimalPrecision(int decimal_precision)
6821 static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f };
6822 return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision);
6825 float ImGui::RoundScalar(float value, int decimal_precision)
6827 // Round past decimal precision
6828 // So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0
6829 // FIXME: Investigate better rounding methods
6830 if (decimal_precision < 0)
6832 const float min_step = GetMinimumStepAtDecimalPrecision(decimal_precision);
6833 bool negative = value < 0.0f;
6834 value = fabsf(value);
6835 float remainder = fmodf(value, min_step);
6836 if (remainder <= min_step*0.5f)
6839 value += (min_step - remainder);
6840 return negative ? -value : value;
6843 static inline float SliderBehaviorCalcRatioFromValue(float v, float v_min, float v_max, float power, float linear_zero_pos)
6848 const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f);
6849 const float v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min);
6852 if (v_clamped < 0.0f)
6854 const float f = 1.0f - (v_clamped - v_min) / (ImMin(0.0f,v_max) - v_min);
6855 return (1.0f - powf(f, 1.0f/power)) * linear_zero_pos;
6859 const float f = (v_clamped - ImMax(0.0f,v_min)) / (v_max - ImMax(0.0f,v_min));
6860 return linear_zero_pos + powf(f, 1.0f/power) * (1.0f - linear_zero_pos);
6865 return (v_clamped - v_min) / (v_max - v_min);
6868 bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags)
6870 ImGuiContext& g = *GImGui;
6871 ImGuiWindow* window = GetCurrentWindow();
6872 const ImGuiStyle& style = g.Style;
6875 RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
6877 const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f);
6878 const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0;
6880 const float grab_padding = 2.0f;
6881 const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f);
6883 if (decimal_precision != 0)
6884 grab_sz = ImMin(style.GrabMinSize, slider_sz);
6886 grab_sz = ImMin(ImMax(1.0f * (slider_sz / ((v_min < v_max ? v_max - v_min : v_min - v_max) + 1.0f)), style.GrabMinSize), slider_sz); // Integer sliders, if possible have the grab size represent 1 unit
6887 const float slider_usable_sz = slider_sz - grab_sz;
6888 const float slider_usable_pos_min = (is_horizontal ? frame_bb.Min.x : frame_bb.Min.y) + grab_padding + grab_sz*0.5f;
6889 const float slider_usable_pos_max = (is_horizontal ? frame_bb.Max.x : frame_bb.Max.y) - grab_padding - grab_sz*0.5f;
6891 // For logarithmic sliders that cross over sign boundary we want the exponential increase to be symmetric around 0.0f
6892 float linear_zero_pos = 0.0f; // 0.0->1.0f
6893 if (v_min * v_max < 0.0f)
6896 const float linear_dist_min_to_0 = powf(fabsf(0.0f - v_min), 1.0f/power);
6897 const float linear_dist_max_to_0 = powf(fabsf(v_max - 0.0f), 1.0f/power);
6898 linear_zero_pos = linear_dist_min_to_0 / (linear_dist_min_to_0+linear_dist_max_to_0);
6903 linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f;
6906 // Process clicking on the slider
6907 bool value_changed = false;
6908 if (g.ActiveId == id)
6910 bool set_new_value = false;
6911 float clicked_t = 0.0f;
6912 if (g.IO.MouseDown[0])
6914 const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
6915 clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f;
6917 clicked_t = 1.0f - clicked_t;
6918 set_new_value = true;
6930 // Account for logarithmic scale on both sides of the zero
6931 if (clicked_t < linear_zero_pos)
6933 // Negative: rescale to the negative range before powering
6934 float a = 1.0f - (clicked_t / linear_zero_pos);
6936 new_value = ImLerp(ImMin(v_max,0.0f), v_min, a);
6940 // Positive: rescale to the positive range before powering
6942 if (fabsf(linear_zero_pos - 1.0f) > 1.e-6f)
6943 a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos);
6947 new_value = ImLerp(ImMax(v_min,0.0f), v_max, a);
6953 new_value = ImLerp(v_min, v_max, clicked_t);
6956 // Round past decimal precision
6957 new_value = RoundScalar(new_value, decimal_precision);
6958 if (*v != new_value)
6961 value_changed = true;
6967 float grab_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos);
6969 grab_t = 1.0f - grab_t;
6970 const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
6973 grab_bb = ImRect(ImVec2(grab_pos - grab_sz*0.5f, frame_bb.Min.y + grab_padding), ImVec2(grab_pos + grab_sz*0.5f, frame_bb.Max.y - grab_padding));
6975 grab_bb = ImRect(ImVec2(frame_bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f), ImVec2(frame_bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f));
6976 window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
6978 return value_changed;
6981 // Use power!=1.0 for logarithmic sliders.
6982 // Adjust display_format to decorate the value with a prefix or a suffix.
6984 // "%5.2f secs" 01.23 secs
6985 // "Gold: %.0f" Gold: 1
6986 bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format, float power)
6988 ImGuiWindow* window = GetCurrentWindow();
6989 if (window->SkipItems)
6992 ImGuiContext& g = *GImGui;
6993 const ImGuiStyle& style = g.Style;
6994 const ImGuiID id = window->GetID(label);
6995 const float w = CalcItemWidth();
6997 const ImVec2 label_size = CalcTextSize(label, NULL, true);
6998 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
6999 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
7001 // NB- we don't call ItemSize() yet because we may turn into a text edit box below
7002 if (!ItemAdd(total_bb, id))
7004 ItemSize(total_bb, style.FramePadding.y);
7007 const bool hovered = ItemHoverable(frame_bb, id);
7009 if (!display_format)
7010 display_format = "%.3f";
7011 int decimal_precision = ParseFormatPrecision(display_format, 3);
7013 // Tabbing or CTRL-clicking on Slider turns it into an input box
7014 bool start_text_input = false;
7015 const bool tab_focus_requested = FocusableItemRegister(window, id);
7016 if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]))
7018 SetActiveID(id, window);
7019 FocusWindow(window);
7020 if (tab_focus_requested || g.IO.KeyCtrl)
7022 start_text_input = true;
7023 g.ScalarAsInputTextId = 0;
7026 if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
7027 return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
7029 // Actual slider behavior + render grab
7030 ItemSize(total_bb, style.FramePadding.y);
7031 const bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision);
7033 // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
7035 const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
7036 RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
7038 if (label_size.x > 0.0f)
7039 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
7041 return value_changed;
7044 bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format, float power)
7046 ImGuiWindow* window = GetCurrentWindow();
7047 if (window->SkipItems)
7050 ImGuiContext& g = *GImGui;
7051 const ImGuiStyle& style = g.Style;
7052 const ImGuiID id = window->GetID(label);
7054 const ImVec2 label_size = CalcTextSize(label, NULL, true);
7055 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
7056 const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
7058 ItemSize(bb, style.FramePadding.y);
7059 if (!ItemAdd(frame_bb, id))
7061 const bool hovered = ItemHoverable(frame_bb, id);
7063 if (!display_format)
7064 display_format = "%.3f";
7065 int decimal_precision = ParseFormatPrecision(display_format, 3);
7067 if (hovered && g.IO.MouseClicked[0])
7069 SetActiveID(id, window);
7070 FocusWindow(window);
7073 // Actual slider behavior + render grab
7074 bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision, ImGuiSliderFlags_Vertical);
7076 // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
7077 // For the vertical slider we allow centered text to overlap the frame padding
7079 char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
7080 RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.0f));
7081 if (label_size.x > 0.0f)
7082 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
7084 return value_changed;
7087 bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max)
7089 float v_deg = (*v_rad) * 360.0f / (2*IM_PI);
7090 bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f);
7091 *v_rad = v_deg * (2*IM_PI) / 360.0f;
7092 return value_changed;
7095 bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format)
7097 if (!display_format)
7098 display_format = "%.0f";
7099 float v_f = (float)*v;
7100 bool value_changed = SliderFloat(label, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
7102 return value_changed;
7105 bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format)
7107 if (!display_format)
7108 display_format = "%.0f";
7109 float v_f = (float)*v;
7110 bool value_changed = VSliderFloat(label, size, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
7112 return value_changed;
7115 // Add multiple sliders on 1 line for compact edition of multiple components
7116 bool ImGui::SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power)
7118 ImGuiWindow* window = GetCurrentWindow();
7119 if (window->SkipItems)
7122 ImGuiContext& g = *GImGui;
7123 bool value_changed = false;
7126 PushMultiItemsWidths(components);
7127 for (int i = 0; i < components; i++)
7130 value_changed |= SliderFloat("##v", &v[i], v_min, v_max, display_format, power);
7131 SameLine(0, g.Style.ItemInnerSpacing.x);
7137 TextUnformatted(label, FindRenderedTextEnd(label));
7140 return value_changed;
7143 bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format, float power)
7145 return SliderFloatN(label, v, 2, v_min, v_max, display_format, power);
7148 bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format, float power)
7150 return SliderFloatN(label, v, 3, v_min, v_max, display_format, power);
7153 bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format, float power)
7155 return SliderFloatN(label, v, 4, v_min, v_max, display_format, power);
7158 bool ImGui::SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format)
7160 ImGuiWindow* window = GetCurrentWindow();
7161 if (window->SkipItems)
7164 ImGuiContext& g = *GImGui;
7165 bool value_changed = false;
7168 PushMultiItemsWidths(components);
7169 for (int i = 0; i < components; i++)
7172 value_changed |= SliderInt("##v", &v[i], v_min, v_max, display_format);
7173 SameLine(0, g.Style.ItemInnerSpacing.x);
7179 TextUnformatted(label, FindRenderedTextEnd(label));
7182 return value_changed;
7185 bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format)
7187 return SliderIntN(label, v, 2, v_min, v_max, display_format);
7190 bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format)
7192 return SliderIntN(label, v, 3, v_min, v_max, display_format);
7195 bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format)
7197 return SliderIntN(label, v, 4, v_min, v_max, display_format);
7200 bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power)
7202 ImGuiContext& g = *GImGui;
7203 const ImGuiStyle& style = g.Style;
7206 const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
7207 RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
7209 bool value_changed = false;
7211 // Process clicking on the drag
7212 if (g.ActiveId == id)
7214 if (g.IO.MouseDown[0])
7216 if (g.ActiveIdIsJustActivated)
7218 // Lock current value on click
7219 g.DragCurrentValue = *v;
7220 g.DragLastMouseDelta = ImVec2(0.f, 0.f);
7223 if (v_speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX)
7224 v_speed = (v_max - v_min) * g.DragSpeedDefaultRatio;
7226 float v_cur = g.DragCurrentValue;
7227 const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f);
7228 float adjust_delta = 0.0f;
7229 //if (g.ActiveIdSource == ImGuiInputSource_Mouse)
7231 adjust_delta = mouse_drag_delta.x - g.DragLastMouseDelta.x;
7232 if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f)
7233 adjust_delta *= g.DragSpeedScaleFast;
7234 if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f)
7235 adjust_delta *= g.DragSpeedScaleSlow;
7237 adjust_delta *= v_speed;
7238 g.DragLastMouseDelta.x = mouse_drag_delta.x;
7240 if (fabsf(adjust_delta) > 0.0f)
7242 if (fabsf(power - 1.0f) > 0.001f)
7244 // Logarithmic curve on both side of 0.0
7245 float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur;
7246 float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f;
7247 float v1 = powf(v0_abs, 1.0f / power) + (adjust_delta * v0_sign);
7248 float v1_abs = v1 >= 0.0f ? v1 : -v1;
7249 float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f; // Crossed sign line
7250 v_cur = powf(v1_abs, power) * v0_sign * v1_sign; // Reapply sign
7254 v_cur += adjust_delta;
7259 v_cur = ImClamp(v_cur, v_min, v_max);
7260 g.DragCurrentValue = v_cur;
7263 // Round to user desired precision, then apply
7264 v_cur = RoundScalar(v_cur, decimal_precision);
7268 value_changed = true;
7277 return value_changed;
7280 bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* display_format, float power)
7282 ImGuiWindow* window = GetCurrentWindow();
7283 if (window->SkipItems)
7286 ImGuiContext& g = *GImGui;
7287 const ImGuiStyle& style = g.Style;
7288 const ImGuiID id = window->GetID(label);
7289 const float w = CalcItemWidth();
7291 const ImVec2 label_size = CalcTextSize(label, NULL, true);
7292 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
7293 const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
7294 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
7296 // NB- we don't call ItemSize() yet because we may turn into a text edit box below
7297 if (!ItemAdd(total_bb, id))
7299 ItemSize(total_bb, style.FramePadding.y);
7302 const bool hovered = ItemHoverable(frame_bb, id);
7304 if (!display_format)
7305 display_format = "%.3f";
7306 int decimal_precision = ParseFormatPrecision(display_format, 3);
7308 // Tabbing or CTRL-clicking on Drag turns it into an input box
7309 bool start_text_input = false;
7310 const bool tab_focus_requested = FocusableItemRegister(window, id);
7311 if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])))
7313 SetActiveID(id, window);
7314 FocusWindow(window);
7315 if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0])
7317 start_text_input = true;
7318 g.ScalarAsInputTextId = 0;
7321 if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
7322 return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
7324 // Actual drag behavior
7325 ItemSize(total_bb, style.FramePadding.y);
7326 const bool value_changed = DragBehavior(frame_bb, id, v, v_speed, v_min, v_max, decimal_precision, power);
7328 // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
7330 const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
7331 RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
7333 if (label_size.x > 0.0f)
7334 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
7336 return value_changed;
7339 bool ImGui::DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power)
7341 ImGuiWindow* window = GetCurrentWindow();
7342 if (window->SkipItems)
7345 ImGuiContext& g = *GImGui;
7346 bool value_changed = false;
7349 PushMultiItemsWidths(components);
7350 for (int i = 0; i < components; i++)
7353 value_changed |= DragFloat("##v", &v[i], v_speed, v_min, v_max, display_format, power);
7354 SameLine(0, g.Style.ItemInnerSpacing.x);
7360 TextUnformatted(label, FindRenderedTextEnd(label));
7363 return value_changed;
7366 bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power)
7368 return DragFloatN(label, v, 2, v_speed, v_min, v_max, display_format, power);
7371 bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* display_format, float power)
7373 return DragFloatN(label, v, 3, v_speed, v_min, v_max, display_format, power);
7376 bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* display_format, float power)
7378 return DragFloatN(label, v, 4, v_speed, v_min, v_max, display_format, power);
7381 bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* display_format, const char* display_format_max, float power)
7383 ImGuiWindow* window = GetCurrentWindow();
7384 if (window->SkipItems)
7387 ImGuiContext& g = *GImGui;
7390 PushMultiItemsWidths(2);
7392 bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format, power);
7394 SameLine(0, g.Style.ItemInnerSpacing.x);
7395 value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, display_format_max ? display_format_max : display_format, power);
7397 SameLine(0, g.Style.ItemInnerSpacing.x);
7399 TextUnformatted(label, FindRenderedTextEnd(label));
7403 return value_changed;
7406 // NB: v_speed is float to allow adjusting the drag speed with more precision
7407 bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* display_format)
7409 if (!display_format)
7410 display_format = "%.0f";
7411 float v_f = (float)*v;
7412 bool value_changed = DragFloat(label, &v_f, v_speed, (float)v_min, (float)v_max, display_format);
7414 return value_changed;
7417 bool ImGui::DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format)
7419 ImGuiWindow* window = GetCurrentWindow();
7420 if (window->SkipItems)
7423 ImGuiContext& g = *GImGui;
7424 bool value_changed = false;
7427 PushMultiItemsWidths(components);
7428 for (int i = 0; i < components; i++)
7431 value_changed |= DragInt("##v", &v[i], v_speed, v_min, v_max, display_format);
7432 SameLine(0, g.Style.ItemInnerSpacing.x);
7438 TextUnformatted(label, FindRenderedTextEnd(label));
7441 return value_changed;
7444 bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* display_format)
7446 return DragIntN(label, v, 2, v_speed, v_min, v_max, display_format);
7449 bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* display_format)
7451 return DragIntN(label, v, 3, v_speed, v_min, v_max, display_format);
7454 bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* display_format)
7456 return DragIntN(label, v, 4, v_speed, v_min, v_max, display_format);
7459 bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* display_format, const char* display_format_max)
7461 ImGuiWindow* window = GetCurrentWindow();
7462 if (window->SkipItems)
7465 ImGuiContext& g = *GImGui;
7468 PushMultiItemsWidths(2);
7470 bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format);
7472 SameLine(0, g.Style.ItemInnerSpacing.x);
7473 value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, display_format_max ? display_format_max : display_format);
7475 SameLine(0, g.Style.ItemInnerSpacing.x);
7477 TextUnformatted(label, FindRenderedTextEnd(label));
7481 return value_changed;
7484 void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
7486 ImGuiWindow* window = GetCurrentWindow();
7487 if (window->SkipItems)
7490 ImGuiContext& g = *GImGui;
7491 const ImGuiStyle& style = g.Style;
7493 const ImVec2 label_size = CalcTextSize(label, NULL, true);
7494 if (graph_size.x == 0.0f)
7495 graph_size.x = CalcItemWidth();
7496 if (graph_size.y == 0.0f)
7497 graph_size.y = label_size.y + (style.FramePadding.y * 2);
7499 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y));
7500 const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
7501 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0));
7502 ItemSize(total_bb, style.FramePadding.y);
7503 if (!ItemAdd(total_bb, 0))
7505 const bool hovered = ItemHoverable(inner_bb, 0);
7507 // Determine scale from values if not specified
7508 if (scale_min == FLT_MAX || scale_max == FLT_MAX)
7510 float v_min = FLT_MAX;
7511 float v_max = -FLT_MAX;
7512 for (int i = 0; i < values_count; i++)
7514 const float v = values_getter(data, i);
7515 v_min = ImMin(v_min, v);
7516 v_max = ImMax(v_max, v);
7518 if (scale_min == FLT_MAX)
7520 if (scale_max == FLT_MAX)
7524 RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
7526 if (values_count > 0)
7528 int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
7529 int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
7535 const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);
7536 const int v_idx = (int)(t * item_count);
7537 IM_ASSERT(v_idx >= 0 && v_idx < values_count);
7539 const float v0 = values_getter(data, (v_idx + values_offset) % values_count);
7540 const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count);
7541 if (plot_type == ImGuiPlotType_Lines)
7542 SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1);
7543 else if (plot_type == ImGuiPlotType_Histogram)
7544 SetTooltip("%d: %8.4g", v_idx, v0);
7548 const float t_step = 1.0f / (float)res_w;
7550 float v0 = values_getter(data, (0 + values_offset) % values_count);
7552 ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) / (scale_max - scale_min)) ); // Point in the normalized space of our target rectangle
7553 float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (-scale_min / (scale_max - scale_min)) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands
7555 const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram);
7556 const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered);
7558 for (int n = 0; n < res_w; n++)
7560 const float t1 = t0 + t_step;
7561 const int v1_idx = (int)(t0 * item_count + 0.5f);
7562 IM_ASSERT(v1_idx >= 0 && v1_idx < values_count);
7563 const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count);
7564 const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) / (scale_max - scale_min)) );
7566 // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU.
7567 ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0);
7568 ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t));
7569 if (plot_type == ImGuiPlotType_Lines)
7571 window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
7573 else if (plot_type == ImGuiPlotType_Histogram)
7575 if (pos1.x >= pos0.x + 2.0f)
7577 window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
7587 RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f,0.0f));
7589 if (label_size.x > 0.0f)
7590 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
7593 struct ImGuiPlotArrayGetterData
7595 const float* Values;
7598 ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; }
7601 static float Plot_ArrayGetter(void* data, int idx)
7603 ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data;
7604 const float v = *(float*)(void*)((unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);
7608 void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)
7610 ImGuiPlotArrayGetterData data(values, stride);
7611 PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
7614 void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
7616 PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
7619 void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)
7621 ImGuiPlotArrayGetterData data(values, stride);
7622 PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
7625 void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
7627 PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
7630 // size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size
7631 void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay)
7633 ImGuiWindow* window = GetCurrentWindow();
7634 if (window->SkipItems)
7637 ImGuiContext& g = *GImGui;
7638 const ImGuiStyle& style = g.Style;
7640 ImVec2 pos = window->DC.CursorPos;
7641 ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f));
7642 ItemSize(bb, style.FramePadding.y);
7643 if (!ItemAdd(bb, 0))
7647 fraction = ImSaturate(fraction);
7648 RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
7649 bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize));
7650 const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y);
7651 RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding);
7653 // Default displaying the fraction as percentage string, but user can override it
7654 char overlay_buf[32];
7657 ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f);
7658 overlay = overlay_buf;
7661 ImVec2 overlay_size = CalcTextSize(overlay, NULL);
7662 if (overlay_size.x > 0.0f)
7663 RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f,0.5f), &bb);
7666 bool ImGui::Checkbox(const char* label, bool* v)
7668 ImGuiWindow* window = GetCurrentWindow();
7669 if (window->SkipItems)
7672 ImGuiContext& g = *GImGui;
7673 const ImGuiStyle& style = g.Style;
7674 const ImGuiID id = window->GetID(label);
7675 const ImVec2 label_size = CalcTextSize(label, NULL, true);
7677 const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2, label_size.y + style.FramePadding.y*2)); // We want a square shape to we use Y twice
7678 ItemSize(check_bb, style.FramePadding.y);
7680 ImRect total_bb = check_bb;
7681 if (label_size.x > 0)
7682 SameLine(0, style.ItemInnerSpacing.x);
7683 const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size);
7684 if (label_size.x > 0)
7686 ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
7687 total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max));
7690 if (!ItemAdd(total_bb, id))
7694 bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
7698 RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
7701 const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
7702 const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
7703 RenderCheckMark(check_bb.Min + ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), check_bb.GetWidth() - pad*2.0f);
7707 LogRenderedText(&text_bb.Min, *v ? "[x]" : "[ ]");
7708 if (label_size.x > 0.0f)
7709 RenderText(text_bb.Min, label);
7714 bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value)
7716 bool v = ((*flags & flags_value) == flags_value);
7717 bool pressed = Checkbox(label, &v);
7721 *flags |= flags_value;
7723 *flags &= ~flags_value;
7729 bool ImGui::RadioButton(const char* label, bool active)
7731 ImGuiWindow* window = GetCurrentWindow();
7732 if (window->SkipItems)
7735 ImGuiContext& g = *GImGui;
7736 const ImGuiStyle& style = g.Style;
7737 const ImGuiID id = window->GetID(label);
7738 const ImVec2 label_size = CalcTextSize(label, NULL, true);
7740 const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2-1, label_size.y + style.FramePadding.y*2-1));
7741 ItemSize(check_bb, style.FramePadding.y);
7743 ImRect total_bb = check_bb;
7744 if (label_size.x > 0)
7745 SameLine(0, style.ItemInnerSpacing.x);
7746 const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size);
7747 if (label_size.x > 0)
7749 ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
7750 total_bb.Add(text_bb);
7753 if (!ItemAdd(total_bb, id))
7756 ImVec2 center = check_bb.GetCenter();
7757 center.x = (float)(int)center.x + 0.5f;
7758 center.y = (float)(int)center.y + 0.5f;
7759 const float radius = check_bb.GetHeight() * 0.5f;
7762 bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
7764 window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16);
7767 const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
7768 const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
7769 window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16);
7772 if (style.FrameBorderSize > 0.0f)
7774 window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize);
7775 window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize);
7779 LogRenderedText(&text_bb.Min, active ? "(x)" : "( )");
7780 if (label_size.x > 0.0f)
7781 RenderText(text_bb.Min, label);
7786 bool ImGui::RadioButton(const char* label, int* v, int v_button)
7788 const bool pressed = RadioButton(label, *v == v_button);
7796 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
7799 const char* s = text_begin;
7800 while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding
7804 if (s[0] != '\n' && s[0] != '\r')
7810 static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line)
7812 ImFont* font = GImGui->Font;
7813 const float line_height = GImGui->FontSize;
7814 const float scale = line_height / font->FontSize;
7816 ImVec2 text_size = ImVec2(0,0);
7817 float line_width = 0.0f;
7819 const ImWchar* s = text_begin;
7820 while (s < text_end)
7822 unsigned int c = (unsigned int)(*s++);
7825 text_size.x = ImMax(text_size.x, line_width);
7826 text_size.y += line_height;
7828 if (stop_on_new_line)
7835 const float char_width = font->GetCharAdvance((unsigned short)c) * scale;
7836 line_width += char_width;
7839 if (text_size.x < line_width)
7840 text_size.x = line_width;
7843 *out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n
7845 if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n
7846 text_size.y += line_height;
7854 // Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar)
7858 static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; }
7859 static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->Text[idx]; }
7860 static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->Text[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); }
7861 static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; }
7862 static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
7863 static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
7865 const ImWchar* text = obj->Text.Data;
7866 const ImWchar* text_remaining = NULL;
7867 const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true);
7870 r->baseline_y_delta = size.y;
7873 r->num_chars = (int)(text_remaining - (text + line_start_idx));
7876 static bool is_separator(unsigned int c) { return ImCharIsSpace(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; }
7877 static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator( obj->Text[idx-1] ) && !is_separator( obj->Text[idx] ) ) : 1; }
7878 static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
7879 #ifdef __APPLE__ // FIXME: Move setting to IO structure
7880 static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator( obj->Text[idx-1] ) && is_separator( obj->Text[idx] ) ) : 1; }
7881 static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
7883 static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
7885 #define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h
7886 #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
7888 static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
7890 ImWchar* dst = obj->Text.Data + pos;
7892 // We maintain our buffer length in both UTF-8 and wchar formats
7893 obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);
7896 // Offset remaining text
7897 const ImWchar* src = obj->Text.Data + pos + n;
7898 while (ImWchar c = *src++)
7903 static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len)
7905 const int text_len = obj->CurLenW;
7906 IM_ASSERT(pos <= text_len);
7907 if (new_text_len + text_len + 1 > obj->Text.Size)
7910 const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
7911 if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA)
7914 ImWchar* text = obj->Text.Data;
7915 if (pos != text_len)
7916 memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));
7917 memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
7919 obj->CurLenW += new_text_len;
7920 obj->CurLenA += new_text_len_utf8;
7921 obj->Text[obj->CurLenW] = '\0';
7926 // We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols)
7927 #define STB_TEXTEDIT_K_LEFT 0x10000 // keyboard input to move cursor left
7928 #define STB_TEXTEDIT_K_RIGHT 0x10001 // keyboard input to move cursor right
7929 #define STB_TEXTEDIT_K_UP 0x10002 // keyboard input to move cursor up
7930 #define STB_TEXTEDIT_K_DOWN 0x10003 // keyboard input to move cursor down
7931 #define STB_TEXTEDIT_K_LINESTART 0x10004 // keyboard input to move cursor to start of line
7932 #define STB_TEXTEDIT_K_LINEEND 0x10005 // keyboard input to move cursor to end of line
7933 #define STB_TEXTEDIT_K_TEXTSTART 0x10006 // keyboard input to move cursor to start of text
7934 #define STB_TEXTEDIT_K_TEXTEND 0x10007 // keyboard input to move cursor to end of text
7935 #define STB_TEXTEDIT_K_DELETE 0x10008 // keyboard input to delete selection or character under cursor
7936 #define STB_TEXTEDIT_K_BACKSPACE 0x10009 // keyboard input to delete selection or character left of cursor
7937 #define STB_TEXTEDIT_K_UNDO 0x1000A // keyboard input to perform undo
7938 #define STB_TEXTEDIT_K_REDO 0x1000B // keyboard input to perform redo
7939 #define STB_TEXTEDIT_K_WORDLEFT 0x1000C // keyboard input to move cursor left one word
7940 #define STB_TEXTEDIT_K_WORDRIGHT 0x1000D // keyboard input to move cursor right one word
7941 #define STB_TEXTEDIT_K_SHIFT 0x20000
7943 #define STB_TEXTEDIT_IMPLEMENTATION
7944 #include "stb_textedit.h"
7948 void ImGuiTextEditState::OnKeyPressed(int key)
7950 stb_textedit_key(this, &StbState, key);
7951 CursorFollow = true;
7955 // Public API to manipulate UTF-8 text
7956 // We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar)
7957 // FIXME: The existence of this rarely exercised code path is a bit of a nuisance.
7958 void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count)
7960 IM_ASSERT(pos + bytes_count <= BufTextLen);
7961 char* dst = Buf + pos;
7962 const char* src = Buf + pos + bytes_count;
7963 while (char c = *src++)
7967 if (CursorPos + bytes_count >= pos)
7968 CursorPos -= bytes_count;
7969 else if (CursorPos >= pos)
7971 SelectionStart = SelectionEnd = CursorPos;
7973 BufTextLen -= bytes_count;
7976 void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)
7978 const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
7979 if (new_text_len + BufTextLen + 1 >= BufSize)
7982 if (BufTextLen != pos)
7983 memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos));
7984 memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char));
7985 Buf[BufTextLen + new_text_len] = '\0';
7987 if (CursorPos >= pos)
7988 CursorPos += new_text_len;
7989 SelectionStart = SelectionEnd = CursorPos;
7991 BufTextLen += new_text_len;
7994 // Return false to discard a character.
7995 static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
7997 unsigned int c = *p_char;
7999 if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF)))
8002 pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline));
8003 pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput));
8008 if (c >= 0xE000 && c <= 0xF8FF) // Filter private Unicode range. I don't imagine anybody would want to input them. GLFW on OSX seems to send private characters for special keys like arrow keys.
8011 if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank))
8013 if (flags & ImGuiInputTextFlags_CharsDecimal)
8014 if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/'))
8017 if (flags & ImGuiInputTextFlags_CharsHexadecimal)
8018 if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F'))
8021 if (flags & ImGuiInputTextFlags_CharsUppercase)
8022 if (c >= 'a' && c <= 'z')
8023 *p_char = (c += (unsigned int)('A'-'a'));
8025 if (flags & ImGuiInputTextFlags_CharsNoBlank)
8026 if (ImCharIsSpace(c))
8030 if (flags & ImGuiInputTextFlags_CallbackCharFilter)
8032 ImGuiTextEditCallbackData callback_data;
8033 memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
8034 callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;
8035 callback_data.EventChar = (ImWchar)c;
8036 callback_data.Flags = flags;
8037 callback_data.UserData = user_data;
8038 if (callback(&callback_data) != 0)
8040 *p_char = callback_data.EventChar;
8041 if (!callback_data.EventChar)
8048 // Edit a string of text
8049 // NB: when active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while active has no effect.
8050 // FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188
8051 bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
8053 ImGuiWindow* window = GetCurrentWindow();
8054 if (window->SkipItems)
8057 IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys)
8058 IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)
8060 ImGuiContext& g = *GImGui;
8061 const ImGuiIO& io = g.IO;
8062 const ImGuiStyle& style = g.Style;
8064 const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;
8065 const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0;
8066 const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
8068 if (is_multiline) // Open group before calling GetID() because groups tracks id created during their spawn
8070 const ImGuiID id = window->GetID(label);
8071 const ImVec2 label_size = CalcTextSize(label, NULL, true);
8072 ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line
8073 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
8074 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f, 0.0f));
8076 ImGuiWindow* draw_window = window;
8079 if (!BeginChildFrame(id, frame_bb.GetSize()))
8085 draw_window = GetCurrentWindow();
8086 size.x -= draw_window->ScrollbarSizes.x;
8090 ItemSize(total_bb, style.FramePadding.y);
8091 if (!ItemAdd(total_bb, id))
8094 const bool hovered = ItemHoverable(frame_bb, id);
8096 g.MouseCursor = ImGuiMouseCursor_TextInput;
8098 // Password pushes a temporary font with only a fallback glyph
8101 const ImFontGlyph* glyph = g.Font->FindGlyph('*');
8102 ImFont* password_font = &g.InputTextPasswordFont;
8103 password_font->FontSize = g.Font->FontSize;
8104 password_font->Scale = g.Font->Scale;
8105 password_font->DisplayOffset = g.Font->DisplayOffset;
8106 password_font->Ascent = g.Font->Ascent;
8107 password_font->Descent = g.Font->Descent;
8108 password_font->ContainerAtlas = g.Font->ContainerAtlas;
8109 password_font->FallbackGlyph = glyph;
8110 password_font->FallbackAdvanceX = glyph->AdvanceX;
8111 IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty());
8112 PushFont(password_font);
8115 // NB: we are only allowed to access 'edit_state' if we are the active widget.
8116 ImGuiTextEditState& edit_state = g.InputTextState;
8118 const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing
8119 const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent);
8120 const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code;
8122 const bool user_clicked = hovered && io.MouseClicked[0];
8123 const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY");
8125 bool clear_active_id = false;
8127 bool select_all = (g.ActiveId != id) && (flags & ImGuiInputTextFlags_AutoSelectAll) != 0;
8128 if (focus_requested || user_clicked || user_scrolled)
8130 if (g.ActiveId != id)
8133 // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)
8134 // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode)
8135 const int prev_len_w = edit_state.CurLenW;
8136 edit_state.Text.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash.
8137 edit_state.InitialText.resize(buf_size+1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash.
8138 ImStrncpy(edit_state.InitialText.Data, buf, edit_state.InitialText.Size);
8139 const char* buf_end = NULL;
8140 edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
8141 edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImFormatString() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8.
8142 edit_state.CursorAnimReset();
8144 // Preserve cursor position and undo/redo stack if we come back to same widget
8145 // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar).
8146 const bool recycle_state = (edit_state.Id == id) && (prev_len_w == edit_state.CurLenW);
8149 // Recycle existing cursor/selection/undo stack but clamp position
8150 // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
8151 edit_state.CursorClamp();
8156 edit_state.ScrollX = 0.0f;
8157 stb_textedit_initialize_state(&edit_state.StbState, !is_multiline);
8158 if (!is_multiline && focus_requested_by_code)
8161 if (flags & ImGuiInputTextFlags_AlwaysInsertMode)
8162 edit_state.StbState.insert_mode = true;
8163 if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl)))
8166 SetActiveID(id, window);
8167 FocusWindow(window);
8169 else if (io.MouseClicked[0])
8171 // Release focus when we click outside
8172 clear_active_id = true;
8175 bool value_changed = false;
8176 bool enter_pressed = false;
8178 if (g.ActiveId == id)
8180 if (!is_editable && !g.ActiveIdIsJustActivated)
8182 // When read-only we always use the live data passed to the function
8183 edit_state.Text.resize(buf_size+1);
8184 const char* buf_end = NULL;
8185 edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
8186 edit_state.CurLenA = (int)(buf_end - buf);
8187 edit_state.CursorClamp();
8190 edit_state.BufSizeA = buf_size;
8192 // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
8193 // Down the line we should have a cleaner library-wide concept of Selected vs Active.
8194 g.ActiveIdAllowOverlap = !io.MouseDown[0];
8195 g.WantTextInputNextFrame = 1;
8198 const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX;
8199 const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f));
8201 const bool osx_double_click_selects_words = io.OptMacOSXBehaviors; // OS X style: Double click selects by word instead of selecting whole text
8202 if (select_all || (hovered && !osx_double_click_selects_words && io.MouseDoubleClicked[0]))
8204 edit_state.SelectAll();
8205 edit_state.SelectedAllMouseLock = true;
8207 else if (hovered && osx_double_click_selects_words && io.MouseDoubleClicked[0])
8209 // Select a word only, OS X style (by simulating keystrokes)
8210 edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT);
8211 edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);
8213 else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock)
8215 stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
8216 edit_state.CursorAnimReset();
8218 else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f))
8220 stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
8221 edit_state.CursorAnimReset();
8222 edit_state.CursorFollow = true;
8224 if (edit_state.SelectedAllMouseLock && !io.MouseDown[0])
8225 edit_state.SelectedAllMouseLock = false;
8227 if (io.InputCharacters[0])
8229 // Process text input (before we check for Return because using some IME will effectively send a Return?)
8230 // We ignore CTRL inputs, but need to allow CTRL+ALT as some keyboards (e.g. German) use AltGR - which is Alt+Ctrl - to input certain characters.
8231 if (!(io.KeyCtrl && !io.KeyAlt) && is_editable)
8233 for (int n = 0; n < IM_ARRAYSIZE(io.InputCharacters) && io.InputCharacters[n]; n++)
8234 if (unsigned int c = (unsigned int)io.InputCharacters[n])
8236 // Insert character if they pass filtering
8237 if (!InputTextFilterCharacter(&c, flags, callback, user_data))
8239 edit_state.OnKeyPressed((int)c);
8243 // Consume characters
8244 memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
8248 bool cancel_edit = false;
8249 if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id)
8251 // Handle key-presses
8252 const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
8253 const bool is_shortcut_key_only = (io.OptMacOSXBehaviors ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl
8254 const bool is_wordmove_key_down = io.OptMacOSXBehaviors ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl
8255 const bool is_startend_key_down = io.OptMacOSXBehaviors && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
8257 if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
8258 else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
8259 else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
8260 else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }
8261 else if (IsKeyPressedMap(ImGuiKey_Home)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
8262 else if (IsKeyPressedMap(ImGuiKey_End)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
8263 else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
8264 else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable)
8266 if (!edit_state.HasSelection())
8268 if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT);
8269 else if (io.OptMacOSXBehaviors && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT);
8271 edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);
8273 else if (IsKeyPressedMap(ImGuiKey_Enter))
8275 bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;
8276 if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl))
8278 enter_pressed = clear_active_id = true;
8280 else if (is_editable)
8282 unsigned int c = '\n'; // Insert new line
8283 if (InputTextFilterCharacter(&c, flags, callback, user_data))
8284 edit_state.OnKeyPressed((int)c);
8287 else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable)
8289 unsigned int c = '\t'; // Insert TAB
8290 if (InputTextFilterCharacter(&c, flags, callback, user_data))
8291 edit_state.OnKeyPressed((int)c);
8293 else if (IsKeyPressedMap(ImGuiKey_Escape)) { clear_active_id = cancel_edit = true; }
8294 else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); }
8295 else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); }
8296 else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_A)) { edit_state.SelectAll(); edit_state.CursorFollow = true; }
8297 else if (is_shortcut_key_only && !is_password && ((IsKeyPressedMap(ImGuiKey_X) && is_editable) || IsKeyPressedMap(ImGuiKey_C)) && (!is_multiline || edit_state.HasSelection()))
8300 const bool cut = IsKeyPressedMap(ImGuiKey_X);
8301 if (cut && !edit_state.HasSelection())
8302 edit_state.SelectAll();
8304 if (io.SetClipboardTextFn)
8306 const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0;
8307 const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW;
8308 edit_state.TempTextBuffer.resize((ie-ib) * 4 + 1);
8309 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data+ib, edit_state.Text.Data+ie);
8310 SetClipboardText(edit_state.TempTextBuffer.Data);
8315 edit_state.CursorFollow = true;
8316 stb_textedit_cut(&edit_state, &edit_state.StbState);
8319 else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_V) && is_editable)
8322 if (const char* clipboard = GetClipboardText())
8324 // Filter pasted buffer
8325 const int clipboard_len = (int)strlen(clipboard);
8326 ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar));
8327 int clipboard_filtered_len = 0;
8328 for (const char* s = clipboard; *s; )
8331 s += ImTextCharFromUtf8(&c, s, NULL);
8334 if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, user_data))
8336 clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;
8338 clipboard_filtered[clipboard_filtered_len] = 0;
8339 if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation
8341 stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len);
8342 edit_state.CursorFollow = true;
8344 ImGui::MemFree(clipboard_filtered);
8349 if (g.ActiveId == id)
8353 // Restore initial value
8356 ImStrncpy(buf, edit_state.InitialText.Data, buf_size);
8357 value_changed = true;
8361 // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer before clearing ActiveId, even though strictly speaking it wasn't modified on this frame.
8362 // If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail. Also this allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage.
8363 bool apply_edit_back_to_user_buffer = !cancel_edit || (enter_pressed && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0);
8364 if (apply_edit_back_to_user_buffer)
8366 // Apply new value immediately - copy modified buffer back
8367 // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer
8368 // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect.
8369 // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks.
8372 edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4);
8373 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL);
8377 if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0)
8379 IM_ASSERT(callback != NULL);
8381 // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
8382 ImGuiInputTextFlags event_flag = 0;
8383 ImGuiKey event_key = ImGuiKey_COUNT;
8384 if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab))
8386 event_flag = ImGuiInputTextFlags_CallbackCompletion;
8387 event_key = ImGuiKey_Tab;
8389 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow))
8391 event_flag = ImGuiInputTextFlags_CallbackHistory;
8392 event_key = ImGuiKey_UpArrow;
8394 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow))
8396 event_flag = ImGuiInputTextFlags_CallbackHistory;
8397 event_key = ImGuiKey_DownArrow;
8399 else if (flags & ImGuiInputTextFlags_CallbackAlways)
8400 event_flag = ImGuiInputTextFlags_CallbackAlways;
8404 ImGuiTextEditCallbackData callback_data;
8405 memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
8406 callback_data.EventFlag = event_flag;
8407 callback_data.Flags = flags;
8408 callback_data.UserData = user_data;
8409 callback_data.ReadOnly = !is_editable;
8411 callback_data.EventKey = event_key;
8412 callback_data.Buf = edit_state.TempTextBuffer.Data;
8413 callback_data.BufTextLen = edit_state.CurLenA;
8414 callback_data.BufSize = edit_state.BufSizeA;
8415 callback_data.BufDirty = false;
8417 // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188)
8418 ImWchar* text = edit_state.Text.Data;
8419 const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor);
8420 const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start);
8421 const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end);
8424 callback(&callback_data);
8426 // Read back what user may have modified
8427 IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data); // Invalid to modify those fields
8428 IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA);
8429 IM_ASSERT(callback_data.Flags == flags);
8430 if (callback_data.CursorPos != utf8_cursor_pos) edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos);
8431 if (callback_data.SelectionStart != utf8_selection_start) edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart);
8432 if (callback_data.SelectionEnd != utf8_selection_end) edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd);
8433 if (callback_data.BufDirty)
8435 IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
8436 edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, callback_data.Buf, NULL);
8437 edit_state.CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
8438 edit_state.CursorAnimReset();
8443 // Copy back to user buffer
8444 if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0)
8446 ImStrncpy(buf, edit_state.TempTextBuffer.Data, buf_size);
8447 value_changed = true;
8452 // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value)
8453 if (clear_active_id && g.ActiveId == id)
8457 // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on.
8458 const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf; buf = NULL;
8461 RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
8463 const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size
8464 ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;
8465 ImVec2 text_size(0.f, 0.f);
8466 const bool is_currently_scrolling = (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY"));
8467 if (g.ActiveId == id || is_currently_scrolling)
8469 edit_state.CursorAnim += io.DeltaTime;
8471 // This is going to be messy. We need to:
8472 // - Display the text (this alone can be more easily clipped)
8473 // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)
8474 // - Measure text height (for scrollbar)
8475 // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)
8476 // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
8477 const ImWchar* text_begin = edit_state.Text.Data;
8478 ImVec2 cursor_offset, select_start_offset;
8481 // Count lines + find lines numbers straddling 'cursor' and 'select_start' position.
8482 const ImWchar* searches_input_ptr[2];
8483 searches_input_ptr[0] = text_begin + edit_state.StbState.cursor;
8484 searches_input_ptr[1] = NULL;
8485 int searches_remaining = 1;
8486 int searches_result_line_number[2] = { -1, -999 };
8487 if (edit_state.StbState.select_start != edit_state.StbState.select_end)
8489 searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
8490 searches_result_line_number[1] = -1;
8491 searches_remaining++;
8494 // Iterate all lines to find our line numbers
8495 // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter.
8496 searches_remaining += is_multiline ? 1 : 0;
8498 for (const ImWchar* s = text_begin; *s != 0; s++)
8502 if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; }
8503 if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; }
8506 if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count;
8507 if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count;
8509 // Calculate 2d position by finding the beginning of the line and measuring distance
8510 cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;
8511 cursor_offset.y = searches_result_line_number[0] * g.FontSize;
8512 if (searches_result_line_number[1] >= 0)
8514 select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;
8515 select_start_offset.y = searches_result_line_number[1] * g.FontSize;
8518 // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224)
8520 text_size = ImVec2(size.x, line_count * g.FontSize);
8524 if (edit_state.CursorFollow)
8526 // Horizontal scroll in chunks of quarter width
8527 if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll))
8529 const float scroll_increment_x = size.x * 0.25f;
8530 if (cursor_offset.x < edit_state.ScrollX)
8531 edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x);
8532 else if (cursor_offset.x - size.x >= edit_state.ScrollX)
8533 edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x);
8537 edit_state.ScrollX = 0.0f;
8543 float scroll_y = draw_window->Scroll.y;
8544 if (cursor_offset.y - g.FontSize < scroll_y)
8545 scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
8546 else if (cursor_offset.y - size.y >= scroll_y)
8547 scroll_y = cursor_offset.y - size.y;
8548 draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y); // To avoid a frame of lag
8549 draw_window->Scroll.y = scroll_y;
8550 render_pos.y = draw_window->DC.CursorPos.y;
8553 edit_state.CursorFollow = false;
8554 const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f);
8557 if (edit_state.StbState.select_start != edit_state.StbState.select_end)
8559 const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
8560 const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end);
8562 float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection.
8563 float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
8564 ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg);
8565 ImVec2 rect_pos = render_pos + select_start_offset - render_scroll;
8566 for (const ImWchar* p = text_selected_begin; p < text_selected_end; )
8568 if (rect_pos.y > clip_rect.w + g.FontSize)
8570 if (rect_pos.y < clip_rect.y)
8572 while (p < text_selected_end)
8578 ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true);
8579 if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((unsigned short)' ') * 0.50f); // So we can see selected empty lines
8580 ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn));
8581 rect.ClipWith(clip_rect);
8582 if (rect.Overlaps(clip_rect))
8583 draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
8585 rect_pos.x = render_pos.x - render_scroll.x;
8586 rect_pos.y += g.FontSize;
8590 draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect);
8592 // Draw blinking cursor
8593 bool cursor_is_visible = (!g.IO.OptCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || fmodf(g.InputTextState.CursorAnim, 1.20f) <= 0.80f;
8594 ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll;
8595 ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y-g.FontSize+0.5f, cursor_screen_pos.x+1.0f, cursor_screen_pos.y-1.5f);
8596 if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
8597 draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
8599 // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
8601 g.OsImePosRequest = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize);
8606 const char* buf_end = NULL;
8608 text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width
8609 draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect);
8614 Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line
8623 if (g.LogEnabled && !is_password)
8624 LogRenderedText(&render_pos, buf_display, NULL);
8626 if (label_size.x > 0)
8627 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
8629 if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)
8630 return enter_pressed;
8632 return value_changed;
8635 bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
8637 IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline()
8638 return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data);
8641 bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
8643 return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);
8646 static inline float SmallSquareSize()
8648 ImGuiContext& g = *GImGui;
8649 return g.FontSize + g.Style.FramePadding.y * 2.0f;
8652 // NB: scalar_format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "display_format" argument)
8653 bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags)
8655 ImGuiWindow* window = GetCurrentWindow();
8656 if (window->SkipItems)
8659 ImGuiContext& g = *GImGui;
8660 const ImGuiStyle& style = g.Style;
8661 const ImVec2 label_size = CalcTextSize(label, NULL, true);
8665 const ImVec2 button_sz = ImVec2(SmallSquareSize(), SmallSquareSize());
8667 PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_sz.x + style.ItemInnerSpacing.x)*2));
8670 DataTypeFormatString(data_type, data_ptr, scalar_format, buf, IM_ARRAYSIZE(buf));
8672 bool value_changed = false;
8673 if (!(extra_flags & ImGuiInputTextFlags_CharsHexadecimal))
8674 extra_flags |= ImGuiInputTextFlags_CharsDecimal;
8675 extra_flags |= ImGuiInputTextFlags_AutoSelectAll;
8676 if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags)) // PushId(label) + "" gives us the expected ID from outside point of view
8677 value_changed = DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, scalar_format);
8683 SameLine(0, style.ItemInnerSpacing.x);
8684 if (ButtonEx("-", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
8686 DataTypeApplyOp(data_type, '-', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
8687 value_changed = true;
8689 SameLine(0, style.ItemInnerSpacing.x);
8690 if (ButtonEx("+", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
8692 DataTypeApplyOp(data_type, '+', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
8693 value_changed = true;
8698 if (label_size.x > 0)
8700 SameLine(0, style.ItemInnerSpacing.x);
8701 RenderText(ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + style.FramePadding.y), label);
8702 ItemSize(label_size, style.FramePadding.y);
8706 return value_changed;
8709 bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags)
8711 char display_format[16];
8712 if (decimal_precision < 0)
8713 strcpy(display_format, "%f"); // Ideally we'd have a minimum decimal precision of 1 to visually denote that this is a float, while hiding non-significant digits? %f doesn't have a minimum of 1
8715 ImFormatString(display_format, IM_ARRAYSIZE(display_format), "%%.%df", decimal_precision);
8716 return InputScalarEx(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), display_format, extra_flags);
8719 bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags)
8721 // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes.
8722 const char* scalar_format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d";
8723 return InputScalarEx(label, ImGuiDataType_Int, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), scalar_format, extra_flags);
8726 bool ImGui::InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags)
8728 ImGuiWindow* window = GetCurrentWindow();
8729 if (window->SkipItems)
8732 ImGuiContext& g = *GImGui;
8733 bool value_changed = false;
8736 PushMultiItemsWidths(components);
8737 for (int i = 0; i < components; i++)
8740 value_changed |= InputFloat("##v", &v[i], 0, 0, decimal_precision, extra_flags);
8741 SameLine(0, g.Style.ItemInnerSpacing.x);
8747 TextUnformatted(label, FindRenderedTextEnd(label));
8750 return value_changed;
8753 bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags)
8755 return InputFloatN(label, v, 2, decimal_precision, extra_flags);
8758 bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags)
8760 return InputFloatN(label, v, 3, decimal_precision, extra_flags);
8763 bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags)
8765 return InputFloatN(label, v, 4, decimal_precision, extra_flags);
8768 bool ImGui::InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags)
8770 ImGuiWindow* window = GetCurrentWindow();
8771 if (window->SkipItems)
8774 ImGuiContext& g = *GImGui;
8775 bool value_changed = false;
8778 PushMultiItemsWidths(components);
8779 for (int i = 0; i < components; i++)
8782 value_changed |= InputInt("##v", &v[i], 0, 0, extra_flags);
8783 SameLine(0, g.Style.ItemInnerSpacing.x);
8789 TextUnformatted(label, FindRenderedTextEnd(label));
8792 return value_changed;
8795 bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags)
8797 return InputIntN(label, v, 2, extra_flags);
8800 bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags)
8802 return InputIntN(label, v, 3, extra_flags);
8805 bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags)
8807 return InputIntN(label, v, 4, extra_flags);
8810 static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
8812 const char* const* items = (const char* const*)data;
8814 *out_text = items[idx];
8818 static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
8820 // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
8821 const char* items_separated_by_zeros = (const char*)data;
8822 int items_count = 0;
8823 const char* p = items_separated_by_zeros;
8826 if (idx == items_count)
8838 // Combo box helper allowing to pass an array of strings.
8839 bool ImGui::Combo(const char* label, int* current_item, const char* const* items, int items_count, int height_in_items)
8841 const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
8842 return value_changed;
8845 // Combo box helper allowing to pass all items in a single string.
8846 bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
8848 int items_count = 0;
8849 const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open
8855 bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
8856 return value_changed;
8859 bool ImGui::BeginCombo(const char* label, const char* preview_value, ImVec2 popup_size)
8861 ImGuiWindow* window = GetCurrentWindow();
8862 if (window->SkipItems)
8865 ImGuiContext& g = *GImGui;
8866 const ImGuiStyle& style = g.Style;
8867 const ImGuiID id = window->GetID(label);
8868 const float w = CalcItemWidth();
8870 const ImVec2 label_size = CalcTextSize(label, NULL, true);
8871 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
8872 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
8873 ItemSize(total_bb, style.FramePadding.y);
8874 if (!ItemAdd(total_bb, id))
8877 const float arrow_size = SmallSquareSize();
8880 bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
8882 bool popup_open = IsPopupOpen(id);
8884 const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
8885 RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
8886 RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(popup_open || hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding); // FIXME-ROUNDING
8887 RenderTriangle(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down);
8889 if (preview_value != NULL)
8890 RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, preview_value, NULL, NULL, ImVec2(0.0f,0.0f));
8892 if (label_size.x > 0)
8893 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
8895 if (pressed && !popup_open)
8897 OpenPopupEx(id, false);
8904 if (popup_size.x == 0.0f)
8907 float popup_y1 = frame_bb.Max.y;
8908 float popup_y2 = ImClamp(popup_y1 + popup_size.y, popup_y1, g.IO.DisplaySize.y - style.DisplaySafeAreaPadding.y);
8909 if ((popup_y2 - popup_y1) < ImMin(popup_size.y, frame_bb.Min.y - style.DisplaySafeAreaPadding.y))
8911 // Position our combo ABOVE because there's more space to fit! (FIXME: Handle in Begin() or use a shared helper. We have similar code in Begin() for popup placement)
8912 popup_y1 = ImClamp(frame_bb.Min.y - popup_size.y, style.DisplaySafeAreaPadding.y, frame_bb.Min.y);
8913 popup_y2 = frame_bb.Min.y;
8914 SetNextWindowPos(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FrameBorderSize), ImGuiCond_Always, ImVec2(0.0f, 1.0f));
8918 // Position our combo below
8919 SetNextWindowPos(ImVec2(frame_bb.Min.x, frame_bb.Max.y - style.FrameBorderSize), ImGuiCond_Always, ImVec2(0.0f, 0.0f));
8921 SetNextWindowSize(ImVec2(popup_size.x, popup_y2 - popup_y1), ImGuiCond_Appearing);
8922 PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
8924 if (!BeginPopupEx(id, ImGuiWindowFlags_ComboBox))
8926 IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above
8934 void ImGui::EndCombo()
8940 // Combo box function.
8941 bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
8943 ImGuiContext& g = *GImGui;
8944 const ImGuiStyle& style = g.Style;
8946 const char* preview_text = NULL;
8947 if (*current_item >= 0 && *current_item < items_count)
8948 items_getter(data, *current_item, &preview_text);
8950 // Size default to hold ~7 items
8951 if (height_in_items < 0)
8952 height_in_items = 7;
8953 float popup_height = (g.FontSize + style.ItemSpacing.y) * ImMin(items_count, height_in_items) + (style.FramePadding.y * 3);
8955 if (!BeginCombo(label, preview_text, ImVec2(0.0f, popup_height)))
8959 // FIXME-OPT: Use clipper
8960 bool value_changed = false;
8961 for (int i = 0; i < items_count; i++)
8963 PushID((void*)(intptr_t)i);
8964 const bool item_selected = (i == *current_item);
8965 const char* item_text;
8966 if (!items_getter(data, i, &item_text))
8967 item_text = "*Unknown item*";
8968 if (Selectable(item_text, item_selected))
8970 value_changed = true;
8973 if (item_selected && IsWindowAppearing())
8979 return value_changed;
8982 // Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image.
8983 // But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID.
8984 bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
8986 ImGuiWindow* window = GetCurrentWindow();
8987 if (window->SkipItems)
8990 ImGuiContext& g = *GImGui;
8991 const ImGuiStyle& style = g.Style;
8993 if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1) // FIXME-OPT: Avoid if vertically clipped.
8996 ImGuiID id = window->GetID(label);
8997 ImVec2 label_size = CalcTextSize(label, NULL, true);
8998 ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
8999 ImVec2 pos = window->DC.CursorPos;
9000 pos.y += window->DC.CurrentLineTextBaseOffset;
9001 ImRect bb(pos, pos + size);
9004 // Fill horizontal space.
9005 ImVec2 window_padding = window->WindowPadding;
9006 float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x;
9007 float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x);
9008 ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y);
9009 ImRect bb_with_spacing(pos, pos + size_draw);
9010 if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth))
9011 bb_with_spacing.Max.x += window_padding.x;
9013 // Selectables are tightly packed together, we extend the box to cover spacing between selectable.
9014 float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f);
9015 float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f);
9016 float spacing_R = style.ItemSpacing.x - spacing_L;
9017 float spacing_D = style.ItemSpacing.y - spacing_U;
9018 bb_with_spacing.Min.x -= spacing_L;
9019 bb_with_spacing.Min.y -= spacing_U;
9020 bb_with_spacing.Max.x += spacing_R;
9021 bb_with_spacing.Max.y += spacing_D;
9022 if (!ItemAdd(bb_with_spacing, id))
9024 if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
9025 PushColumnClipRect();
9029 ImGuiButtonFlags button_flags = 0;
9030 if (flags & ImGuiSelectableFlags_Menu) button_flags |= ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_NoHoldingActiveID;
9031 if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnRelease;
9032 if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled;
9033 if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
9035 bool pressed = ButtonBehavior(bb_with_spacing, id, &hovered, &held, button_flags);
9036 if (flags & ImGuiSelectableFlags_Disabled)
9040 if (hovered || selected)
9042 const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
9043 RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, 0.0f);
9046 if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
9048 PushColumnClipRect();
9049 bb_with_spacing.Max.x -= (GetContentRegionMax().x - max_x);
9052 if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
9053 RenderTextClipped(bb.Min, bb_with_spacing.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f));
9054 if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor();
9056 // Automatically close popups
9057 if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup))
9058 CloseCurrentPopup();
9062 bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
9064 if (Selectable(label, *p_selected, flags, size_arg))
9066 *p_selected = !*p_selected;
9072 // Helper to calculate the size of a listbox and display a label on the right.
9073 // Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty"
9074 bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
9076 ImGuiWindow* window = GetCurrentWindow();
9077 if (window->SkipItems)
9080 const ImGuiStyle& style = GetStyle();
9081 const ImGuiID id = GetID(label);
9082 const ImVec2 label_size = CalcTextSize(label, NULL, true);
9084 // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
9085 ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y);
9086 ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y));
9087 ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
9088 ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
9089 window->DC.LastItemRect = bb;
9092 if (label_size.x > 0)
9093 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
9095 BeginChildFrame(id, frame_bb.GetSize());
9099 bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items)
9101 // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
9102 // However we don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size.
9103 // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution.
9104 if (height_in_items < 0)
9105 height_in_items = ImMin(items_count, 7);
9106 float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f);
9108 // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild().
9111 size.y = GetTextLineHeightWithSpacing() * height_in_items_f + GetStyle().ItemSpacing.y;
9112 return ListBoxHeader(label, size);
9115 void ImGui::ListBoxFooter()
9117 ImGuiWindow* parent_window = GetParentWindow();
9118 const ImRect bb = parent_window->DC.LastItemRect;
9119 const ImGuiStyle& style = GetStyle();
9123 // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect)
9124 // We call SameLine() to restore DC.CurrentLine* data
9126 parent_window->DC.CursorPos = bb.Min;
9127 ItemSize(bb, style.FramePadding.y);
9131 bool ImGui::ListBox(const char* label, int* current_item, const char* const* items, int items_count, int height_items)
9133 const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items);
9134 return value_changed;
9137 bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
9139 if (!ListBoxHeader(label, items_count, height_in_items))
9142 // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper.
9143 bool value_changed = false;
9144 ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to.
9145 while (clipper.Step())
9146 for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
9148 const bool item_selected = (i == *current_item);
9149 const char* item_text;
9150 if (!items_getter(data, i, &item_text))
9151 item_text = "*Unknown item*";
9154 if (Selectable(item_text, item_selected))
9157 value_changed = true;
9162 return value_changed;
9165 bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)
9167 ImGuiWindow* window = GetCurrentWindow();
9168 if (window->SkipItems)
9171 ImGuiContext& g = *GImGui;
9172 ImGuiStyle& style = g.Style;
9173 ImVec2 pos = window->DC.CursorPos;
9174 ImVec2 label_size = CalcTextSize(label, NULL, true);
9176 ImGuiSelectableFlags flags = ImGuiSelectableFlags_MenuItem | (enabled ? 0 : ImGuiSelectableFlags_Disabled);
9178 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
9180 // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
9181 // Note that in this situation we render neither the shortcut neither the selected tick mark
9182 float w = label_size.x;
9183 window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
9184 PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
9185 pressed = Selectable(label, false, flags, ImVec2(w, 0.0f));
9187 window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
9191 ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f);
9192 float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame
9193 float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
9194 pressed = Selectable(label, false, flags | ImGuiSelectableFlags_DrawFillAvailWidth, ImVec2(w, 0.0f));
9195 if (shortcut_size.x > 0.0f)
9197 PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
9198 RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
9202 RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f);
9207 bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled)
9209 if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled))
9212 *p_selected = !*p_selected;
9218 bool ImGui::BeginMainMenuBar()
9220 ImGuiContext& g = *GImGui;
9221 SetNextWindowPos(ImVec2(0.0f, 0.0f));
9222 SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.FontBaseSize + g.Style.FramePadding.y * 2.0f));
9223 PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
9224 PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0));
9225 if (!Begin("##MainMenuBar", NULL, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_MenuBar)
9232 g.CurrentWindow->DC.MenuBarOffsetX += g.Style.DisplaySafeAreaPadding.x;
9236 void ImGui::EndMainMenuBar()
9243 bool ImGui::BeginMenuBar()
9245 ImGuiWindow* window = GetCurrentWindow();
9246 if (window->SkipItems)
9248 if (!(window->Flags & ImGuiWindowFlags_MenuBar))
9251 IM_ASSERT(!window->DC.MenuBarAppending);
9252 BeginGroup(); // Save position
9253 PushID("##menubar");
9255 // We don't clip with regular window clipping rectangle as it is already set to the area below. However we clip with window full rect.
9256 // We remove 1 worth of rounding to Max.x to that text in long menus don't tend to display over the lower-right rounded area, which looks particularly glitchy.
9257 ImRect bar_rect = window->MenuBarRect();
9258 ImRect clip_rect(ImFloor(bar_rect.Min.x + 0.5f), ImFloor(bar_rect.Min.y + window->WindowBorderSize + 0.5f), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - window->WindowRounding) + 0.5f), ImFloor(bar_rect.Max.y + 0.5f));
9259 clip_rect.ClipWith(window->Rect());
9260 PushClipRect(clip_rect.Min, clip_rect.Max, false);
9262 window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffsetX, bar_rect.Min.y);// + g.Style.FramePadding.y);
9263 window->DC.LayoutType = ImGuiLayoutType_Horizontal;
9264 window->DC.MenuBarAppending = true;
9265 AlignTextToFramePadding();
9269 void ImGui::EndMenuBar()
9271 ImGuiWindow* window = GetCurrentWindow();
9272 if (window->SkipItems)
9275 IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);
9276 IM_ASSERT(window->DC.MenuBarAppending);
9279 window->DC.MenuBarOffsetX = window->DC.CursorPos.x - window->MenuBarRect().Min.x;
9280 window->DC.GroupStack.back().AdvanceCursor = false;
9282 window->DC.LayoutType = ImGuiLayoutType_Vertical;
9283 window->DC.MenuBarAppending = false;
9286 bool ImGui::BeginMenu(const char* label, bool enabled)
9288 ImGuiWindow* window = GetCurrentWindow();
9289 if (window->SkipItems)
9292 ImGuiContext& g = *GImGui;
9293 const ImGuiStyle& style = g.Style;
9294 const ImGuiID id = window->GetID(label);
9296 ImVec2 label_size = CalcTextSize(label, NULL, true);
9299 bool menu_is_open = IsPopupOpen(id);
9300 bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentMenuSet == window->GetID("##menus"));
9301 ImGuiWindow* backed_nav_window = g.NavWindow;
9302 if (menuset_is_open)
9303 g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent)
9305 // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestPopupWindowPos).
9306 ImVec2 popup_pos, pos = window->DC.CursorPos;
9307 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
9309 // Menu inside an horizontal menu bar
9310 // Selectable extend their highlight by half ItemSpacing in each direction.
9311 // For ChildMenu, the popup position will be overwritten by the call to FindBestPopupWindowPos() in Begin()
9312 popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight());
9313 window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
9314 PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
9315 float w = label_size.x;
9316 pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
9318 window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
9322 // Menu inside a menu
9323 popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
9324 float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame
9325 float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
9326 pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
9327 if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
9328 RenderTriangle(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), ImGuiDir_Right);
9329 if (!enabled) PopStyleColor();
9332 const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id);
9333 if (menuset_is_open)
9334 g.NavWindow = backed_nav_window;
9336 bool want_open = false, want_close = false;
9337 if (window->DC.LayoutType != ImGuiLayoutType_Horizontal) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
9339 // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
9340 bool moving_within_opened_triangle = false;
9341 if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window)
9343 if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window)
9345 ImRect next_window_rect = next_window->Rect();
9346 ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
9347 ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
9348 ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
9349 float extra = ImClamp(fabsf(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
9350 ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues
9351 tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
9352 tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
9353 moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
9354 //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug
9358 want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle);
9359 want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed);
9364 if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it
9367 want_open = menu_is_open = false;
9369 else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others
9375 if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }'
9377 if (want_close && IsPopupOpen(id))
9378 ClosePopupToLevel(GImGui->CurrentPopupStack.Size);
9380 if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size)
9382 // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
9387 menu_is_open |= want_open;
9393 SetNextWindowPos(popup_pos, ImGuiCond_Always);
9394 ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu);
9395 menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
9398 return menu_is_open;
9401 void ImGui::EndMenu()
9406 // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
9407 void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags)
9409 ImGuiContext& g = *GImGui;
9411 int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]);
9412 BeginTooltipEx(0, true);
9414 const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text;
9415 if (text_end > text)
9417 TextUnformatted(text, text_end);
9421 ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2);
9422 ColorButton("##preview", ImVec4(col[0], col[1], col[2], col[3]), (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz);
9424 if (flags & ImGuiColorEditFlags_NoAlpha)
9425 Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]);
9427 Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]);
9431 static inline ImU32 ImAlphaBlendColor(ImU32 col_a, ImU32 col_b)
9433 float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
9434 int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
9435 int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
9436 int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
9437 return IM_COL32(r, g, b, 0xFF);
9440 // NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that.
9441 // I spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding alltogether.
9442 void ImGui::RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, int rounding_corners_flags)
9444 ImGuiWindow* window = GetCurrentWindow();
9445 if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF)
9447 ImU32 col_bg1 = GetColorU32(ImAlphaBlendColor(IM_COL32(204,204,204,255), col));
9448 ImU32 col_bg2 = GetColorU32(ImAlphaBlendColor(IM_COL32(128,128,128,255), col));
9449 window->DrawList->AddRectFilled(p_min, p_max, col_bg1, rounding, rounding_corners_flags);
9452 for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++)
9454 float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y);
9457 for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f)
9459 float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x);
9462 int rounding_corners_flags_cell = 0;
9463 if (y1 <= p_min.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopRight; }
9464 if (y2 >= p_max.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotRight; }
9465 rounding_corners_flags_cell &= rounding_corners_flags;
9466 window->DrawList->AddRectFilled(ImVec2(x1,y1), ImVec2(x2,y2), col_bg2, rounding_corners_flags_cell ? rounding : 0.0f, rounding_corners_flags_cell);
9472 window->DrawList->AddRectFilled(p_min, p_max, col, rounding, rounding_corners_flags);
9476 void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags)
9478 ImGuiContext& g = *GImGui;
9479 if ((flags & ImGuiColorEditFlags__InputsMask) == 0)
9480 flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputsMask;
9481 if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0)
9482 flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask;
9483 if ((flags & ImGuiColorEditFlags__PickerMask) == 0)
9484 flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask;
9485 IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__InputsMask))); // Check only 1 option is selected
9486 IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__DataTypeMask))); // Check only 1 option is selected
9487 IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check only 1 option is selected
9488 g.ColorEditOptions = flags;
9491 // A little colored square. Return true when clicked.
9492 // FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip.
9493 // 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip.
9494 bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size)
9496 ImGuiWindow* window = GetCurrentWindow();
9497 if (window->SkipItems)
9500 ImGuiContext& g = *GImGui;
9501 const ImGuiID id = window->GetID(desc_id);
9502 float default_size = SmallSquareSize();
9504 size.x = default_size;
9506 size.y = default_size;
9507 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
9508 ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f);
9509 if (!ItemAdd(bb, id))
9513 bool pressed = ButtonBehavior(bb, id, &hovered, &held);
9515 if (flags & ImGuiColorEditFlags_NoAlpha)
9516 flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf);
9518 ImVec4 col_without_alpha(col.x, col.y, col.z, 1.0f);
9519 float grid_step = ImMin(size.x, size.y) / 2.99f;
9520 float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f);
9521 ImRect bb_inner = bb;
9522 float off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts.
9523 bb_inner.Expand(off);
9524 if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col.w < 1.0f)
9526 float mid_x = (float)(int)((bb_inner.Min.x + bb_inner.Max.x) * 0.5f + 0.5f);
9527 RenderColorRectWithAlphaCheckerboard(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight| ImDrawCornerFlags_BotRight);
9528 window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_without_alpha), rounding, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotLeft);
9532 // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha
9533 ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col : col_without_alpha;
9534 if (col_source.w < 1.0f)
9535 RenderColorRectWithAlphaCheckerboard(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding);
9537 window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding, ImDrawCornerFlags_All);
9539 if (g.Style.FrameBorderSize > 0.0f)
9540 RenderFrameBorder(bb.Min, bb.Max, rounding);
9542 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border
9544 if (hovered && !(flags & ImGuiColorEditFlags_NoTooltip))
9545 ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf));
9550 bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags)
9552 return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha);
9555 void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
9557 bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__InputsMask);
9558 bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask);
9559 if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context"))
9561 ImGuiContext& g = *GImGui;
9562 ImGuiColorEditFlags opts = g.ColorEditOptions;
9563 if (allow_opt_inputs)
9565 if (RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB;
9566 if (RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV;
9567 if (RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX;
9569 if (allow_opt_datatype)
9571 if (allow_opt_inputs) Separator();
9572 if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8;
9573 if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float;
9576 if (allow_opt_inputs || allow_opt_datatype)
9578 if (Button("Copy as..", ImVec2(-1,0)))
9580 if (BeginPopup("Copy"))
9582 int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]);
9584 ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
9585 if (Selectable(buf))
9586 SetClipboardText(buf);
9587 ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca);
9588 if (Selectable(buf))
9589 SetClipboardText(buf);
9590 if (flags & ImGuiColorEditFlags_NoAlpha)
9591 ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X", cr, cg, cb);
9593 ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X%02X", cr, cg, cb, ca);
9594 if (Selectable(buf))
9595 SetClipboardText(buf);
9599 g.ColorEditOptions = opts;
9603 static void ColorPickerOptionsPopup(ImGuiColorEditFlags flags, float* ref_col)
9605 bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask);
9606 bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar);
9607 if ((!allow_opt_picker && !allow_opt_alpha_bar) || !ImGui::BeginPopup("context"))
9609 ImGuiContext& g = *GImGui;
9610 if (allow_opt_picker)
9612 ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (SmallSquareSize() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function
9613 ImGui::PushItemWidth(picker_size.x);
9614 for (int picker_type = 0; picker_type < 2; picker_type++)
9616 // Draw small/thumbnail version of each picker type (over an invisible button for selection)
9617 if (picker_type > 0) ImGui::Separator();
9618 ImGui::PushID(picker_type);
9619 ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs|ImGuiColorEditFlags_NoOptions|ImGuiColorEditFlags_NoLabel|ImGuiColorEditFlags_NoSidePreview|(flags & ImGuiColorEditFlags_NoAlpha);
9620 if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar;
9621 if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel;
9622 ImVec2 backup_pos = ImGui::GetCursorScreenPos();
9623 if (ImGui::Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup
9624 g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask);
9625 ImGui::SetCursorScreenPos(backup_pos);
9626 ImVec4 dummy_ref_col;
9627 memcpy(&dummy_ref_col.x, ref_col, sizeof(float) * (picker_flags & ImGuiColorEditFlags_NoAlpha ? 3 : 4));
9628 ImGui::ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags);
9631 ImGui::PopItemWidth();
9633 if (allow_opt_alpha_bar)
9635 if (allow_opt_picker) ImGui::Separator();
9636 ImGui::CheckboxFlags("Alpha Bar", (unsigned int*)&g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar);
9641 // Edit colors components (each component in 0.0f..1.0f range).
9642 // See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
9643 // With typical options: Left-click on colored square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item.
9644 bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags)
9646 ImGuiWindow* window = GetCurrentWindow();
9647 if (window->SkipItems)
9650 ImGuiContext& g = *GImGui;
9651 const ImGuiStyle& style = g.Style;
9652 const float square_sz = SmallSquareSize();
9653 const float w_extra = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x);
9654 const float w_items_all = CalcItemWidth() - w_extra;
9655 const char* label_display_end = FindRenderedTextEnd(label);
9657 const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0;
9658 const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0;
9659 const int components = alpha ? 4 : 3;
9660 const ImGuiColorEditFlags flags_untouched = flags;
9665 // If we're not showing any slider there's no point in doing any HSV conversions
9666 if (flags & ImGuiColorEditFlags_NoInputs)
9667 flags = (flags & (~ImGuiColorEditFlags__InputsMask)) | ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_NoOptions;
9669 // Context menu: display and modify options (before defaults are applied)
9670 if (!(flags & ImGuiColorEditFlags_NoOptions))
9671 ColorEditOptionsPopup(col, flags);
9673 // Read stored options
9674 if (!(flags & ImGuiColorEditFlags__InputsMask))
9675 flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputsMask);
9676 if (!(flags & ImGuiColorEditFlags__DataTypeMask))
9677 flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask);
9678 if (!(flags & ImGuiColorEditFlags__PickerMask))
9679 flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask);
9680 flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask));
9682 // Convert to the formats we need
9683 float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f };
9684 if (flags & ImGuiColorEditFlags_HSV)
9685 ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
9686 int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) };
9688 bool value_changed = false;
9689 bool value_changed_as_float = false;
9691 if ((flags & (ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_HSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
9693 // RGB/HSV 0..255 Sliders
9694 const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
9695 const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
9697 const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x);
9698 const char* ids[4] = { "##X", "##Y", "##Z", "##W" };
9699 const char* fmt_table_int[3][4] =
9701 { "%3.0f", "%3.0f", "%3.0f", "%3.0f" }, // Short display
9702 { "R:%3.0f", "G:%3.0f", "B:%3.0f", "A:%3.0f" }, // Long display for RGBA
9703 { "H:%3.0f", "S:%3.0f", "V:%3.0f", "A:%3.0f" } // Long display for HSVA
9705 const char* fmt_table_float[3][4] =
9707 { "%0.3f", "%0.3f", "%0.3f", "%0.3f" }, // Short display
9708 { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA
9709 { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA
9711 const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_HSV) ? 2 : 1;
9713 PushItemWidth(w_item_one);
9714 for (int n = 0; n < components; n++)
9717 SameLine(0, style.ItemInnerSpacing.x);
9718 if (n + 1 == components)
9719 PushItemWidth(w_item_last);
9720 if (flags & ImGuiColorEditFlags_Float)
9721 value_changed = value_changed_as_float = value_changed | DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]);
9723 value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]);
9724 if (!(flags & ImGuiColorEditFlags_NoOptions))
9725 OpenPopupOnItemClick("context");
9730 else if ((flags & ImGuiColorEditFlags_HEX) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
9732 // RGB Hexadecimal Input
9735 ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255), ImClamp(i[3],0,255));
9737 ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255));
9738 PushItemWidth(w_items_all);
9739 if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase))
9741 value_changed = true;
9743 while (*p == '#' || ImCharIsSpace(*p))
9745 i[0] = i[1] = i[2] = i[3] = 0;
9747 sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned)
9749 sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
9751 if (!(flags & ImGuiColorEditFlags_NoOptions))
9752 OpenPopupOnItemClick("context");
9756 bool picker_active = false;
9757 if (!(flags & ImGuiColorEditFlags_NoSmallPreview))
9759 if (!(flags & ImGuiColorEditFlags_NoInputs))
9760 SameLine(0, style.ItemInnerSpacing.x);
9762 const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f);
9763 if (ColorButton("##ColorButton", col_v4, flags))
9765 if (!(flags & ImGuiColorEditFlags_NoPicker))
9767 // Store current color and open a picker
9768 g.ColorPickerRef = col_v4;
9769 OpenPopup("picker");
9770 SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1,style.ItemSpacing.y));
9773 if (!(flags & ImGuiColorEditFlags_NoOptions))
9774 OpenPopupOnItemClick("context");
9776 if (BeginPopup("picker"))
9778 picker_active = true;
9779 if (label != label_display_end)
9781 TextUnformatted(label, label_display_end);
9784 ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar;
9785 ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf;
9786 PushItemWidth(square_sz * 12.0f); // Use 256 + bar sizes?
9787 value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x);
9793 if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel))
9795 SameLine(0, style.ItemInnerSpacing.x);
9796 TextUnformatted(label, label_display_end);
9802 if (!value_changed_as_float)
9803 for (int n = 0; n < 4; n++)
9804 f[n] = i[n] / 255.0f;
9805 if (flags & ImGuiColorEditFlags_HSV)
9806 ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
9820 return value_changed;
9823 bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags)
9825 float col4[4] = { col[0], col[1], col[2], 1.0f };
9826 if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha))
9828 col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2];
9832 // 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side.
9833 static void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col)
9837 case ImGuiDir_Left: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), pos, col); return;
9838 case ImGuiDir_Right: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), pos, col); return;
9839 case ImGuiDir_Up: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), pos, col); return;
9840 case ImGuiDir_Down: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), pos, col); return;
9841 default: return; // Fix warning for ImGuiDir_None
9845 static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w)
9847 RenderArrow(draw_list, ImVec2(pos.x + half_sz.x + 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32_BLACK);
9848 RenderArrow(draw_list, ImVec2(pos.x + half_sz.x, pos.y), half_sz, ImGuiDir_Right, IM_COL32_WHITE);
9849 RenderArrow(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left, IM_COL32_BLACK);
9850 RenderArrow(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, ImGuiDir_Left, IM_COL32_WHITE);
9854 // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
9855 // FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..)
9856 bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col)
9858 ImGuiContext& g = *GImGui;
9859 ImGuiWindow* window = GetCurrentWindow();
9860 ImDrawList* draw_list = window->DrawList;
9862 ImGuiStyle& style = g.Style;
9868 if (!(flags & ImGuiColorEditFlags_NoSidePreview))
9869 flags |= ImGuiColorEditFlags_NoSmallPreview;
9871 // Context menu: display and store options.
9872 if (!(flags & ImGuiColorEditFlags_NoOptions))
9873 ColorPickerOptionsPopup(flags, col);
9875 // Read stored options
9876 if (!(flags & ImGuiColorEditFlags__PickerMask))
9877 flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask;
9878 IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check that only 1 is selected
9879 if (!(flags & ImGuiColorEditFlags_NoOptions))
9880 flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar);
9883 bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha);
9884 ImVec2 picker_pos = window->DC.CursorPos;
9885 float square_sz = SmallSquareSize();
9886 float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars
9887 float sv_picker_size = ImMax(bars_width * 1, CalcItemWidth() - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box
9888 float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x;
9889 float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x;
9890 float bars_triangles_half_sz = (float)(int)(bars_width * 0.20f);
9892 float wheel_thickness = sv_picker_size * 0.08f;
9893 float wheel_r_outer = sv_picker_size * 0.50f;
9894 float wheel_r_inner = wheel_r_outer - wheel_thickness;
9895 ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size*0.5f);
9897 // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic.
9898 float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f);
9899 ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point.
9900 ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point.
9901 ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point.
9904 ColorConvertRGBtoHSV(col[0], col[1], col[2], H, S, V);
9906 bool value_changed = false, value_changed_h = false, value_changed_sv = false;
9908 if (flags & ImGuiColorEditFlags_PickerHueWheel)
9910 // Hue wheel + SV triangle logic
9911 InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size));
9914 ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center;
9915 ImVec2 current_off = g.IO.MousePos - wheel_center;
9916 float initial_dist2 = ImLengthSqr(initial_off);
9917 if (initial_dist2 >= (wheel_r_inner-1)*(wheel_r_inner-1) && initial_dist2 <= (wheel_r_outer+1)*(wheel_r_outer+1))
9919 // Interactive with Hue wheel
9920 H = atan2f(current_off.y, current_off.x) / IM_PI*0.5f;
9923 value_changed = value_changed_h = true;
9925 float cos_hue_angle = cosf(-H * 2.0f * IM_PI);
9926 float sin_hue_angle = sinf(-H * 2.0f * IM_PI);
9927 if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle)))
9929 // Interacting with SV triangle
9930 ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle);
9931 if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated))
9932 current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated);
9934 ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww);
9935 V = ImClamp(1.0f - vv, 0.0001f, 1.0f);
9936 S = ImClamp(uu / V, 0.0001f, 1.0f);
9937 value_changed = value_changed_sv = true;
9940 if (!(flags & ImGuiColorEditFlags_NoOptions))
9941 OpenPopupOnItemClick("context");
9943 else if (flags & ImGuiColorEditFlags_PickerHueBar)
9945 // SV rectangle logic
9946 InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size));
9949 S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size-1));
9950 V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
9951 value_changed = value_changed_sv = true;
9953 if (!(flags & ImGuiColorEditFlags_NoOptions))
9954 OpenPopupOnItemClick("context");
9957 SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y));
9958 InvisibleButton("hue", ImVec2(bars_width, sv_picker_size));
9961 H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
9962 value_changed = value_changed_h = true;
9969 SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y));
9970 InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size));
9973 col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
9974 value_changed = true;
9978 if (!(flags & ImGuiColorEditFlags_NoSidePreview))
9980 SameLine(0, style.ItemInnerSpacing.x);
9984 if (!(flags & ImGuiColorEditFlags_NoLabel))
9986 const char* label_display_end = FindRenderedTextEnd(label);
9987 if (label != label_display_end)
9989 if ((flags & ImGuiColorEditFlags_NoSidePreview))
9990 SameLine(0, style.ItemInnerSpacing.x);
9991 TextUnformatted(label, label_display_end);
9995 if (!(flags & ImGuiColorEditFlags_NoSidePreview))
9997 ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
9998 if ((flags & ImGuiColorEditFlags_NoLabel))
10000 ColorButton("##current", col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2));
10001 if (ref_col != NULL)
10004 ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]);
10005 if (ColorButton("##original", ref_col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2)))
10007 memcpy(col, ref_col, ((flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4) * sizeof(float));
10008 value_changed = true;
10014 // Convert back color to RGB
10015 if (value_changed_h || value_changed_sv)
10016 ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10*1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]);
10018 // R,G,B and H,S,V slider color editor
10019 if ((flags & ImGuiColorEditFlags_NoInputs) == 0)
10021 PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x);
10022 ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf;
10023 ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker;
10024 if (flags & ImGuiColorEditFlags_RGB || (flags & ImGuiColorEditFlags__InputsMask) == 0)
10025 value_changed |= ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_RGB);
10026 if (flags & ImGuiColorEditFlags_HSV || (flags & ImGuiColorEditFlags__InputsMask) == 0)
10027 value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_HSV);
10028 if (flags & ImGuiColorEditFlags_HEX || (flags & ImGuiColorEditFlags__InputsMask) == 0)
10029 value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_HEX);
10033 // Try to cancel hue wrap (after ColorEdit), if any
10036 float new_H, new_S, new_V;
10037 ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V);
10038 if (new_H <= 0 && H > 0)
10040 if (new_V <= 0 && V != new_V)
10041 ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]);
10042 else if (new_S <= 0)
10043 ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]);
10047 ImVec4 hue_color_f(1, 1, 1, 1); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z);
10048 ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f);
10049 ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 1.0f));
10051 const ImU32 hue_colors[6+1] = { IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255), IM_COL32(0,255,255,255), IM_COL32(0,0,255,255), IM_COL32(255,0,255,255), IM_COL32(255,0,0,255) };
10052 ImVec2 sv_cursor_pos;
10054 if (flags & ImGuiColorEditFlags_PickerHueWheel)
10056 // Render Hue Wheel
10057 const float aeps = 1.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out).
10058 const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12);
10059 for (int n = 0; n < 6; n++)
10061 const float a0 = (n) /6.0f * 2.0f * IM_PI - aeps;
10062 const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps;
10063 const int vert_start_idx = draw_list->VtxBuffer.Size;
10064 draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc);
10065 draw_list->PathStroke(IM_COL32_WHITE, false, wheel_thickness);
10066 const int vert_end_idx = draw_list->VtxBuffer.Size;
10068 // Paint colors over existing vertices
10069 ImVec2 gradient_p0(wheel_center.x + cosf(a0) * wheel_r_inner, wheel_center.y + sinf(a0) * wheel_r_inner);
10070 ImVec2 gradient_p1(wheel_center.x + cosf(a1) * wheel_r_inner, wheel_center.y + sinf(a1) * wheel_r_inner);
10071 ShadeVertsLinearColorGradientKeepAlpha(draw_list->VtxBuffer.Data + vert_start_idx, draw_list->VtxBuffer.Data + vert_end_idx, gradient_p0, gradient_p1, hue_colors[n], hue_colors[n+1]);
10074 // Render Cursor + preview on Hue Wheel
10075 float cos_hue_angle = cosf(H * 2.0f * IM_PI);
10076 float sin_hue_angle = sinf(H * 2.0f * IM_PI);
10077 ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f);
10078 float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f;
10079 int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32);
10080 draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments);
10081 draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad+1, IM_COL32(128,128,128,255), hue_cursor_segments);
10082 draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, IM_COL32_WHITE, hue_cursor_segments);
10084 // Render SV triangle (rotated according to hue)
10085 ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle);
10086 ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle);
10087 ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle);
10088 ImVec2 uv_white = g.FontTexUvWhitePixel;
10089 draw_list->PrimReserve(6, 6);
10090 draw_list->PrimVtx(tra, uv_white, hue_color32);
10091 draw_list->PrimVtx(trb, uv_white, hue_color32);
10092 draw_list->PrimVtx(trc, uv_white, IM_COL32_WHITE);
10093 draw_list->PrimVtx(tra, uv_white, IM_COL32_BLACK_TRANS);
10094 draw_list->PrimVtx(trb, uv_white, IM_COL32_BLACK);
10095 draw_list->PrimVtx(trc, uv_white, IM_COL32_BLACK_TRANS);
10096 draw_list->AddTriangle(tra, trb, trc, IM_COL32(128,128,128,255), 1.5f);
10097 sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V));
10099 else if (flags & ImGuiColorEditFlags_PickerHueBar)
10101 // Render SV Square
10102 draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_WHITE, hue_color32, hue_color32, IM_COL32_WHITE);
10103 draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_BLACK_TRANS, IM_COL32_BLACK_TRANS, IM_COL32_BLACK, IM_COL32_BLACK);
10104 RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), 0.0f);
10105 sv_cursor_pos.x = ImClamp((float)(int)(picker_pos.x + ImSaturate(S) * sv_picker_size + 0.5f), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much
10106 sv_cursor_pos.y = ImClamp((float)(int)(picker_pos.y + ImSaturate(1 - V) * sv_picker_size + 0.5f), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2);
10109 for (int i = 0; i < 6; ++i)
10110 draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), hue_colors[i], hue_colors[i], hue_colors[i + 1], hue_colors[i + 1]);
10111 float bar0_line_y = (float)(int)(picker_pos.y + H * sv_picker_size + 0.5f);
10112 RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f);
10113 RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f);
10116 // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range)
10117 float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f;
10118 draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, col32_no_alpha, 12);
10119 draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad+1, IM_COL32(128,128,128,255), 12);
10120 draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, IM_COL32_WHITE, 12);
10122 // Render alpha bar
10125 float alpha = ImSaturate(col[3]);
10126 ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size);
10127 RenderColorRectWithAlphaCheckerboard(bar1_bb.Min, bar1_bb.Max, IM_COL32(0,0,0,0), bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f));
10128 draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, col32_no_alpha, col32_no_alpha, col32_no_alpha & ~IM_COL32_A_MASK, col32_no_alpha & ~IM_COL32_A_MASK);
10129 float bar1_line_y = (float)(int)(picker_pos.y + (1.0f - alpha) * sv_picker_size + 0.5f);
10130 RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f);
10131 RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f);
10137 return value_changed;
10140 // Horizontal separating line.
10141 void ImGui::Separator()
10143 ImGuiWindow* window = GetCurrentWindow();
10144 if (window->SkipItems)
10146 ImGuiContext& g = *GImGui;
10148 ImGuiWindowFlags flags = 0;
10149 if ((flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)) == 0)
10150 flags |= (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal;
10151 IM_ASSERT(ImIsPowerOfTwo((int)(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)))); // Check that only 1 option is selected
10152 if (flags & ImGuiSeparatorFlags_Vertical)
10154 VerticalSeparator();
10158 // Horizontal Separator
10159 if (window->DC.ColumnsCount > 1)
10162 float x1 = window->Pos.x;
10163 float x2 = window->Pos.x + window->Size.x;
10164 if (!window->DC.GroupStack.empty())
10165 x1 += window->DC.IndentX;
10167 const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f));
10168 ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit, we don't provide height to not alter layout.
10169 if (!ItemAdd(bb, 0))
10171 if (window->DC.ColumnsCount > 1)
10172 PushColumnClipRect();
10176 window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Separator));
10179 LogRenderedText(NULL, IM_NEWLINE "--------------------------------");
10181 if (window->DC.ColumnsCount > 1)
10183 PushColumnClipRect();
10184 window->DC.ColumnsCellMinY = window->DC.CursorPos.y;
10188 void ImGui::VerticalSeparator()
10190 ImGuiWindow* window = GetCurrentWindow();
10191 if (window->SkipItems)
10193 ImGuiContext& g = *GImGui;
10195 float y1 = window->DC.CursorPos.y;
10196 float y2 = window->DC.CursorPos.y + window->DC.CurrentLineHeight;
10197 const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + 1.0f, y2));
10198 ItemSize(ImVec2(bb.GetWidth(), 0.0f));
10199 if (!ItemAdd(bb, 0))
10202 window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x, bb.Max.y), GetColorU32(ImGuiCol_Separator));
10207 bool ImGui::SplitterBehavior(ImGuiID id, const ImRect& bb, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend)
10209 ImGuiContext& g = *GImGui;
10210 ImGuiWindow* window = g.CurrentWindow;
10212 const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
10213 #ifdef IMGUI_HAS_NAV
10214 window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus;
10216 bool add = ItemAdd(bb, id);
10217 window->DC.ItemFlags = item_flags_backup;
10221 bool hovered, held;
10222 ImRect bb_interact = bb;
10223 bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f));
10224 ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChilds | ImGuiButtonFlags_AllowOverlapMode);
10225 if (g.ActiveId != id)
10226 SetItemAllowOverlap();
10228 if (held || (g.HoveredId == id && g.HoveredIdPreviousFrame == id))
10229 SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW);
10231 ImRect bb_render = bb;
10234 ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min;
10235 float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x;
10237 // Minimum pane size
10238 if (mouse_delta < min_size1 - *size1)
10239 mouse_delta = min_size1 - *size1;
10240 if (mouse_delta > *size2 - min_size2)
10241 mouse_delta = *size2 - min_size2;
10244 *size1 += mouse_delta;
10245 *size2 -= mouse_delta;
10246 bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta));
10250 const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
10251 RenderFrame(bb_render.Min, bb_render.Max, col, true, g.Style.FrameRounding);
10256 void ImGui::Spacing()
10258 ImGuiWindow* window = GetCurrentWindow();
10259 if (window->SkipItems)
10261 ItemSize(ImVec2(0,0));
10264 void ImGui::Dummy(const ImVec2& size)
10266 ImGuiWindow* window = GetCurrentWindow();
10267 if (window->SkipItems)
10270 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
10275 bool ImGui::IsRectVisible(const ImVec2& size)
10277 ImGuiWindow* window = GetCurrentWindowRead();
10278 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
10281 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
10283 ImGuiWindow* window = GetCurrentWindowRead();
10284 return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
10287 // Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
10288 void ImGui::BeginGroup()
10290 ImGuiWindow* window = GetCurrentWindow();
10292 window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
10293 ImGuiGroupData& group_data = window->DC.GroupStack.back();
10294 group_data.BackupCursorPos = window->DC.CursorPos;
10295 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
10296 group_data.BackupIndentX = window->DC.IndentX;
10297 group_data.BackupGroupOffsetX = window->DC.GroupOffsetX;
10298 group_data.BackupCurrentLineHeight = window->DC.CurrentLineHeight;
10299 group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
10300 group_data.BackupLogLinePosY = window->DC.LogLinePosY;
10301 group_data.BackupActiveIdIsAlive = GImGui->ActiveIdIsAlive;
10302 group_data.AdvanceCursor = true;
10304 window->DC.GroupOffsetX = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffsetX;
10305 window->DC.IndentX = window->DC.GroupOffsetX;
10306 window->DC.CursorMaxPos = window->DC.CursorPos;
10307 window->DC.CurrentLineHeight = 0.0f;
10308 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
10311 void ImGui::EndGroup()
10313 ImGuiContext& g = *GImGui;
10314 ImGuiWindow* window = GetCurrentWindow();
10316 IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls
10318 ImGuiGroupData& group_data = window->DC.GroupStack.back();
10320 ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
10321 group_bb.Max.y -= g.Style.ItemSpacing.y; // Cancel out last vertical spacing because we are adding one ourselves.
10322 group_bb.Max = ImMax(group_bb.Min, group_bb.Max);
10324 window->DC.CursorPos = group_data.BackupCursorPos;
10325 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
10326 window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight;
10327 window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
10328 window->DC.IndentX = group_data.BackupIndentX;
10329 window->DC.GroupOffsetX = group_data.BackupGroupOffsetX;
10330 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
10332 if (group_data.AdvanceCursor)
10334 window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrentLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.
10335 ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset);
10336 ItemAdd(group_bb, 0);
10339 // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive() will be functional on the entire group.
10340 // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but if you search for LastItemId you'll notice it is only used in that context.
10341 const bool active_id_within_group = (!group_data.BackupActiveIdIsAlive && g.ActiveIdIsAlive && g.ActiveId && g.ActiveIdWindow->RootWindow == window->RootWindow);
10342 if (active_id_within_group)
10343 window->DC.LastItemId = g.ActiveId;
10344 window->DC.LastItemRect = group_bb;
10346 window->DC.GroupStack.pop_back();
10348 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
10351 // Gets back to previous line and continue with horizontal layout
10352 // pos_x == 0 : follow right after previous item
10353 // pos_x != 0 : align to specified x position (relative to window/group left)
10354 // spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0
10355 // spacing_w >= 0 : enforce spacing amount
10356 void ImGui::SameLine(float pos_x, float spacing_w)
10358 ImGuiWindow* window = GetCurrentWindow();
10359 if (window->SkipItems)
10362 ImGuiContext& g = *GImGui;
10365 if (spacing_w < 0.0f) spacing_w = 0.0f;
10366 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffsetX + window->DC.ColumnsOffsetX;
10367 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
10371 if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
10372 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
10373 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
10375 window->DC.CurrentLineHeight = window->DC.PrevLineHeight;
10376 window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
10379 void ImGui::NewLine()
10381 ImGuiWindow* window = GetCurrentWindow();
10382 if (window->SkipItems)
10385 ImGuiContext& g = *GImGui;
10386 const ImGuiLayoutType backup_layout_type = window->DC.LayoutType;
10387 window->DC.LayoutType = ImGuiLayoutType_Vertical;
10388 if (window->DC.CurrentLineHeight > 0.0f) // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height.
10389 ItemSize(ImVec2(0,0));
10391 ItemSize(ImVec2(0.0f, g.FontSize));
10392 window->DC.LayoutType = backup_layout_type;
10395 void ImGui::NextColumn()
10397 ImGuiWindow* window = GetCurrentWindow();
10398 if (window->SkipItems || window->DC.ColumnsCount <= 1)
10401 ImGuiContext& g = *GImGui;
10405 window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
10406 if (++window->DC.ColumnsCurrent < window->DC.ColumnsCount)
10408 // Columns 1+ cancel out IndentX
10409 window->DC.ColumnsOffsetX = GetColumnOffset(window->DC.ColumnsCurrent) - window->DC.IndentX + g.Style.ItemSpacing.x;
10410 window->DrawList->ChannelsSetCurrent(window->DC.ColumnsCurrent);
10414 window->DC.ColumnsCurrent = 0;
10415 window->DC.ColumnsOffsetX = 0.0f;
10416 window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY;
10417 window->DrawList->ChannelsSetCurrent(0);
10419 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
10420 window->DC.CursorPos.y = window->DC.ColumnsCellMinY;
10421 window->DC.CurrentLineHeight = 0.0f;
10422 window->DC.CurrentLineTextBaseOffset = 0.0f;
10424 PushColumnClipRect();
10425 PushItemWidth(GetColumnWidth() * 0.65f); // FIXME: Move on columns setup
10428 int ImGui::GetColumnIndex()
10430 ImGuiWindow* window = GetCurrentWindowRead();
10431 return window->DC.ColumnsCurrent;
10434 int ImGui::GetColumnsCount()
10436 ImGuiWindow* window = GetCurrentWindowRead();
10437 return window->DC.ColumnsCount;
10440 static float OffsetNormToPixels(ImGuiWindow* window, float offset_norm)
10442 return offset_norm * (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
10445 static float PixelsToOffsetNorm(ImGuiWindow* window, float offset)
10447 return (offset - window->DC.ColumnsMinX) / (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
10450 static float GetDraggedColumnOffset(int column_index)
10452 // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
10453 // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
10454 ImGuiContext& g = *GImGui;
10455 ImGuiWindow* window = g.CurrentWindow;
10456 IM_ASSERT(column_index > 0); // We cannot drag column 0. If you get this assert you may have a conflict between the ID of your columns and another widgets.
10457 IM_ASSERT(g.ActiveId == window->DC.ColumnsSetId + ImGuiID(column_index));
10459 float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x - window->Pos.x;
10460 x = ImMax(x, ImGui::GetColumnOffset(column_index-1) + g.Style.ColumnsMinSpacing);
10461 if ((window->DC.ColumnsFlags & ImGuiColumnsFlags_NoPreserveWidths))
10462 x = ImMin(x, ImGui::GetColumnOffset(column_index+1) - g.Style.ColumnsMinSpacing);
10467 float ImGui::GetColumnOffset(int column_index)
10469 ImGuiWindow* window = GetCurrentWindowRead();
10470 if (column_index < 0)
10471 column_index = window->DC.ColumnsCurrent;
10476 ImGuiContext& g = *GImGui;
10477 const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
10478 if (g.ActiveId == column_id)
10479 return GetDraggedColumnOffset(column_index);
10483 IM_ASSERT(column_index < window->DC.ColumnsData.Size);
10484 const float t = window->DC.ColumnsData[column_index].OffsetNorm;
10485 const float x_offset = ImLerp(window->DC.ColumnsMinX, window->DC.ColumnsMaxX, t);
10489 void ImGui::SetColumnOffset(int column_index, float offset)
10491 ImGuiContext& g = *GImGui;
10492 ImGuiWindow* window = GetCurrentWindow();
10493 if (column_index < 0)
10494 column_index = window->DC.ColumnsCurrent;
10496 IM_ASSERT(column_index < window->DC.ColumnsData.Size);
10498 const bool preserve_width = !(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < window->DC.ColumnsCount-1);
10499 const float width = preserve_width ? GetColumnWidth(column_index) : 0.0f;
10501 if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoForceWithinWindow))
10502 offset = ImMin(offset, window->DC.ColumnsMaxX - g.Style.ColumnsMinSpacing * (window->DC.ColumnsCount - column_index));
10503 const float offset_norm = PixelsToOffsetNorm(window, offset);
10505 const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
10506 window->DC.StateStorage->SetFloat(column_id, offset_norm);
10507 window->DC.ColumnsData[column_index].OffsetNorm = offset_norm;
10509 if (preserve_width)
10510 SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width));
10513 float ImGui::GetColumnWidth(int column_index)
10515 ImGuiWindow* window = GetCurrentWindowRead();
10516 if (column_index < 0)
10517 column_index = window->DC.ColumnsCurrent;
10519 return OffsetNormToPixels(window, window->DC.ColumnsData[column_index+1].OffsetNorm - window->DC.ColumnsData[column_index].OffsetNorm);
10522 void ImGui::SetColumnWidth(int column_index, float width)
10524 ImGuiWindow* window = GetCurrentWindowRead();
10525 if (column_index < 0)
10526 column_index = window->DC.ColumnsCurrent;
10528 SetColumnOffset(column_index+1, GetColumnOffset(column_index) + width);
10531 void ImGui::PushColumnClipRect(int column_index)
10533 ImGuiWindow* window = GetCurrentWindowRead();
10534 if (column_index < 0)
10535 column_index = window->DC.ColumnsCurrent;
10537 PushClipRect(window->DC.ColumnsData[column_index].ClipRect.Min, window->DC.ColumnsData[column_index].ClipRect.Max, false);
10540 void ImGui::BeginColumns(const char* id, int columns_count, ImGuiColumnsFlags flags)
10542 ImGuiContext& g = *GImGui;
10543 ImGuiWindow* window = GetCurrentWindow();
10545 IM_ASSERT(columns_count > 1);
10546 IM_ASSERT(window->DC.ColumnsCount == 1); // Nested columns are currently not supported
10548 // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
10549 // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
10550 PushID(0x11223347 + (id ? 0 : columns_count));
10551 window->DC.ColumnsSetId = window->GetID(id ? id : "columns");
10554 // Set state for first column
10555 window->DC.ColumnsCurrent = 0;
10556 window->DC.ColumnsCount = columns_count;
10557 window->DC.ColumnsFlags = flags;
10559 const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? (window->SizeContentsExplicit.x) : (window->Size.x -window->ScrollbarSizes.x);
10560 window->DC.ColumnsMinX = window->DC.IndentX - g.Style.ItemSpacing.x; // Lock our horizontal range
10561 //window->DC.ColumnsMaxX = content_region_width - window->Scroll.x -((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x;
10562 window->DC.ColumnsMaxX = content_region_width - window->Scroll.x;
10563 window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
10564 window->DC.ColumnsStartMaxPosX = window->DC.CursorMaxPos.x;
10565 window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.CursorPos.y;
10566 window->DC.ColumnsOffsetX = 0.0f;
10567 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
10569 // Cache column offsets
10570 window->DC.ColumnsData.resize(columns_count + 1);
10571 for (int column_index = 0; column_index < columns_count + 1; column_index++)
10573 const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
10574 KeepAliveID(column_id);
10575 const float default_t = column_index / (float)window->DC.ColumnsCount;
10576 float t = window->DC.StateStorage->GetFloat(column_id, default_t);
10577 if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoForceWithinWindow))
10578 t = ImMin(t, PixelsToOffsetNorm(window, window->DC.ColumnsMaxX - g.Style.ColumnsMinSpacing * (window->DC.ColumnsCount - column_index)));
10579 window->DC.ColumnsData[column_index].OffsetNorm = t;
10582 // Cache clipping rectangles
10583 for (int column_index = 0; column_index < columns_count; column_index++)
10585 float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(column_index) - 1.0f);
10586 float clip_x2 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(column_index + 1) - 1.0f);
10587 window->DC.ColumnsData[column_index].ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);
10588 window->DC.ColumnsData[column_index].ClipRect.ClipWith(window->ClipRect);
10591 window->DrawList->ChannelsSplit(window->DC.ColumnsCount);
10592 PushColumnClipRect();
10593 PushItemWidth(GetColumnWidth() * 0.65f);
10596 void ImGui::EndColumns()
10598 ImGuiContext& g = *GImGui;
10599 ImGuiWindow* window = GetCurrentWindow();
10600 IM_ASSERT(window->DC.ColumnsCount > 1);
10604 window->DrawList->ChannelsMerge();
10606 window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
10607 window->DC.CursorPos.y = window->DC.ColumnsCellMaxY;
10608 if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_GrowParentContentsSize))
10609 window->DC.CursorMaxPos.x = ImMax(window->DC.ColumnsStartMaxPosX, window->DC.ColumnsMaxX); // Restore cursor max pos, as columns don't grow parent
10611 // Draw columns borders and handle resize
10612 if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems)
10614 const float y1 = window->DC.ColumnsStartPosY;
10615 const float y2 = window->DC.CursorPos.y;
10616 int dragging_column = -1;
10617 for (int i = 1; i < window->DC.ColumnsCount; i++)
10619 float x = window->Pos.x + GetColumnOffset(i);
10620 const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(i);
10621 const float column_hw = 4.0f; // Half-width for interaction
10622 const ImRect column_rect(ImVec2(x - column_hw, y1), ImVec2(x + column_hw, y2));
10623 if (IsClippedEx(column_rect, column_id, false))
10626 bool hovered = false, held = false;
10627 if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoResize))
10629 ButtonBehavior(column_rect, column_id, &hovered, &held);
10630 if (hovered || held)
10631 g.MouseCursor = ImGuiMouseCursor_ResizeEW;
10632 if (held && g.ActiveIdIsJustActivated)
10633 g.ActiveIdClickOffset.x -= column_hw; // Store from center of column line (we used a 8 wide rect for columns clicking). This is used by GetDraggedColumnOffset().
10635 dragging_column = i;
10639 // We clip the Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.
10640 const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
10641 const float xi = (float)(int)x;
10642 window->DrawList->AddLine(ImVec2(xi, ImMax(y1 + 1.0f, window->ClipRect.Min.y)), ImVec2(xi, ImMin(y2, window->ClipRect.Max.y)), col);
10645 // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
10646 if (dragging_column != -1)
10648 float x = GetDraggedColumnOffset(dragging_column);
10649 SetColumnOffset(dragging_column, x);
10653 window->DC.ColumnsSetId = 0;
10654 window->DC.ColumnsCurrent = 0;
10655 window->DC.ColumnsCount = 1;
10656 window->DC.ColumnsFlags = 0;
10657 window->DC.ColumnsData.resize(0);
10658 window->DC.ColumnsOffsetX = 0.0f;
10659 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
10662 // [2017/08: This is currently the only public API, while we are working on making BeginColumns/EndColumns user-facing]
10663 void ImGui::Columns(int columns_count, const char* id, bool border)
10665 ImGuiWindow* window = GetCurrentWindow();
10666 IM_ASSERT(columns_count >= 1);
10668 if (window->DC.ColumnsCount != columns_count && window->DC.ColumnsCount != 1)
10671 ImGuiColumnsFlags flags = (border ? 0 : ImGuiColumnsFlags_NoBorder);
10672 //flags |= ImGuiColumnsFlags_NoPreserveWidths; // NB: Legacy behavior
10673 if (columns_count != 1)
10674 BeginColumns(id, columns_count, flags);
10677 void ImGui::Indent(float indent_w)
10679 ImGuiContext& g = *GImGui;
10680 ImGuiWindow* window = GetCurrentWindow();
10681 window->DC.IndentX += (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing;
10682 window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
10685 void ImGui::Unindent(float indent_w)
10687 ImGuiContext& g = *GImGui;
10688 ImGuiWindow* window = GetCurrentWindow();
10689 window->DC.IndentX -= (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing;
10690 window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
10693 void ImGui::TreePush(const char* str_id)
10695 ImGuiWindow* window = GetCurrentWindow();
10697 window->DC.TreeDepth++;
10698 PushID(str_id ? str_id : "#TreePush");
10701 void ImGui::TreePush(const void* ptr_id)
10703 ImGuiWindow* window = GetCurrentWindow();
10705 window->DC.TreeDepth++;
10706 PushID(ptr_id ? ptr_id : (const void*)"#TreePush");
10709 void ImGui::TreePushRawID(ImGuiID id)
10711 ImGuiWindow* window = GetCurrentWindow();
10713 window->DC.TreeDepth++;
10714 window->IDStack.push_back(id);
10717 void ImGui::TreePop()
10719 ImGuiWindow* window = GetCurrentWindow();
10721 window->DC.TreeDepth--;
10725 void ImGui::Value(const char* prefix, bool b)
10727 Text("%s: %s", prefix, (b ? "true" : "false"));
10730 void ImGui::Value(const char* prefix, int v)
10732 Text("%s: %d", prefix, v);
10735 void ImGui::Value(const char* prefix, unsigned int v)
10737 Text("%s: %d", prefix, v);
10740 void ImGui::Value(const char* prefix, float v, const char* float_format)
10745 ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format);
10746 Text(fmt, prefix, v);
10750 Text("%s: %.3f", prefix, v);
10754 //-----------------------------------------------------------------------------
10755 // PLATFORM DEPENDENT HELPERS
10756 //-----------------------------------------------------------------------------
10758 #if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
10759 #undef WIN32_LEAN_AND_MEAN
10760 #define WIN32_LEAN_AND_MEAN
10761 #include <windows.h>
10764 // Win32 API clipboard implementation
10765 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
10768 #pragma comment(lib, "user32")
10771 static const char* GetClipboardTextFn_DefaultImpl(void*)
10773 static ImVector<char> buf_local;
10775 if (!OpenClipboard(NULL))
10777 HANDLE wbuf_handle = GetClipboardData(CF_UNICODETEXT);
10778 if (wbuf_handle == NULL)
10783 if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle))
10785 int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
10786 buf_local.resize(buf_len);
10787 ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
10789 GlobalUnlock(wbuf_handle);
10791 return buf_local.Data;
10794 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10796 if (!OpenClipboard(NULL))
10798 const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
10799 HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
10800 if (wbuf_handle == NULL)
10805 ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle);
10806 ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
10807 GlobalUnlock(wbuf_handle);
10809 SetClipboardData(CF_UNICODETEXT, wbuf_handle);
10815 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
10816 static const char* GetClipboardTextFn_DefaultImpl(void*)
10818 ImGuiContext& g = *GImGui;
10819 return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
10822 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
10823 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10825 ImGuiContext& g = *GImGui;
10826 g.PrivateClipboard.clear();
10827 const char* text_end = text + strlen(text);
10828 g.PrivateClipboard.resize((int)(text_end - text) + 1);
10829 memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
10830 g.PrivateClipboard[(int)(text_end - text)] = 0;
10835 // Win32 API IME support (for Asian languages, etc.)
10836 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
10840 #pragma comment(lib, "imm32")
10843 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
10845 // Notify OS Input Method Editor of text input position
10846 if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle)
10847 if (HIMC himc = ImmGetContext(hwnd))
10849 COMPOSITIONFORM cf;
10850 cf.ptCurrentPos.x = x;
10851 cf.ptCurrentPos.y = y;
10852 cf.dwStyle = CFS_FORCE_POSITION;
10853 ImmSetCompositionWindow(himc, &cf);
10859 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
10863 //-----------------------------------------------------------------------------
10865 //-----------------------------------------------------------------------------
10867 void ImGui::ShowMetricsWindow(bool* p_open)
10869 if (ImGui::Begin("ImGui Metrics", p_open))
10871 ImGui::Text("ImGui %s", ImGui::GetVersion());
10872 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
10873 ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3);
10874 ImGui::Text("%d allocations", ImGui::GetIO().MetricsAllocs);
10875 static bool show_clip_rects = true;
10876 ImGui::Checkbox("Show clipping rectangles when hovering an ImDrawCmd", &show_clip_rects);
10877 ImGui::Separator();
10881 static void NodeDrawList(ImDrawList* draw_list, const char* label)
10883 bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size);
10884 if (draw_list == ImGui::GetWindowDrawList())
10887 ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
10888 if (node_open) ImGui::TreePop();
10894 ImDrawList* overlay_draw_list = &GImGui->OverlayDrawList; // Render additional visuals into the top-most draw list
10895 overlay_draw_list->PushClipRectFullScreen();
10896 int elem_offset = 0;
10897 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
10899 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
10901 if (pcmd->UserCallback)
10903 ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
10906 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
10907 bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %-4d %s vtx, tex = %p, clip_rect = (%.0f,%.0f)..(%.0f,%.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
10908 if (show_clip_rects && ImGui::IsItemHovered())
10910 ImRect clip_rect = pcmd->ClipRect;
10912 for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
10913 vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
10914 clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255));
10915 vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255));
10917 if (!pcmd_node_open)
10919 ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
10920 while (clipper.Step())
10921 for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
10924 char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
10925 ImVec2 triangles_pos[3];
10926 for (int n = 0; n < 3; n++, vtx_i++)
10928 ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i];
10929 triangles_pos[n] = v.pos;
10930 buf_p += ImFormatString(buf_p, (int)(buf_end - buf_p), "%s %04d { pos = (%8.2f,%8.2f), uv = (%.6f,%.6f), col = %08X }\n", (n == 0) ? "vtx" : " ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
10932 ImGui::Selectable(buf, false);
10933 if (ImGui::IsItemHovered())
10934 overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f, false); // Add triangle without AA, more readable for large-thin triangle
10938 overlay_draw_list->PopClipRect();
10942 static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
10944 if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
10946 for (int i = 0; i < windows.Size; i++)
10947 Funcs::NodeWindow(windows[i], "Window");
10951 static void NodeWindow(ImGuiWindow* window, const char* label)
10953 if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
10955 NodeDrawList(window->DrawList, "DrawList");
10956 ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y);
10957 if (ImGui::IsItemHovered())
10958 GImGui->OverlayDrawList.AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255,255,0,255));
10959 ImGui::BulletText("Scroll: (%.2f,%.2f)", window->Scroll.x, window->Scroll.y);
10960 ImGui::BulletText("Active: %d, WriteAccessed: %d", window->Active, window->WriteAccessed);
10961 if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
10962 if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
10963 ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
10968 ImGuiContext& g = *GImGui; // Access private state
10969 Funcs::NodeWindows(g.Windows, "Windows");
10970 if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.RenderDrawLists[0].Size))
10972 for (int layer = 0; layer < IM_ARRAYSIZE(g.RenderDrawLists); layer++)
10973 for (int i = 0; i < g.RenderDrawLists[layer].Size; i++)
10974 Funcs::NodeDrawList(g.RenderDrawLists[0][i], "DrawList");
10977 if (ImGui::TreeNode("Popups", "Open Popups Stack (%d)", g.OpenPopupStack.Size))
10979 for (int i = 0; i < g.OpenPopupStack.Size; i++)
10981 ImGuiWindow* window = g.OpenPopupStack[i].Window;
10982 ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : "");
10986 if (ImGui::TreeNode("Basic state"))
10988 ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
10989 ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
10990 ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec)", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
10991 ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec)", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer);
10992 ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
10993 ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
11000 //-----------------------------------------------------------------------------
11002 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
11003 // Prefer just including imgui_internal.h from your code rather than using this define. If a declaration is missing from imgui_internal.h add it or request it on the github.
11004 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
11005 #include "imgui_user.inl"
11008 //-----------------------------------------------------------------------------