1 // dear imgui, v1.50
2 // (main code and documentation)
3
4 // See ImGui::ShowTestWindow() in imgui_demo.cpp for demo code.
5 // Newcomers, read 'Programmer guide' below for notes on how to setup ImGui in your codebase.
6 // Get latest version at https://github.com/ocornut/imgui
7 // Releases change-log at https://github.com/ocornut/imgui/releases
8 // Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/772
9 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
10 // This library is free but I need your support to sustain development and maintenance.
11 // If you work for a company, please consider financial support, e.g: https://www.patreon.com/imgui
12
13 /*
14
15 Index
16 - MISSION STATEMENT
17 - END-USER GUIDE
18 - PROGRAMMER GUIDE (read me!)
19 - API BREAKING CHANGES (read me when you update!)
20 - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
21 - How can I help?
22 - How do I update to a newer version of ImGui?
23 - What is ImTextureID and how do I display an image?
24 - I integrated ImGui in my engine and the text or lines are blurry..
25 - I integrated ImGui in my engine and some elements are clipping or disappearing when I move windows around..
26 - How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on the purpose of labels/IDs.
27 - How can I tell when ImGui wants my mouse/keyboard inputs and when I can pass them to my application?
28 - How can I load a different font than the default?
29 - How can I easily use icons in my application?
30 - How can I load multiple fonts?
31 - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
32 - How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
33 - ISSUES & TODO-LIST
34 - CODE
35
36
37 MISSION STATEMENT
38 =================
39
40 - easy to use to create code-driven and data-driven tools
41 - easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools
42 - easy to hack and improve
43 - minimize screen real-estate usage
44 - minimize setup and maintenance
45 - minimize state storage on user side
46 - portable, minimize dependencies, run on target (consoles, phones, etc.)
47 - efficient runtime (NB- we do allocate when "growing" content - creating a window / opening a tree node for the first time, etc. - but a typical frame won't allocate anything)
48 - read about immediate-mode gui principles @ http://mollyrocket.com/861, http://mollyrocket.com/forums/index.html
49
50 Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
51 - doesn't look fancy, doesn't animate
52 - limited layout features, intricate layouts are typically crafted in code
53 - occasionally uses statically sized buffers for string manipulations - won't crash, but some very long pieces of text may be clipped. functions like ImGui::TextUnformatted() don't have such restriction.
54
55
56 END-USER GUIDE
57 ==============
58
59 - double-click title bar to collapse window
60 - click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin()
61 - click and drag on lower right corner to resize window
62 - click and drag on any empty space to move window
63 - double-click/double-tap on lower right corner grip to auto-fit to content
64 - TAB/SHIFT+TAB to cycle through keyboard editable fields
65 - use mouse wheel to scroll
66 - use CTRL+mouse wheel to zoom window contents (if IO.FontAllowScaling is true)
67 - CTRL+Click on a slider or drag box to input value as text
68 - text editor:
69 - Hold SHIFT or use mouse to select text.
70 - CTRL+Left/Right to word jump
71 - CTRL+Shift+Left/Right to select words
72 - CTRL+A our Double-Click to select all
73 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard
74 - CTRL+Z,CTRL+Y to undo/redo
75 - ESCAPE to revert text to its original value
76 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
77
78
79 PROGRAMMER GUIDE
80 ================
81
82 - read the FAQ below this section!
83 - your code creates the UI, if your code doesn't run the UI is gone! == very dynamic UI, no construction/destructions steps, less data retention on your side, no state duplication, less sync, less bugs.
84 - call and read ImGui::ShowTestWindow() for demo code demonstrating most features.
85 - see examples/ folder for standalone sample applications. Prefer reading examples/opengl2_example/ first as it is the simplest.
86 you may be able to grab and copy a ready made imgui_impl_*** file from the examples/.
87 - customization: PushStyleColor()/PushStyleVar() or the style editor to tweak the look of the interface (e.g. if you want a more compact UI or a different color scheme).
88
89 - getting started:
90 - init: call ImGui::GetIO() to retrieve the ImGuiIO structure and fill the fields marked 'Settings'.
91 - init: call io.Fonts->GetTexDataAsRGBA32(...) and load the font texture pixels into graphics memory.
92 - every frame:
93 1/ in your mainloop or right after you got your keyboard/mouse info, call ImGui::GetIO() and fill the fields marked 'Input'
94 2/ call ImGui::NewFrame() as early as you can!
95 3/ use any ImGui function you want between NewFrame() and Render()
96 4/ call ImGui::Render() as late as you can to end the frame and finalize render data. it will call your RenderDrawListFn handler that you set in the IO structure.
97 (if you don't need to render, you still need to call Render() and ignore the callback, or call EndFrame() instead. if you call neither some aspects of windows focusing/moving will appear broken.)
98 - all rendering information are stored into command-lists until ImGui::Render() is called.
99 - ImGui never touches or know about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide.
100 - effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases of your own application.
101 - refer to the examples applications in the examples/ folder for instruction on how to setup your code.
102 - a typical application skeleton may be:
103
104 // Application init
105 ImGuiIO& io = ImGui::GetIO();
106 io.DisplaySize.x = 1920.0f;
107 io.DisplaySize.y = 1280.0f;
108 io.IniFilename = "imgui.ini";
109 io.RenderDrawListsFn = my_render_function; // Setup a render function, or set to NULL and call GetDrawData() after Render() to access the render data.
110 // TODO: Fill others settings of the io structure
111
112 // Load texture atlas
113 // There is a default font so you don't need to care about choosing a font yet
114 unsigned char* pixels;
115 int width, height;
116 io.Fonts->GetTexDataAsRGBA32(pixels, &width, &height);
117 // TODO: At this points you've got a texture pointed to by 'pixels' and you need to upload that your your graphic system
118 // TODO: Store your texture pointer/identifier (whatever your engine uses) in 'io.Fonts->TexID'
119
120 // Application main loop
121 while (true)
122 {
123 // 1) get low-level inputs (e.g. on Win32, GetKeyboardState(), or poll your events, etc.)
124 // TODO: fill all fields of IO structure and call NewFrame
125 ImGuiIO& io = ImGui::GetIO();
126 io.DeltaTime = 1.0f/60.0f;
127 io.MousePos = mouse_pos;
128 io.MouseDown[0] = mouse_button_0;
129 io.MouseDown[1] = mouse_button_1;
130 io.KeysDown[i] = ...
131
132 // 2) call NewFrame(), after this point you can use ImGui::* functions anytime
133 ImGui::NewFrame();
134
135 // 3) most of your application code here
136 MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
137 MyGameRender(); // may use any ImGui functions
138
139 // 4) render & swap video buffers
140 ImGui::Render();
141 SwapBuffers();
142 }
143
144 - You can read back 'io.WantCaptureMouse', 'io.WantCaptureKeybord' etc. flags from the IO structure to tell how ImGui intends to use your
145 inputs and to know if you should share them or hide them from the rest of your application. Read the FAQ below for more information.
146
147
148 API BREAKING CHANGES
149 ====================
150
151 Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix.
152 Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code.
153 Also read releases logs https://github.com/ocornut/imgui/releases for more details.
154
155 - 2017/05/26 (1.50) - Removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
156 - 2017/05/01 (1.50) - Renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
157 - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetId() and use it instead of passing string to BeginChild().
158 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
159 - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc.
160 - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully breakage should be minimal.
161 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
162 If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you.
163 However if your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
164 This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color.
165 ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
166 {
167 float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
168 return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a);
169 }
170 If this is confusing, pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color.
171 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
172 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
173 - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen).
174 - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDraw::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer.
175 - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref github issue #337).
176 - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337)
177 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
178 - 2016/03/02 (1.48) - InputText() completion/history/always callbacks: if you modify the text buffer manually (without using DeleteChars()/InsertChars() helper) you need to maintain the BufTextLen field. added an assert.
179 - 2016/01/23 (1.48) - fixed not honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. if you had manual pixel-perfect alignment in place it might affect you.
180 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
181 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
182 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
183 GetCursorPos()/SetCursorPos() functions now include the scrolled amount. It shouldn't affect the majority of users, but take note that SetCursorPosX(100.0f) puts you at +100 from the starting x position which may include scrolling, not at +100 from the window left side.
184 GetContentRegionMax()/GetWindowContentRegionMin()/GetWindowContentRegionMax() functions allow include the scrolled amount. Typically those were used in cases where no scrolling would happen so it may not be a problem, but watch out!
185 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
186 - 2015/08/05 (1.44) - split imgui.cpp into extra files: imgui_demo.cpp imgui_draw.cpp imgui_internal.h that you need to add to your project.
187 - 2015/07/18 (1.44) - fixed angles in ImDrawList::PathArcTo(), PathArcToFast() (introduced in 1.43) being off by an extra PI for no justifiable reason
188 - 2015/07/14 (1.43) - add new ImFontAtlas::AddFont() API. For the old AddFont***, moved the 'font_no' parameter of ImFontAtlas::AddFont** functions to the ImFontConfig structure.
189 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
190 - 2015/07/08 (1.43) - switched rendering data to use indexed rendering. this is saving a fair amount of CPU/GPU and enables us to get anti-aliasing for a marginal cost.
191 this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
192 - if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
193 - the signature of the io.RenderDrawListsFn handler has changed!
194 ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
195 became:
196 ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
197 argument 'cmd_lists' -> 'draw_data->CmdLists'
198 argument 'cmd_lists_count' -> 'draw_data->CmdListsCount'
199 ImDrawList 'commands' -> 'CmdBuffer'
200 ImDrawList 'vtx_buffer' -> 'VtxBuffer'
201 ImDrawList n/a -> 'IdxBuffer' (new)
202 ImDrawCmd 'vtx_count' -> 'ElemCount'
203 ImDrawCmd 'clip_rect' -> 'ClipRect'
204 ImDrawCmd 'user_callback' -> 'UserCallback'
205 ImDrawCmd 'texture_id' -> 'TextureId'
206 - each ImDrawList now contains both a vertex buffer and an index buffer. For each command, render ElemCount/3 triangles using indices from the index buffer.
207 - if you REALLY cannot render indexed primitives, you can call the draw_data->DeIndexAllBuffers() method to de-index the buffers. This is slow and a waste of CPU/GPU. Prefer using indexed rendering!
208 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
209 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
210 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
211 - 2015/07/02 (1.42) - renamed GetScrollPosY() to GetScrollY(). Necessary to reduce confusion along with other scrolling functions, because positions (e.g. cursor position) are not equivalent to scrolling amount.
212 - 2015/06/14 (1.41) - changed ImageButton() default bg_col parameter from (0,0,0,1) (black) to (0,0,0,0) (transparent) - makes a difference when texture have transparence
213 - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry!
214 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
215 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
216 - 2015/05/27 (1.40) - removed the third 'repeat_if_held' parameter from Button() - sorry! it was rarely used and inconsistent. Use PushButtonRepeat(true) / PopButtonRepeat() to enable repeat on desired buttons.
217 - 2015/05/11 (1.40) - changed BeginPopup() API, takes a string identifier instead of a bool. ImGui needs to manage the open/closed state of popups. Call OpenPopup() to actually set the "open" state of a popup. BeginPopup() returns true if the popup is opened.
218 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
219 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
220 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
221 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
222 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
223 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
224 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
225 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
226 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
227 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
228 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
229 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
230 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
231 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
232 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
233 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
234 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
235 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
236 this sequence:
237 const void* png_data;
238 unsigned int png_size;
239 ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size);
240 // <Copy to GPU>
241 became:
242 unsigned char* pixels;
243 int width, height;
244 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
245 // <Copy to GPU>
246 io.Fonts->TexID = (your_texture_identifier);
247 you now have much more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
248 it is now recommended that you sample the font texture with bilinear interpolation.
249 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
250 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
251 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
252 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
253 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
254 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
255 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
256 - 2014/10/02 (1.14) - renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL and imgui_user.cpp to imgui_user.inl (more IDE friendly)
257 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
258 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
259 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
260 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
261 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
262 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
263
264
265 FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
266 ======================================
267
268 Q: How can I help?
269 A: - If you are experienced enough with ImGui and with C/C++, look at the todo list and see how you want/can help!
270 - Become a Patron/donate. Convince your company to become a Patron or provide serious funding for development time.
271
272 Q: How do I update to a newer version of ImGui?
273 A: Overwrite the following files:
274 imgui.cpp
275 imgui.h
276 imgui_demo.cpp
277 imgui_draw.cpp
278 imgui_internal.h
279 stb_rect_pack.h
280 stb_textedit.h
281 stb_truetype.h
282 Don't overwrite imconfig.h if you have made modification to your copy.
283 If you have a problem with a missing function/symbols, search for its name in the code, there will likely be a comment about it.
284 Check the "API BREAKING CHANGES" sections for a list of occasional API breaking changes.
285 Please report any issue to the GitHub page!
286
287 Q: What is ImTextureID and how do I display an image?
288 A: ImTextureID is a void* used to pass renderer-agnostic texture references around until it hits your render function.
289 ImGui knows nothing about what those bits represent, it just passes them around. It is up to you to decide what you want the void* to carry!
290 It could be an identifier to your OpenGL texture (cast GLuint to void*), a pointer to your custom engine material (cast MyMaterial* to void*), etc.
291 At the end of the chain, your renderer takes this void* to cast it back into whatever it needs to select a current texture to render.
292 Refer to examples applications, where each renderer (in a imgui_impl_xxxx.cpp file) is treating ImTextureID as a different thing.
293 (c++ tip: OpenGL uses integers to identify textures. You can safely store an integer into a void*, just cast it to void*, don't take it's address!)
294 To display a custom image/texture within an ImGui window, you may use ImGui::Image(), ImGui::ImageButton(), ImDrawList::AddImage() functions.
295 ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use.
296 It is your responsibility to get textures uploaded to your GPU.
297
298 Q: I integrated ImGui in my engine and the text or lines are blurry..
299 A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
300 Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
301
302 Q: I integrated ImGui in my engine and some elements are clipping or disappearing when I move windows around..
303 A: Most likely you are mishandling the clipping rectangles in your render function. Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
304
305 Q: Can I have multiple widgets with the same label? Can I have widget without a label? (Yes)
306 A: Yes. A primer on the use of labels/IDs in ImGui..
307
308 - Elements that are not clickable, such as Text() items don't need an ID.
309
310 - Interactive widgets require state to be carried over multiple frames (most typically ImGui often needs to remember what is the "active" widget).
311 to do so they need a unique ID. unique ID are typically derived from a string label, an integer index or a pointer.
312
313 Button("OK"); // Label = "OK", ID = hash of "OK"
314 Button("Cancel"); // Label = "Cancel", ID = hash of "Cancel"
315
316 - ID are uniquely scoped within windows, tree nodes, etc. so no conflict can happen if you have two buttons called "OK" in two different windows
317 or in two different locations of a tree.
318
319 - If you have a same ID twice in the same location, you'll have a conflict:
320
321 Button("OK");
322 Button("OK"); // ID collision! Both buttons will be treated as the same.
323
324 Fear not! this is easy to solve and there are many ways to solve it!
325
326 - When passing a label you can optionally specify extra unique ID information within string itself. This helps solving the simpler collision cases.
327 use "##" to pass a complement to the ID that won't be visible to the end-user:
328
329 Button("Play"); // Label = "Play", ID = hash of "Play"
330 Button("Play##foo1"); // Label = "Play", ID = hash of "Play##foo1" (different from above)
331 Button("Play##foo2"); // Label = "Play", ID = hash of "Play##foo2" (different from above)
332
333 - If you want to completely hide the label, but still need an ID:
334
335 Checkbox("##On", &b); // Label = "", ID = hash of "##On" (no label!)
336
337 - Occasionally/rarely you might want change a label while preserving a constant ID. This allows you to animate labels.
338 For example you may want to include varying information in a window title bar (and windows are uniquely identified by their ID.. obviously)
339 Use "###" to pass a label that isn't part of ID:
340
341 Button("Hello###ID"; // Label = "Hello", ID = hash of "ID"
342 Button("World###ID"; // Label = "World", ID = hash of "ID" (same as above)
343
344 sprintf(buf, "My game (%f FPS)###MyGame");
345 Begin(buf); // Variable label, ID = hash of "MyGame"
346
347 - Use PushID() / PopID() to create scopes and avoid ID conflicts within the same Window.
348 This is the most convenient way of distinguishing ID if you are iterating and creating many UI elements.
349 You can push a pointer, a string or an integer value. Remember that ID are formed from the concatenation of everything in the ID stack!
350
351 for (int i = 0; i < 100; i++)
352 {
353 PushID(i);
354 Button("Click"); // Label = "Click", ID = hash of integer + "label" (unique)
355 PopID();
356 }
357
358 for (int i = 0; i < 100; i++)
359 {
360 MyObject* obj = Objects[i];
361 PushID(obj);
362 Button("Click"); // Label = "Click", ID = hash of pointer + "label" (unique)
363 PopID();
364 }
365
366 for (int i = 0; i < 100; i++)
367 {
368 MyObject* obj = Objects[i];
369 PushID(obj->Name);
370 Button("Click"); // Label = "Click", ID = hash of string + "label" (unique)
371 PopID();
372 }
373
374 - More example showing that you can stack multiple prefixes into the ID stack:
375
376 Button("Click"); // Label = "Click", ID = hash of "Click"
377 PushID("node");
378 Button("Click"); // Label = "Click", ID = hash of "node" + "Click"
379 PushID(my_ptr);
380 Button("Click"); // Label = "Click", ID = hash of "node" + ptr + "Click"
381 PopID();
382 PopID();
383
384 - Tree nodes implicitly creates a scope for you by calling PushID().
385
386 Button("Click"); // Label = "Click", ID = hash of "Click"
387 if (TreeNode("node"))
388 {
389 Button("Click"); // Label = "Click", ID = hash of "node" + "Click"
390 TreePop();
391 }
392
393 - When working with trees, ID are used to preserve the open/close state of each tree node.
394 Depending on your use cases you may want to use strings, indices or pointers as ID.
395 e.g. when displaying a single object that may change over time (1-1 relationship), using a static string as ID will preserve your node open/closed state when the targeted object change.
396 e.g. when displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state differently. experiment and see what makes more sense!
397
398 Q: How can I tell when ImGui wants my mouse/keyboard inputs and when I can pass them to my application?
399 A: You can read the 'io.WantCaptureXXX' flags in the ImGuiIO structure. Preferably read them after calling ImGui::NewFrame() to avoid those flags lagging by one frame, but either should be fine.
400 When 'io.WantCaptureMouse' or 'io.WantCaptureKeyboard' flags are set you may want to discard/hide the inputs from the rest of your application.
401 When 'io.WantInputsCharacters' is set to may want to notify your OS to popup an on-screen keyboard, if available.
402 ImGui is tracking dragging and widget activity that may occur outside the boundary of a window, so 'io.WantCaptureMouse' is a more accurate and complete than testing for ImGui::IsMouseHoveringAnyWindow().
403 (Advanced note: text input releases focus on Return 'KeyDown', so the following Return 'KeyUp' event that your application receive will typically have 'io.WantcaptureKeyboard=false'.
404 Depending on your application logic it may or not be inconvenient. You might want to track which key-downs were for ImGui (e.g. with an array of bool) and filter out the corresponding key-ups.)
405
406 Q: How can I load a different font than the default? (default is an embedded version of ProggyClean.ttf, rendered at size 13)
407 A: Use the font atlas to load the TTF file you want:
408
409 ImGuiIO& io = ImGui::GetIO();
410 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
411 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
412
413 Q: How can I easily use icons in my application?
414 A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you main font. Then you can refer to icons within your strings.
415 Read 'How can I load multiple fonts?' and the file 'extra_fonts/README.txt' for instructions.
416
417 Q: How can I load multiple fonts?
418 A: Use the font atlas to pack them into a single texture:
419 (Read extra_fonts/README.txt and the code in ImFontAtlas for more details.)
420
421 ImGuiIO& io = ImGui::GetIO();
422 ImFont* font0 = io.Fonts->AddFontDefault();
423 ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
424 ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
425 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
426 // the first loaded font gets used by default
427 // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
428
429 // Options
430 ImFontConfig config;
431 config.OversampleH = 3;
432 config.OversampleV = 1;
433 config.GlyphExtraSpacing.x = 1.0f;
434 io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config);
435
436 // Combine multiple fonts into one (e.g. for icon fonts)
437 ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
438 ImFontConfig config;
439 config.MergeMode = true;
440 io.Fonts->AddFontDefault();
441 io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font
442 io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs
443
444 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
445 A: When loading a font, pass custom Unicode ranges to specify the glyphs to load.
446 All your strings needs to use UTF-8 encoding. Specifying literal in your source code using a local code page (such as CP-923 for Japanese or CP-1251 for Cyrillic) will not work.
447 In C++11 you can encode a string literal in UTF-8 by using the u8"hello" syntax. Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
448 You can also try to remap your local codepage characters to their Unicode codepoint using font->AddRemapChar(), but international users may have problems reading/editing your source code.
449
450 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese()); // Load Japanese characters
451 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
452 io.ImeWindowHandle = MY_HWND; // To input using Microsoft IME, give ImGui the hwnd of your application
453
454 As for text input, depends on you passing the right character code to io.AddInputCharacter(). The example applications do that.
455
456 Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
457 A: The easiest way is to create a dummy window. Call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flag, zero background alpha,
458 then retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
459
460 - tip: the construct 'IMGUI_ONCE_UPON_A_FRAME { ... }' will run the block of code only once a frame. You can use it to quickly add custom UI in the middle of a deep nested inner loop in your code.
461 - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug"
462 - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window. this is also useful to set yourself in the context of another window (to get/set other settings)
463 - tip: you can call Render() multiple times (e.g for VR renders).
464 - tip: call and read the ShowTestWindow() code in imgui_demo.cpp for more example of how to use ImGui!
465
466
467 ISSUES & TODO-LIST
468 ==================
469 Issue numbers (#) refer to github issues listed at https://github.com/ocornut/imgui/issues
470 The list below consist mostly of ideas noted down before they are requested/discussed by users (at which point it usually moves to the github)
471
472 - doc: add a proper documentation+regression testing system (#435)
473 - window: add a way for very transient windows (non-saved, temporary overlay over hundreds of objects) to "clean" up from the global window list. perhaps a lightweight explicit cleanup pass.
474 - window: calling SetNextWindowSize() every frame with <= 0 doesn't do anything, may be useful to allow (particularly when used for a single axis) (#690)
475 - window: auto-fit feedback loop when user relies on any dynamic layout (window width multiplier, column) appears weird to end-user. clarify.
476 - window: allow resizing of child windows (possibly given min/max for each axis?)
477 - window: background options for child windows, border option (disable rounding)
478 - window: add a way to clear an existing window instead of appending (e.g. for tooltip override using a consistent api rather than the deferred tooltip)
479 - window: resizing from any sides? + mouse cursor directives for app.
480 !- window: begin with *p_open == false should return false.
481 - window: get size/pos helpers given names (see discussion in #249)
482 - window: a collapsed window can be stuck behind the main menu bar?
483 - window: when window is small, prioritize resize button over close button.
484 - window: detect extra End() call that pop the "Debug" window out and assert at call site instead of later.
485 - window/tooltip: allow to set the width of a tooltip to allow TextWrapped() etc. while keeping the height automatic.
486 - window: increase minimum size of a window with menus or fix the menu rendering so that it doesn't look odd.
487 - draw-list: maintaining bounding box per command would allow to merge draw command when clipping isn't relied on (typical non-scrolling window or non-overflowing column would merge with previous command).
488 !- scrolling: allow immediately effective change of scroll if we haven't appended items yet
489 - splitter/separator: formalize the splitter idiom into an official api (we want to handle n-way split) (#319)
490 - widgets: display mode: widget-label, label-widget (aligned on column or using fixed size), label-newline-tab-widget etc.
491 - widgets: clean up widgets internal toward exposing everything.
492 - widgets: add disabled and read-only modes (#211)
493 - main: considering adding an Init() function? some constructs are awkward in the implementation because of the lack of them.
494 !- main: make it so that a frame with no window registered won't refocus every window on subsequent frames (~bump LastFrameActive of all windows).
495 - main: IsItemHovered() make it more consistent for various type of widgets, widgets with multiple components, etc. also effectively IsHovered() region sometimes differs from hot region, e.g tree nodes
496 - main: IsItemHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode?
497 - input text: clean up the mess caused by converting UTF-8 <> wchar. the code is rather inefficient right now and super fragile.
498 - input text: reorganize event handling, allow CharFilter to modify buffers, allow multiple events? (#541)
499 - input text: expose CursorPos in char filter event (#816)
500 - input text: flag to disable live update of the user buffer (also applies to float/int text input)
501 - input text: resize behavior - field could stretch when being edited? hover tooltip shows more text?
502 - input text: add ImGuiInputTextFlags_EnterToApply? (off #218)
503 - input text: add discard flag (e.g. ImGuiInputTextFlags_DiscardActiveBuffer) or make it easier to clear active focus for text replacement during edition (#725)
504 - input text multi-line: don't directly call AddText() which does an unnecessary vertex reserve for character count prior to clipping. and/or more line-based clipping to AddText(). and/or reorganize TextUnformatted/RenderText for more efficiency for large text (e.g TextUnformatted could clip and log separately, etc).
505 - input text multi-line: way to dynamically grow the buffer without forcing the user to initially allocate for worse case (follow up on #200)
506 - input text multi-line: line numbers? status bar? (follow up on #200)
507 - input text multi-line: behave better when user changes input buffer while editing is active (even though it is illegal behavior). namely, the change of buffer can create a scrollbar glitch (#725)
508 - input text: allow centering/positioning text so that ctrl+clicking Drag or Slider keeps the textual value at the same pixel position.
509 - input number: optional range min/max for Input*() functions
510 - input number: holding [-]/[+] buttons could increase the step speed non-linearly (or user-controlled)
511 - input number: use mouse wheel to step up/down
512 - input number: applying arithmetics ops (+,-,*,/) messes up with text edit undo stack.
513 - button: provide a button that looks framed.
514 - text: proper alignment options
515 - image/image button: misalignment on padded/bordered button?
516 - image/image button: parameters are confusing, image() has tint_col,border_col whereas imagebutton() has bg_col/tint_col. Even thou they are different parameters ordering could be more consistent. can we fix that?
517 - layout: horizontal layout helper (#97)
518 - layout: horizontal flow until no space left (#404)
519 - layout: more generic alignment state (left/right/centered) for single items?
520 - layout: clean up the InputFloatN/SliderFloatN/ColorEdit4 layout code. item width should include frame padding.
521 - layout: BeginGroup() needs a border option.
522 - columns: declare column set (each column: fixed size, %, fill, distribute default size among fills) (#513, #125)
523 - columns: add a conditional parameter to SetColumnOffset() (#513, #125)
524 - columns: separator function or parameter that works within the column (currently Separator() bypass all columns) (#125)
525 - columns: columns header to act as button (~sort op) and allow resize/reorder (#513, #125)
526 - columns: user specify columns size (#513, #125)
527 - columns: flag to add horizontal separator above/below?
528 - columns/layout: setup minimum line height (equivalent of automatically calling AlignFirstTextHeightToWidgets)
529 - combo: sparse combo boxes (via function call?) / iterators
530 - combo: contents should extends to fit label if combo widget is small
531 - combo/listbox: keyboard control. need InputText-like non-active focus + key handling. considering keyboard for custom listbox (pr #203)
532 - listbox: multiple selection
533 - listbox: user may want to initial scroll to focus on the one selected value?
534 - listbox: keyboard navigation.
535 - listbox: scrolling should track modified selection.
536 !- popups/menus: clarify usage of popups id, how MenuItem/Selectable closing parent popups affects the ID, etc. this is quite fishy needs improvement! (#331, #402)
537 - popups: add variant using global identifier similar to Begin/End (#402)
538 - popups: border options. richer api like BeginChild() perhaps? (#197)
539 - tooltip: tooltip that doesn't fit in entire screen seems to lose their "last preferred button" and may teleport when moving mouse
540 - menus: local shortcuts, global shortcuts (#456, #126)
541 - menus: icons
542 - menus: menubars: some sort of priority / effect of main menu-bar on desktop size?
543 - menus: calling BeginMenu() twice with a same name doesn't seem to append nicely
544 - statusbar: add a per-window status bar helper similar to what menubar does.
545 - tabs (#261, #351)
546 - separator: separator on the initial position of a window is not visible (cursorpos.y <= clippos.y)
547 !- color: the color helpers/typing is a mess and needs sorting out.
548 - color: add a better color picker (#346)
549 - node/graph editor (#306)
550 - pie menus patterns (#434)
551 - drag'n drop, dragging helpers (carry dragging info, visualize drag source before clicking, drop target, etc.) (#143, #479)
552 - plot: PlotLines() should use the polygon-stroke facilities (currently issues with averaging normals)
553 - plot: make it easier for user to draw extra stuff into the graph (e.g: draw basis, highlight certain points, 2d plots, multiple plots)
554 - plot: "smooth" automatic scale over time, user give an input 0.0(full user scale) 1.0(full derived from value)
555 - plot: add a helper e.g. Plot(char* label, float value, float time_span=2.0f) that stores values and Plot them for you - probably another function name. and/or automatically allow to plot ANY displayed value (more reliance on stable ID)
556 - slider: allow using the [-]/[+] buttons used by InputFloat()/InputInt()
557 - slider: initial absolute click is imprecise. change to relative movement slider (same as scrollbar).
558 - slider: add dragging-based widgets to edit values with mouse (on 2 axises), saving screen real-estate.
559 - slider: tint background based on value (e.g. v_min -> v_max, or use 0.0f either side of the sign)
560 - slider & drag: int data passing through a float
561 - drag float: up/down axis
562 - drag float: added leeway on edge (e.g. a few invisible steps past the clamp limits)
563 - tree node / optimization: avoid formatting when clipped.
564 - tree node: tree-node/header right-most side doesn't take account of horizontal scrolling.
565 - tree node: add treenode/treepush int variants? not there because (void*) cast from int warns on some platforms/settings?
566 - tree node: try to apply scrolling at time of TreePop() if node was just opened and end of node is past scrolling limits?
567 - tree node / selectable render mismatch which is visible if you use them both next to each other (e.g. cf. property viewer)
568 - tree node: tweak color scheme to distinguish headers from selected tree node (#581)
569 - textwrapped: figure out better way to use TextWrapped() in an always auto-resize context (tooltip, etc.) (#249)
570 - settings: write more decent code to allow saving/loading new fields
571 - settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file
572 - style: add window shadows.
573 - style/optimization: store rounded corners in texture to use 1 quad per corner (filled and wireframe) to lower the cost of rounding.
574 - style: color-box not always square?
575 - style: a concept of "compact style" that the end-user can easily rely on (e.g. PushStyleCompact()?) that maps to other settings? avoid implementing duplicate helpers such as SmallCheckbox(), etc.
576 - style: try to make PushStyleVar() more robust to incorrect parameters (to be more friendly to edit & continues situation).
577 - style: global scale setting.
578 - style: WindowPadding needs to be EVEN needs the 0.5 multiplier probably have a subtle effect on clip rectangle
579 - text: simple markup language for color change?
580 - font: dynamic font atlas to avoid baking huge ranges into bitmap and make scaling easier.
581 - font: small opt: for monospace font (like the defalt one) we can trim IndexXAdvance as long as trailing value is == FallbackXAdvance
582 - font: add support for kerning, probably optional. perhaps default to (32..128)^2 matrix ~ 36KB then hash fallback.
583 - font: add a simpler CalcTextSizeA() api? current one ok but not welcome if user needs to call it directly (without going through ImGui::CalcTextSize)
584 - font: fix AddRemapChar() to work before font has been built.
585 - log: LogButtons() options for specifying depth and/or hiding depth slider
586 - log: have more control over the log scope (e.g. stop logging when leaving current tree node scope)
587 - log: be able to log anything (e.g. right-click on a window/tree-node, shows context menu? log into tty/file/clipboard)
588 - log: let user copy any window content to clipboard easily (CTRL+C on windows? while moving it? context menu?). code is commented because it fails with multiple Begin/End pairs.
589 - filters: set a current filter that tree node can automatically query to hide themselves
590 - filters: handle wildcards (with implicit leading/trailing *), regexps
591 - shortcuts: add a shortcut api, e.g. parse "&Save" and/or "Save (CTRL+S)", pass in to widgets or provide simple ways to use (button=activate, input=focus)
592 !- keyboard: tooltip & combo boxes are messing up / not honoring keyboard tabbing
593 - keyboard: full keyboard navigation and focus. (#323)
594 - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622)
595 - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame)
596 - input: rework IO system to be able to pass actual ordered/timestamped events. (~#335, #71)
597 - input: allow to decide and pass explicit double-clicks (e.g. for windows by the CS_DBLCLKS style).
598 - input: support track pad style scrolling & slider edit.
599 - misc: provide a way to compile out the entire implementation while providing a dummy API (e.g. #define IMGUI_DUMMY_IMPL)
600 - misc: double-clicking on title bar to minimize isn't consistent, perhaps move to single-click on left-most collapse icon?
601 - misc: provide HoveredTime and ActivatedTime to ease the creation of animations.
602 - style editor: have a more global HSV setter (e.g. alter hue on all elements). consider replacing active/hovered by offset in HSV space? (#438)
603 - style editor: color child window height expressed in multiple of line height.
604 - remote: make a system like RemoteImGui first-class citizen/project (#75)
605 - drawlist: move Font, FontSize, FontTexUvWhitePixel inside ImDrawList and make it self-contained (apart from drawing settings?)
606 - drawlist: end-user probably can't call Clear() directly because we expect a texture to be pushed in the stack.
607 - examples: directx9: save/restore device state more thoroughly.
608 - examples: window minimize, maximize (#583)
609 - optimization: add a flag to disable most of rendering, for the case where the user expect to skip it (#335)
610 - optimization: use another hash function than crc32, e.g. FNV1a
611 - optimization/render: merge command-lists with same clip-rect into one even if they aren't sequential? (as long as in-between clip rectangle don't overlap)?
612 - optimization: turn some the various stack vectors into statically-sized arrays
613 - optimization: better clipping for multi-component widgets
614 */
615
616 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
617 #define _CRT_SECURE_NO_WARNINGS
618 #endif
619
620 #include "imgui.h"
621 #define IMGUI_DEFINE_MATH_OPERATORS
622 #define IMGUI_DEFINE_PLACEMENT_NEW
623 #include "imgui_internal.h"
624
625 #include <ctype.h> // toupper, isprint
626 #include <stdlib.h> // NULL, malloc, free, qsort, atoi
627 #include <stdio.h> // vsnprintf, sscanf, printf
628 #include <limits.h> // INT_MIN, INT_MAX
629 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
630 #include <stddef.h> // intptr_t
631 #else
632 #include <stdint.h> // intptr_t
633 #endif
634
635 #ifdef _MSC_VER
636 #pragma warning (disable: 4127) // condition expression is constant
637 #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
638 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
639 #endif
640
641 // Clang warnings with -Weverything
642 #ifdef __clang__
643 #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
644 #pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
645 #pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
646 #pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.
647 #pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it.
648 #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
649 #pragma clang diagnostic ignored "-Wformat-pedantic" // warning : format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic.
650 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' //
651 #elif defined(__GNUC__)
652 #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
653 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
654 #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
655 #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
656 #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
657 #pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'xxxx' to type 'xxxx' casts away qualifiers
658 #endif
659
660 //-------------------------------------------------------------------------
661 // Forward Declarations
662 //-------------------------------------------------------------------------
663
664 static void LogRenderedText(const ImVec2& ref_pos, const char* text, const char* text_end = NULL);
665
666 static void PushMultiItemsWidths(int components, float w_full = 0.0f);
667 static float GetDraggedColumnOffset(int column_index);
668
669 static bool IsKeyPressedMap(ImGuiKey key, bool repeat = true);
670
671 static ImFont* GetDefaultFont();
672 static void SetCurrentFont(ImFont* font);
673 static void SetCurrentWindow(ImGuiWindow* window);
674 static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y);
675 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond);
676 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond);
677 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiSetCond cond);
678 static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs);
679 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
680 static inline bool IsWindowContentHoverable(ImGuiWindow* window);
681 static void ClearSetNextWindowData();
682 static void CheckStacksSize(ImGuiWindow* window, bool write);
683 static void Scrollbar(ImGuiWindow* window, bool horizontal);
684
685 static void AddDrawListToRenderList(ImVector<ImDrawList*>& out_render_list, ImDrawList* draw_list);
686 static void AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiWindow* window);
687 static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>& out_sorted_windows, ImGuiWindow* window);
688
689 static ImGuiIniData* FindWindowSettings(const char* name);
690 static ImGuiIniData* AddWindowSettings(const char* name);
691 static void LoadIniSettingsFromDisk(const char* ini_filename);
692 static void SaveIniSettingsToDisk(const char* ini_filename);
693 static void MarkIniSettingsDirty();
694
695 static void PushColumnClipRect(int column_index = -1);
696 static ImRect GetVisibleRect();
697
698 static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags);
699 static void CloseInactivePopups();
700 static void ClosePopupToLevel(int remaining);
701 static void ClosePopup(ImGuiID id);
702 static bool IsPopupOpen(ImGuiID id);
703 static ImGuiWindow* GetFrontMostModalRootWindow();
704 static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& rect_to_avoid);
705
706 static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data);
707 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
708 static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false);
709
710 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size);
711 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size);
712 static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2);
713 static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format);
714
715 //-----------------------------------------------------------------------------
716 // Platform dependent default implementations
717 //-----------------------------------------------------------------------------
718
719 static const char* GetClipboardTextFn_DefaultImpl(void* user_data);
720 static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
721 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
722
723 //-----------------------------------------------------------------------------
724 // Context
725 //-----------------------------------------------------------------------------
726
727 // Default font atlas storage .
728 // New contexts always point by default to this font atlas. It can be changed by reassigning the GetIO().Fonts variable.
729 static ImFontAtlas GImDefaultFontAtlas;
730
731 // Default context storage + current context pointer.
732 // Implicitely used by all ImGui functions. Always assumed to be != NULL. Change to a different context by calling ImGui::SetCurrentContext()
733 // ImGui is currently not thread-safe because of this variable. If you want thread-safety to allow N threads to access N different contexts, you might work around it by:
734 // - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts)
735 // - or: Changing this variable to be TLS. You may #define GImGui in imconfig.h for further custom hackery. Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
736 #ifndef GImGui
737 static ImGuiContext GImDefaultContext;
738 ImGuiContext* GImGui = &GImDefaultContext;
739 #endif
740
741 //-----------------------------------------------------------------------------
742 // User facing structures
743 //-----------------------------------------------------------------------------
744
ImGuiStyle()745 ImGuiStyle::ImGuiStyle()
746 {
747 Alpha = 1.0f; // Global alpha applies to everything in ImGui
748 WindowPadding = ImVec2(8,8); // Padding within a window
749 WindowMinSize = ImVec2(32,32); // Minimum window size
750 WindowRounding = 9.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows
751 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
752 ChildWindowRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
753 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
754 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
755 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
756 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
757 TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!
758 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
759 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns
760 ScrollbarSize = 16.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
761 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
762 GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
763 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
764 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
765 DisplayWindowPadding = ImVec2(22,22); // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows.
766 DisplaySafeAreaPadding = ImVec2(4,4); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
767 AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
768 AntiAliasedShapes = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
769 CurveTessellationTol = 1.25f; // Tessellation tolerance. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
770
771 Colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
772 Colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
773 Colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.70f);
774 Colors[ImGuiCol_ChildWindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
775 Colors[ImGuiCol_PopupBg] = ImVec4(0.05f, 0.05f, 0.10f, 0.90f);
776 Colors[ImGuiCol_Border] = ImVec4(0.70f, 0.70f, 0.70f, 0.65f);
777 Colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
778 Colors[ImGuiCol_FrameBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.30f); // Background of checkbox, radio button, plot, slider, text input
779 Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.90f, 0.80f, 0.80f, 0.40f);
780 Colors[ImGuiCol_FrameBgActive] = ImVec4(0.90f, 0.65f, 0.65f, 0.45f);
781 Colors[ImGuiCol_TitleBg] = ImVec4(0.27f, 0.27f, 0.54f, 0.83f);
782 Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.40f, 0.40f, 0.80f, 0.20f);
783 Colors[ImGuiCol_TitleBgActive] = ImVec4(0.32f, 0.32f, 0.63f, 0.87f);
784 Colors[ImGuiCol_MenuBarBg] = ImVec4(0.40f, 0.40f, 0.55f, 0.80f);
785 Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.20f, 0.25f, 0.30f, 0.60f);
786 Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.40f, 0.40f, 0.80f, 0.30f);
787 Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.80f, 0.40f);
788 Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 0.40f);
789 Colors[ImGuiCol_ComboBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.99f);
790 Colors[ImGuiCol_CheckMark] = ImVec4(0.90f, 0.90f, 0.90f, 0.50f);
791 Colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
792 Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
793 Colors[ImGuiCol_Button] = ImVec4(0.67f, 0.40f, 0.40f, 0.60f);
794 Colors[ImGuiCol_ButtonHovered] = ImVec4(0.67f, 0.40f, 0.40f, 1.00f);
795 Colors[ImGuiCol_ButtonActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
796 Colors[ImGuiCol_Header] = ImVec4(0.40f, 0.40f, 0.90f, 0.45f);
797 Colors[ImGuiCol_HeaderHovered] = ImVec4(0.45f, 0.45f, 0.90f, 0.80f);
798 Colors[ImGuiCol_HeaderActive] = ImVec4(0.53f, 0.53f, 0.87f, 0.80f);
799 Colors[ImGuiCol_Column] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
800 Colors[ImGuiCol_ColumnHovered] = ImVec4(0.70f, 0.60f, 0.60f, 1.00f);
801 Colors[ImGuiCol_ColumnActive] = ImVec4(0.90f, 0.70f, 0.70f, 1.00f);
802 Colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
803 Colors[ImGuiCol_ResizeGripHovered] = ImVec4(1.00f, 1.00f, 1.00f, 0.60f);
804 Colors[ImGuiCol_ResizeGripActive] = ImVec4(1.00f, 1.00f, 1.00f, 0.90f);
805 Colors[ImGuiCol_CloseButton] = ImVec4(0.50f, 0.50f, 0.90f, 0.50f);
806 Colors[ImGuiCol_CloseButtonHovered] = ImVec4(0.70f, 0.70f, 0.90f, 0.60f);
807 Colors[ImGuiCol_CloseButtonActive] = ImVec4(0.70f, 0.70f, 0.70f, 1.00f);
808 Colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
809 Colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
810 Colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
811 Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
812 Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
813 Colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
814 }
815
ImGuiIO()816 ImGuiIO::ImGuiIO()
817 {
818 // Most fields are initialized with zero
819 memset(this, 0, sizeof(*this));
820
821 DisplaySize = ImVec2(-1.0f, -1.0f);
822 DeltaTime = 1.0f/60.0f;
823 IniSavingRate = 5.0f;
824 IniFilename = "imgui.ini";
825 LogFilename = "imgui_log.txt";
826 Fonts = &GImDefaultFontAtlas;
827 FontGlobalScale = 1.0f;
828 FontDefault = NULL;
829 DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
830 MousePos = ImVec2(-1,-1);
831 MousePosPrev = ImVec2(-1,-1);
832 MouseDoubleClickTime = 0.30f;
833 MouseDoubleClickMaxDist = 6.0f;
834 MouseDragThreshold = 6.0f;
835 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++)
836 MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
837 for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++)
838 KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
839 for (int i = 0; i < ImGuiKey_COUNT; i++)
840 KeyMap[i] = -1;
841 KeyRepeatDelay = 0.250f;
842 KeyRepeatRate = 0.050f;
843 UserData = NULL;
844
845 // User functions
846 RenderDrawListsFn = NULL;
847 MemAllocFn = malloc;
848 MemFreeFn = free;
849 GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
850 SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
851 ClipboardUserData = NULL;
852 ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
853
854 // Set OS X style defaults based on __APPLE__ compile time flag
855 #ifdef __APPLE__
856 OSXBehaviors = true;
857 #endif
858 }
859
860 // Pass in translated ASCII characters for text input.
861 // - with glfw you can get those from the callback set in glfwSetCharCallback()
862 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(ImWchar c)863 void ImGuiIO::AddInputCharacter(ImWchar c)
864 {
865 const int n = ImStrlenW(InputCharacters);
866 if (n + 1 < IM_ARRAYSIZE(InputCharacters))
867 {
868 InputCharacters[n] = c;
869 InputCharacters[n+1] = '\0';
870 }
871 }
872
AddInputCharactersUTF8(const char * utf8_chars)873 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
874 {
875 // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more
876 const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar);
877 ImWchar wchars[wchars_buf_len];
878 ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL);
879 for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++)
880 AddInputCharacter(wchars[i]);
881 }
882
883 //-----------------------------------------------------------------------------
884 // HELPERS
885 //-----------------------------------------------------------------------------
886
887 #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose
888 #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255
889
890 // Play it nice with Windows users. Notepad in 2015 still doesn't display text data with Unix-style \n.
891 #ifdef _WIN32
892 #define IM_NEWLINE "\r\n"
893 #else
894 #define IM_NEWLINE "\n"
895 #endif
896
ImIsPointInTriangle(const ImVec2 & p,const ImVec2 & a,const ImVec2 & b,const ImVec2 & c)897 bool ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, const ImVec2& c)
898 {
899 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
900 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
901 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
902 return ((b1 == b2) && (b2 == b3));
903 }
904
ImStricmp(const char * str1,const char * str2)905 int ImStricmp(const char* str1, const char* str2)
906 {
907 int d;
908 while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
909 return d;
910 }
911
ImStrnicmp(const char * str1,const char * str2,int count)912 int ImStrnicmp(const char* str1, const char* str2, int count)
913 {
914 int d = 0;
915 while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
916 return d;
917 }
918
ImStrncpy(char * dst,const char * src,int count)919 void ImStrncpy(char* dst, const char* src, int count)
920 {
921 if (count < 1) return;
922 strncpy(dst, src, (size_t)count);
923 dst[count-1] = 0;
924 }
925
ImStrdup(const char * str)926 char* ImStrdup(const char *str)
927 {
928 size_t len = strlen(str) + 1;
929 void* buff = ImGui::MemAlloc(len);
930 return (char*)memcpy(buff, (const void*)str, len);
931 }
932
ImStrlenW(const ImWchar * str)933 int ImStrlenW(const ImWchar* str)
934 {
935 int n = 0;
936 while (*str++) n++;
937 return n;
938 }
939
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)940 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
941 {
942 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
943 buf_mid_line--;
944 return buf_mid_line;
945 }
946
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)947 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
948 {
949 if (!needle_end)
950 needle_end = needle + strlen(needle);
951
952 const char un0 = (char)toupper(*needle);
953 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
954 {
955 if (toupper(*haystack) == un0)
956 {
957 const char* b = needle + 1;
958 for (const char* a = haystack + 1; b < needle_end; a++, b++)
959 if (toupper(*a) != toupper(*b))
960 break;
961 if (b == needle_end)
962 return haystack;
963 }
964 haystack++;
965 }
966 return NULL;
967 }
968
969
970 // MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
971 // Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm.
ImFormatString(char * buf,int buf_size,const char * fmt,...)972 int ImFormatString(char* buf, int buf_size, const char* fmt, ...)
973 {
974 IM_ASSERT(buf_size > 0);
975 va_list args;
976 va_start(args, fmt);
977 int w = vsnprintf(buf, buf_size, fmt, args);
978 va_end(args);
979 if (w == -1 || w >= buf_size)
980 w = buf_size - 1;
981 buf[w] = 0;
982 return w;
983 }
984
ImFormatStringV(char * buf,int buf_size,const char * fmt,va_list args)985 int ImFormatStringV(char* buf, int buf_size, const char* fmt, va_list args)
986 {
987 IM_ASSERT(buf_size > 0);
988 int w = vsnprintf(buf, buf_size, fmt, args);
989 if (w == -1 || w >= buf_size)
990 w = buf_size - 1;
991 buf[w] = 0;
992 return w;
993 }
994
995 // Pass data_size==0 for zero-terminated strings
996 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImHash(const void * data,int data_size,ImU32 seed)997 ImU32 ImHash(const void* data, int data_size, ImU32 seed)
998 {
999 static ImU32 crc32_lut[256] = { 0 };
1000 if (!crc32_lut[1])
1001 {
1002 const ImU32 polynomial = 0xEDB88320;
1003 for (ImU32 i = 0; i < 256; i++)
1004 {
1005 ImU32 crc = i;
1006 for (ImU32 j = 0; j < 8; j++)
1007 crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial);
1008 crc32_lut[i] = crc;
1009 }
1010 }
1011
1012 seed = ~seed;
1013 ImU32 crc = seed;
1014 const unsigned char* current = (const unsigned char*)data;
1015
1016 if (data_size > 0)
1017 {
1018 // Known size
1019 while (data_size--)
1020 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++];
1021 }
1022 else
1023 {
1024 // Zero-terminated string
1025 while (unsigned char c = *current++)
1026 {
1027 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1028 // Because this syntax is rarely used we are optimizing for the common case.
1029 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1030 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller.
1031 if (c == '#' && current[0] == '#' && current[1] == '#')
1032 crc = seed;
1033 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1034 }
1035 }
1036 return ~crc;
1037 }
1038
1039 //-----------------------------------------------------------------------------
1040 // ImText* helpers
1041 //-----------------------------------------------------------------------------
1042
1043 // Convert UTF-8 to 32-bits character, process single character input.
1044 // Based on stb_from_utf8() from github.com/nothings/stb/
1045 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1046 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1047 {
1048 unsigned int c = (unsigned int)-1;
1049 const unsigned char* str = (const unsigned char*)in_text;
1050 if (!(*str & 0x80))
1051 {
1052 c = (unsigned int)(*str++);
1053 *out_char = c;
1054 return 1;
1055 }
1056 if ((*str & 0xe0) == 0xc0)
1057 {
1058 *out_char = 0xFFFD; // will be invalid but not end of string
1059 if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1060 if (*str < 0xc2) return 2;
1061 c = (unsigned int)((*str++ & 0x1f) << 6);
1062 if ((*str & 0xc0) != 0x80) return 2;
1063 c += (*str++ & 0x3f);
1064 *out_char = c;
1065 return 2;
1066 }
1067 if ((*str & 0xf0) == 0xe0)
1068 {
1069 *out_char = 0xFFFD; // will be invalid but not end of string
1070 if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1071 if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1072 if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1073 c = (unsigned int)((*str++ & 0x0f) << 12);
1074 if ((*str & 0xc0) != 0x80) return 3;
1075 c += (unsigned int)((*str++ & 0x3f) << 6);
1076 if ((*str & 0xc0) != 0x80) return 3;
1077 c += (*str++ & 0x3f);
1078 *out_char = c;
1079 return 3;
1080 }
1081 if ((*str & 0xf8) == 0xf0)
1082 {
1083 *out_char = 0xFFFD; // will be invalid but not end of string
1084 if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1085 if (*str > 0xf4) return 4;
1086 if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1087 if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1088 c = (unsigned int)((*str++ & 0x07) << 18);
1089 if ((*str & 0xc0) != 0x80) return 4;
1090 c += (unsigned int)((*str++ & 0x3f) << 12);
1091 if ((*str & 0xc0) != 0x80) return 4;
1092 c += (unsigned int)((*str++ & 0x3f) << 6);
1093 if ((*str & 0xc0) != 0x80) return 4;
1094 c += (*str++ & 0x3f);
1095 // utf-8 encodings of values used in surrogate pairs are invalid
1096 if ((c & 0xFFFFF800) == 0xD800) return 4;
1097 *out_char = c;
1098 return 4;
1099 }
1100 *out_char = 0;
1101 return 0;
1102 }
1103
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1104 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1105 {
1106 ImWchar* buf_out = buf;
1107 ImWchar* buf_end = buf + buf_size;
1108 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1109 {
1110 unsigned int c;
1111 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1112 if (c == 0)
1113 break;
1114 if (c < 0x10000) // FIXME: Losing characters that don't fit in 2 bytes
1115 *buf_out++ = (ImWchar)c;
1116 }
1117 *buf_out = 0;
1118 if (in_text_remaining)
1119 *in_text_remaining = in_text;
1120 return (int)(buf_out - buf);
1121 }
1122
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1123 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1124 {
1125 int char_count = 0;
1126 while ((!in_text_end || in_text < in_text_end) && *in_text)
1127 {
1128 unsigned int c;
1129 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1130 if (c == 0)
1131 break;
1132 if (c < 0x10000)
1133 char_count++;
1134 }
1135 return char_count;
1136 }
1137
1138 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1139 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1140 {
1141 if (c < 0x80)
1142 {
1143 buf[0] = (char)c;
1144 return 1;
1145 }
1146 if (c < 0x800)
1147 {
1148 if (buf_size < 2) return 0;
1149 buf[0] = (char)(0xc0 + (c >> 6));
1150 buf[1] = (char)(0x80 + (c & 0x3f));
1151 return 2;
1152 }
1153 if (c >= 0xdc00 && c < 0xe000)
1154 {
1155 return 0;
1156 }
1157 if (c >= 0xd800 && c < 0xdc00)
1158 {
1159 if (buf_size < 4) return 0;
1160 buf[0] = (char)(0xf0 + (c >> 18));
1161 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1162 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1163 buf[3] = (char)(0x80 + ((c ) & 0x3f));
1164 return 4;
1165 }
1166 //else if (c < 0x10000)
1167 {
1168 if (buf_size < 3) return 0;
1169 buf[0] = (char)(0xe0 + (c >> 12));
1170 buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1171 buf[2] = (char)(0x80 + ((c ) & 0x3f));
1172 return 3;
1173 }
1174 }
1175
ImTextCountUtf8BytesFromChar(unsigned int c)1176 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1177 {
1178 if (c < 0x80) return 1;
1179 if (c < 0x800) return 2;
1180 if (c >= 0xdc00 && c < 0xe000) return 0;
1181 if (c >= 0xd800 && c < 0xdc00) return 4;
1182 return 3;
1183 }
1184
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1185 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1186 {
1187 char* buf_out = buf;
1188 const char* buf_end = buf + buf_size;
1189 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1190 {
1191 unsigned int c = (unsigned int)(*in_text++);
1192 if (c < 0x80)
1193 *buf_out++ = (char)c;
1194 else
1195 buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1196 }
1197 *buf_out = 0;
1198 return (int)(buf_out - buf);
1199 }
1200
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1201 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1202 {
1203 int bytes_count = 0;
1204 while ((!in_text_end || in_text < in_text_end) && *in_text)
1205 {
1206 unsigned int c = (unsigned int)(*in_text++);
1207 if (c < 0x80)
1208 bytes_count++;
1209 else
1210 bytes_count += ImTextCountUtf8BytesFromChar(c);
1211 }
1212 return bytes_count;
1213 }
1214
ColorConvertU32ToFloat4(ImU32 in)1215 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1216 {
1217 float s = 1.0f/255.0f;
1218 return ImVec4(
1219 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1220 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1221 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1222 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1223 }
1224
ColorConvertFloat4ToU32(const ImVec4 & in)1225 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1226 {
1227 ImU32 out;
1228 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1229 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1230 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1231 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1232 return out;
1233 }
1234
GetColorU32(ImGuiCol idx,float alpha_mul)1235 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1236 {
1237 ImVec4 c = GImGui->Style.Colors[idx];
1238 c.w *= GImGui->Style.Alpha * alpha_mul;
1239 return ColorConvertFloat4ToU32(c);
1240 }
1241
GetColorU32(const ImVec4 & col)1242 ImU32 ImGui::GetColorU32(const ImVec4& col)
1243 {
1244 ImVec4 c = col;
1245 c.w *= GImGui->Style.Alpha;
1246 return ColorConvertFloat4ToU32(c);
1247 }
1248
1249 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1250 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
ColorConvertRGBtoHSV(float r,float g,float b,float & out_h,float & out_s,float & out_v)1251 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1252 {
1253 float K = 0.f;
1254 if (g < b)
1255 {
1256 const float tmp = g; g = b; b = tmp;
1257 K = -1.f;
1258 }
1259 if (r < g)
1260 {
1261 const float tmp = r; r = g; g = tmp;
1262 K = -2.f / 6.f - K;
1263 }
1264
1265 const float chroma = r - (g < b ? g : b);
1266 out_h = fabsf(K + (g - b) / (6.f * chroma + 1e-20f));
1267 out_s = chroma / (r + 1e-20f);
1268 out_v = r;
1269 }
1270
1271 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1272 // also http://en.wikipedia.org/wiki/HSL_and_HSV
ColorConvertHSVtoRGB(float h,float s,float v,float & out_r,float & out_g,float & out_b)1273 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1274 {
1275 if (s == 0.0f)
1276 {
1277 // gray
1278 out_r = out_g = out_b = v;
1279 return;
1280 }
1281
1282 h = fmodf(h, 1.0f) / (60.0f/360.0f);
1283 int i = (int)h;
1284 float f = h - (float)i;
1285 float p = v * (1.0f - s);
1286 float q = v * (1.0f - s * f);
1287 float t = v * (1.0f - s * (1.0f - f));
1288
1289 switch (i)
1290 {
1291 case 0: out_r = v; out_g = t; out_b = p; break;
1292 case 1: out_r = q; out_g = v; out_b = p; break;
1293 case 2: out_r = p; out_g = v; out_b = t; break;
1294 case 3: out_r = p; out_g = q; out_b = v; break;
1295 case 4: out_r = t; out_g = p; out_b = v; break;
1296 case 5: default: out_r = v; out_g = p; out_b = q; break;
1297 }
1298 }
1299
ImFileOpen(const char * filename,const char * mode)1300 FILE* ImFileOpen(const char* filename, const char* mode)
1301 {
1302 #if defined(_WIN32) && !defined(__CYGWIN__)
1303 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can)
1304 const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1305 const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1306 ImVector<ImWchar> buf;
1307 buf.resize(filename_wsize + mode_wsize);
1308 ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1309 ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1310 return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1311 #else
1312 return fopen(filename, mode);
1313 #endif
1314 }
1315
1316 // Load file content into memory
1317 // Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree()
ImFileLoadToMemory(const char * filename,const char * file_open_mode,int * out_file_size,int padding_bytes)1318 void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, int* out_file_size, int padding_bytes)
1319 {
1320 IM_ASSERT(filename && file_open_mode);
1321 if (out_file_size)
1322 *out_file_size = 0;
1323
1324 FILE* f;
1325 if ((f = ImFileOpen(filename, file_open_mode)) == NULL)
1326 return NULL;
1327
1328 long file_size_signed;
1329 if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
1330 {
1331 fclose(f);
1332 return NULL;
1333 }
1334
1335 int file_size = (int)file_size_signed;
1336 void* file_data = ImGui::MemAlloc(file_size + padding_bytes);
1337 if (file_data == NULL)
1338 {
1339 fclose(f);
1340 return NULL;
1341 }
1342 if (fread(file_data, 1, (size_t)file_size, f) != (size_t)file_size)
1343 {
1344 fclose(f);
1345 ImGui::MemFree(file_data);
1346 return NULL;
1347 }
1348 if (padding_bytes > 0)
1349 memset((void *)(((char*)file_data) + file_size), 0, padding_bytes);
1350
1351 fclose(f);
1352 if (out_file_size)
1353 *out_file_size = file_size;
1354
1355 return file_data;
1356 }
1357
1358 //-----------------------------------------------------------------------------
1359 // ImGuiStorage
1360 //-----------------------------------------------------------------------------
1361
1362 // Helper: Key->value storage
Clear()1363 void ImGuiStorage::Clear()
1364 {
1365 Data.clear();
1366 }
1367
1368 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::Pair> & data,ImGuiID key)1369 static ImVector<ImGuiStorage::Pair>::iterator LowerBound(ImVector<ImGuiStorage::Pair>& data, ImGuiID key)
1370 {
1371 ImVector<ImGuiStorage::Pair>::iterator first = data.begin();
1372 ImVector<ImGuiStorage::Pair>::iterator last = data.end();
1373 int count = (int)(last - first);
1374 while (count > 0)
1375 {
1376 int count2 = count / 2;
1377 ImVector<ImGuiStorage::Pair>::iterator mid = first + count2;
1378 if (mid->key < key)
1379 {
1380 first = ++mid;
1381 count -= count2 + 1;
1382 }
1383 else
1384 {
1385 count = count2;
1386 }
1387 }
1388 return first;
1389 }
1390
GetInt(ImGuiID key,int default_val) const1391 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1392 {
1393 ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1394 if (it == Data.end() || it->key != key)
1395 return default_val;
1396 return it->val_i;
1397 }
1398
GetBool(ImGuiID key,bool default_val) const1399 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1400 {
1401 return GetInt(key, default_val ? 1 : 0) != 0;
1402 }
1403
GetFloat(ImGuiID key,float default_val) const1404 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1405 {
1406 ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1407 if (it == Data.end() || it->key != key)
1408 return default_val;
1409 return it->val_f;
1410 }
1411
GetVoidPtr(ImGuiID key) const1412 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1413 {
1414 ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1415 if (it == Data.end() || it->key != key)
1416 return NULL;
1417 return it->val_p;
1418 }
1419
1420 // References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer.
GetIntRef(ImGuiID key,int default_val)1421 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1422 {
1423 ImVector<Pair>::iterator it = LowerBound(Data, key);
1424 if (it == Data.end() || it->key != key)
1425 it = Data.insert(it, Pair(key, default_val));
1426 return &it->val_i;
1427 }
1428
GetBoolRef(ImGuiID key,bool default_val)1429 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1430 {
1431 return (bool*)GetIntRef(key, default_val ? 1 : 0);
1432 }
1433
GetFloatRef(ImGuiID key,float default_val)1434 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1435 {
1436 ImVector<Pair>::iterator it = LowerBound(Data, key);
1437 if (it == Data.end() || it->key != key)
1438 it = Data.insert(it, Pair(key, default_val));
1439 return &it->val_f;
1440 }
1441
GetVoidPtrRef(ImGuiID key,void * default_val)1442 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1443 {
1444 ImVector<Pair>::iterator it = LowerBound(Data, key);
1445 if (it == Data.end() || it->key != key)
1446 it = Data.insert(it, Pair(key, default_val));
1447 return &it->val_p;
1448 }
1449
1450 // FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame)
SetInt(ImGuiID key,int val)1451 void ImGuiStorage::SetInt(ImGuiID key, int val)
1452 {
1453 ImVector<Pair>::iterator it = LowerBound(Data, key);
1454 if (it == Data.end() || it->key != key)
1455 {
1456 Data.insert(it, Pair(key, val));
1457 return;
1458 }
1459 it->val_i = val;
1460 }
1461
SetBool(ImGuiID key,bool val)1462 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1463 {
1464 SetInt(key, val ? 1 : 0);
1465 }
1466
SetFloat(ImGuiID key,float val)1467 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1468 {
1469 ImVector<Pair>::iterator it = LowerBound(Data, key);
1470 if (it == Data.end() || it->key != key)
1471 {
1472 Data.insert(it, Pair(key, val));
1473 return;
1474 }
1475 it->val_f = val;
1476 }
1477
SetVoidPtr(ImGuiID key,void * val)1478 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1479 {
1480 ImVector<Pair>::iterator it = LowerBound(Data, key);
1481 if (it == Data.end() || it->key != key)
1482 {
1483 Data.insert(it, Pair(key, val));
1484 return;
1485 }
1486 it->val_p = val;
1487 }
1488
SetAllInt(int v)1489 void ImGuiStorage::SetAllInt(int v)
1490 {
1491 for (int i = 0; i < Data.Size; i++)
1492 Data[i].val_i = v;
1493 }
1494
1495 //-----------------------------------------------------------------------------
1496 // ImGuiTextFilter
1497 //-----------------------------------------------------------------------------
1498
1499 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1500 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1501 {
1502 if (default_filter)
1503 {
1504 ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1505 Build();
1506 }
1507 else
1508 {
1509 InputBuf[0] = 0;
1510 CountGrep = 0;
1511 }
1512 }
1513
Draw(const char * label,float width)1514 bool ImGuiTextFilter::Draw(const char* label, float width)
1515 {
1516 if (width != 0.0f)
1517 ImGui::PushItemWidth(width);
1518 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1519 if (width != 0.0f)
1520 ImGui::PopItemWidth();
1521 if (value_changed)
1522 Build();
1523 return value_changed;
1524 }
1525
split(char separator,ImVector<TextRange> & out)1526 void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>& out)
1527 {
1528 out.resize(0);
1529 const char* wb = b;
1530 const char* we = wb;
1531 while (we < e)
1532 {
1533 if (*we == separator)
1534 {
1535 out.push_back(TextRange(wb, we));
1536 wb = we + 1;
1537 }
1538 we++;
1539 }
1540 if (wb != we)
1541 out.push_back(TextRange(wb, we));
1542 }
1543
Build()1544 void ImGuiTextFilter::Build()
1545 {
1546 Filters.resize(0);
1547 TextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
1548 input_range.split(',', Filters);
1549
1550 CountGrep = 0;
1551 for (int i = 0; i != Filters.Size; i++)
1552 {
1553 Filters[i].trim_blanks();
1554 if (Filters[i].empty())
1555 continue;
1556 if (Filters[i].front() != '-')
1557 CountGrep += 1;
1558 }
1559 }
1560
PassFilter(const char * text,const char * text_end) const1561 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
1562 {
1563 if (Filters.empty())
1564 return true;
1565
1566 if (text == NULL)
1567 text = "";
1568
1569 for (int i = 0; i != Filters.Size; i++)
1570 {
1571 const TextRange& f = Filters[i];
1572 if (f.empty())
1573 continue;
1574 if (f.front() == '-')
1575 {
1576 // Subtract
1577 if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL)
1578 return false;
1579 }
1580 else
1581 {
1582 // Grep
1583 if (ImStristr(text, text_end, f.begin(), f.end()) != NULL)
1584 return true;
1585 }
1586 }
1587
1588 // Implicit * grep
1589 if (CountGrep == 0)
1590 return true;
1591
1592 return false;
1593 }
1594
1595 //-----------------------------------------------------------------------------
1596 // ImGuiTextBuffer
1597 //-----------------------------------------------------------------------------
1598
1599 // On some platform vsnprintf() takes va_list by reference and modifies it.
1600 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1601 #ifndef va_copy
1602 #define va_copy(dest, src) (dest = src)
1603 #endif
1604
1605 // Helper: Text buffer for logging/accumulating text
appendv(const char * fmt,va_list args)1606 void ImGuiTextBuffer::appendv(const char* fmt, va_list args)
1607 {
1608 va_list args_copy;
1609 va_copy(args_copy, args);
1610
1611 int len = vsnprintf(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
1612 if (len <= 0)
1613 return;
1614
1615 const int write_off = Buf.Size;
1616 const int needed_sz = write_off + len;
1617 if (write_off + len >= Buf.Capacity)
1618 {
1619 int double_capacity = Buf.Capacity * 2;
1620 Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity);
1621 }
1622
1623 Buf.resize(needed_sz);
1624 ImFormatStringV(&Buf[write_off] - 1, len+1, fmt, args_copy);
1625 }
1626
append(const char * fmt,...)1627 void ImGuiTextBuffer::append(const char* fmt, ...)
1628 {
1629 va_list args;
1630 va_start(args, fmt);
1631 appendv(fmt, args);
1632 va_end(args);
1633 }
1634
1635 //-----------------------------------------------------------------------------
1636 // ImGuiSimpleColumns
1637 //-----------------------------------------------------------------------------
1638
ImGuiSimpleColumns()1639 ImGuiSimpleColumns::ImGuiSimpleColumns()
1640 {
1641 Count = 0;
1642 Spacing = Width = NextWidth = 0.0f;
1643 memset(Pos, 0, sizeof(Pos));
1644 memset(NextWidths, 0, sizeof(NextWidths));
1645 }
1646
Update(int count,float spacing,bool clear)1647 void ImGuiSimpleColumns::Update(int count, float spacing, bool clear)
1648 {
1649 IM_ASSERT(Count <= IM_ARRAYSIZE(Pos));
1650 Count = count;
1651 Width = NextWidth = 0.0f;
1652 Spacing = spacing;
1653 if (clear) memset(NextWidths, 0, sizeof(NextWidths));
1654 for (int i = 0; i < Count; i++)
1655 {
1656 if (i > 0 && NextWidths[i] > 0.0f)
1657 Width += Spacing;
1658 Pos[i] = (float)(int)Width;
1659 Width += NextWidths[i];
1660 NextWidths[i] = 0.0f;
1661 }
1662 }
1663
DeclColumns(float w0,float w1,float w2)1664 float ImGuiSimpleColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double
1665 {
1666 NextWidth = 0.0f;
1667 NextWidths[0] = ImMax(NextWidths[0], w0);
1668 NextWidths[1] = ImMax(NextWidths[1], w1);
1669 NextWidths[2] = ImMax(NextWidths[2], w2);
1670 for (int i = 0; i < 3; i++)
1671 NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f);
1672 return ImMax(Width, NextWidth);
1673 }
1674
CalcExtraSpace(float avail_w)1675 float ImGuiSimpleColumns::CalcExtraSpace(float avail_w)
1676 {
1677 return ImMax(0.0f, avail_w - Width);
1678 }
1679
1680 //-----------------------------------------------------------------------------
1681 // ImGuiListClipper
1682 //-----------------------------------------------------------------------------
1683
SetCursorPosYAndSetupDummyPrevLine(float pos_y,float line_height)1684 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
1685 {
1686 // Set cursor position and a few other things so that SetScrollHere() and Columns() can work when seeking cursor.
1687 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. Consider moving within SetCursorXXX functions?
1688 ImGui::SetCursorPosY(pos_y);
1689 ImGuiWindow* window = ImGui::GetCurrentWindow();
1690 window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHere() can properly function after the end of our clipper usage.
1691 window->DC.PrevLineHeight = (line_height - GImGui->Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list.
1692 if (window->DC.ColumnsCount > 1)
1693 window->DC.ColumnsCellMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
1694 }
1695
1696 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
1697 // Use case B: Begin() called from constructor with items_height>0
1698 // FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style.
Begin(int count,float items_height)1699 void ImGuiListClipper::Begin(int count, float items_height)
1700 {
1701 StartPosY = ImGui::GetCursorPosY();
1702 ItemsHeight = items_height;
1703 ItemsCount = count;
1704 StepNo = 0;
1705 DisplayEnd = DisplayStart = -1;
1706 if (ItemsHeight > 0.0f)
1707 {
1708 ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
1709 if (DisplayStart > 0)
1710 SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
1711 StepNo = 2;
1712 }
1713 }
1714
End()1715 void ImGuiListClipper::End()
1716 {
1717 if (ItemsCount < 0)
1718 return;
1719 // In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user.
1720 if (ItemsCount < INT_MAX)
1721 SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
1722 ItemsCount = -1;
1723 StepNo = 3;
1724 }
1725
Step()1726 bool ImGuiListClipper::Step()
1727 {
1728 if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems)
1729 {
1730 ItemsCount = -1;
1731 return false;
1732 }
1733 if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height.
1734 {
1735 DisplayStart = 0;
1736 DisplayEnd = 1;
1737 StartPosY = ImGui::GetCursorPosY();
1738 StepNo = 1;
1739 return true;
1740 }
1741 if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element.
1742 {
1743 if (ItemsCount == 1) { ItemsCount = -1; return false; }
1744 float items_height = ImGui::GetCursorPosY() - StartPosY;
1745 IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically
1746 Begin(ItemsCount-1, items_height);
1747 DisplayStart++;
1748 DisplayEnd++;
1749 StepNo = 3;
1750 return true;
1751 }
1752 if (StepNo == 2) // Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3.
1753 {
1754 IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
1755 StepNo = 3;
1756 return true;
1757 }
1758 if (StepNo == 3) // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop.
1759 End();
1760 return false;
1761 }
1762
1763 //-----------------------------------------------------------------------------
1764 // ImGuiWindow
1765 //-----------------------------------------------------------------------------
1766
ImGuiWindow(const char * name)1767 ImGuiWindow::ImGuiWindow(const char* name)
1768 {
1769 Name = ImStrdup(name);
1770 ID = ImHash(name, 0);
1771 IDStack.push_back(ID);
1772 MoveId = GetID("#MOVE");
1773
1774 Flags = 0;
1775 IndexWithinParent = 0;
1776 PosFloat = Pos = ImVec2(0.0f, 0.0f);
1777 Size = SizeFull = ImVec2(0.0f, 0.0f);
1778 SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
1779 WindowPadding = ImVec2(0.0f, 0.0f);
1780 Scroll = ImVec2(0.0f, 0.0f);
1781 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
1782 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
1783 ScrollbarX = ScrollbarY = false;
1784 ScrollbarSizes = ImVec2(0.0f, 0.0f);
1785 BorderSize = 0.0f;
1786 Active = WasActive = false;
1787 Accessed = false;
1788 Collapsed = false;
1789 SkipItems = false;
1790 BeginCount = 0;
1791 PopupId = 0;
1792 AutoFitFramesX = AutoFitFramesY = -1;
1793 AutoFitOnlyGrows = false;
1794 AutoPosLastDirection = -1;
1795 HiddenFrames = 0;
1796 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiSetCond_Always | ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing;
1797 SetWindowPosCenterWanted = false;
1798
1799 LastFrameActive = -1;
1800 ItemWidthDefault = 0.0f;
1801 FontWindowScale = 1.0f;
1802
1803 DrawList = (ImDrawList*)ImGui::MemAlloc(sizeof(ImDrawList));
1804 IM_PLACEMENT_NEW(DrawList) ImDrawList();
1805 DrawList->_OwnerName = Name;
1806 RootWindow = NULL;
1807 RootNonPopupWindow = NULL;
1808 ParentWindow = NULL;
1809
1810 FocusIdxAllCounter = FocusIdxTabCounter = -1;
1811 FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX;
1812 FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX;
1813 }
1814
~ImGuiWindow()1815 ImGuiWindow::~ImGuiWindow()
1816 {
1817 DrawList->~ImDrawList();
1818 ImGui::MemFree(DrawList);
1819 DrawList = NULL;
1820 ImGui::MemFree(Name);
1821 Name = NULL;
1822 }
1823
GetID(const char * str,const char * str_end)1824 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
1825 {
1826 ImGuiID seed = IDStack.back();
1827 ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
1828 ImGui::KeepAliveID(id);
1829 return id;
1830 }
1831
GetID(const void * ptr)1832 ImGuiID ImGuiWindow::GetID(const void* ptr)
1833 {
1834 ImGuiID seed = IDStack.back();
1835 ImGuiID id = ImHash(&ptr, sizeof(void*), seed);
1836 ImGui::KeepAliveID(id);
1837 return id;
1838 }
1839
GetIDNoKeepAlive(const char * str,const char * str_end)1840 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
1841 {
1842 ImGuiID seed = IDStack.back();
1843 return ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
1844 }
1845
1846 //-----------------------------------------------------------------------------
1847 // Internal API exposed in imgui_internal.h
1848 //-----------------------------------------------------------------------------
1849
SetCurrentWindow(ImGuiWindow * window)1850 static void SetCurrentWindow(ImGuiWindow* window)
1851 {
1852 ImGuiContext& g = *GImGui;
1853 g.CurrentWindow = window;
1854 if (window)
1855 g.FontSize = window->CalcFontSize();
1856 }
1857
GetParentWindow()1858 ImGuiWindow* ImGui::GetParentWindow()
1859 {
1860 ImGuiContext& g = *GImGui;
1861 IM_ASSERT(g.CurrentWindowStack.Size >= 2);
1862 return g.CurrentWindowStack[(unsigned int)g.CurrentWindowStack.Size - 2];
1863 }
1864
SetActiveID(ImGuiID id,ImGuiWindow * window)1865 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
1866 {
1867 ImGuiContext& g = *GImGui;
1868 g.ActiveId = id;
1869 g.ActiveIdAllowOverlap = false;
1870 g.ActiveIdIsJustActivated = true;
1871 if (id)
1872 g.ActiveIdIsAlive = true;
1873 g.ActiveIdWindow = window;
1874 }
1875
ClearActiveID()1876 void ImGui::ClearActiveID()
1877 {
1878 SetActiveID(0, NULL);
1879 }
1880
SetHoveredID(ImGuiID id)1881 void ImGui::SetHoveredID(ImGuiID id)
1882 {
1883 ImGuiContext& g = *GImGui;
1884 g.HoveredId = id;
1885 g.HoveredIdAllowOverlap = false;
1886 }
1887
KeepAliveID(ImGuiID id)1888 void ImGui::KeepAliveID(ImGuiID id)
1889 {
1890 ImGuiContext& g = *GImGui;
1891 if (g.ActiveId == id)
1892 g.ActiveIdIsAlive = true;
1893 }
1894
1895 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_offset_y)1896 void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
1897 {
1898 ImGuiWindow* window = GetCurrentWindow();
1899 if (window->SkipItems)
1900 return;
1901
1902 // Always align ourselves on pixel boundaries
1903 ImGuiContext& g = *GImGui;
1904 const float line_height = ImMax(window->DC.CurrentLineHeight, size.y);
1905 const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y);
1906 window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
1907 window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y));
1908 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
1909 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
1910
1911 //window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // Debug
1912
1913 window->DC.PrevLineHeight = line_height;
1914 window->DC.PrevLineTextBaseOffset = text_base_offset;
1915 window->DC.CurrentLineHeight = window->DC.CurrentLineTextBaseOffset = 0.0f;
1916 }
1917
ItemSize(const ImRect & bb,float text_offset_y)1918 void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
1919 {
1920 ItemSize(bb.GetSize(), text_offset_y);
1921 }
1922
1923 // Declare item bounding box for clipping and interaction.
1924 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
1925 // declares their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd().
ItemAdd(const ImRect & bb,const ImGuiID * id)1926 bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id)
1927 {
1928 ImGuiWindow* window = GetCurrentWindow();
1929 window->DC.LastItemId = id ? *id : 0;
1930 window->DC.LastItemRect = bb;
1931 window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = false;
1932 if (IsClippedEx(bb, id, false))
1933 return false;
1934
1935 // This is a sensible default, but widgets are free to override it after calling ItemAdd()
1936 ImGuiContext& g = *GImGui;
1937 if (IsMouseHoveringRect(bb.Min, bb.Max))
1938 {
1939 // Matching the behavior of IsHovered() but allow if ActiveId==window->MoveID (we clicked on the window background)
1940 // So that clicking on items with no active id such as Text() still returns true with IsItemHovered()
1941 window->DC.LastItemHoveredRect = true;
1942 if (g.HoveredRootWindow == window->RootWindow)
1943 if (g.ActiveId == 0 || (id && g.ActiveId == *id) || g.ActiveIdAllowOverlap || (g.ActiveId == window->MoveId))
1944 if (IsWindowContentHoverable(window))
1945 window->DC.LastItemHoveredAndUsable = true;
1946 }
1947
1948 return true;
1949 }
1950
IsClippedEx(const ImRect & bb,const ImGuiID * id,bool clip_even_when_logged)1951 bool ImGui::IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged)
1952 {
1953 ImGuiContext& g = *GImGui;
1954 ImGuiWindow* window = GetCurrentWindowRead();
1955
1956 if (!bb.Overlaps(window->ClipRect))
1957 if (!id || *id != GImGui->ActiveId)
1958 if (clip_even_when_logged || !g.LogEnabled)
1959 return true;
1960 return false;
1961 }
1962
1963 // NB: This is an internal helper. The user-facing IsItemHovered() is using data emitted from ItemAdd(), with a slightly different logic.
IsHovered(const ImRect & bb,ImGuiID id,bool flatten_childs)1964 bool ImGui::IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs)
1965 {
1966 ImGuiContext& g = *GImGui;
1967 if (g.HoveredId == 0 || g.HoveredId == id || g.HoveredIdAllowOverlap)
1968 {
1969 ImGuiWindow* window = GetCurrentWindowRead();
1970 if (g.HoveredWindow == window || (flatten_childs && g.HoveredRootWindow == window->RootWindow))
1971 if ((g.ActiveId == 0 || g.ActiveId == id || g.ActiveIdAllowOverlap) && IsMouseHoveringRect(bb.Min, bb.Max))
1972 if (IsWindowContentHoverable(g.HoveredRootWindow))
1973 return true;
1974 }
1975 return false;
1976 }
1977
FocusableItemRegister(ImGuiWindow * window,bool is_active,bool tab_stop)1978 bool ImGui::FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_stop)
1979 {
1980 ImGuiContext& g = *GImGui;
1981
1982 const bool allow_keyboard_focus = window->DC.AllowKeyboardFocus;
1983 window->FocusIdxAllCounter++;
1984 if (allow_keyboard_focus)
1985 window->FocusIdxTabCounter++;
1986
1987 // Process keyboard input at this point: TAB, Shift-TAB switch focus
1988 // We can always TAB out of a widget that doesn't allow tabbing in.
1989 if (tab_stop && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && is_active && IsKeyPressedMap(ImGuiKey_Tab))
1990 {
1991 // Modulo on index will be applied at the end of frame once we've got the total counter of items.
1992 window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1);
1993 }
1994
1995 if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
1996 return true;
1997
1998 if (allow_keyboard_focus)
1999 if (window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent)
2000 return true;
2001
2002 return false;
2003 }
2004
FocusableItemUnregister(ImGuiWindow * window)2005 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
2006 {
2007 window->FocusIdxAllCounter--;
2008 window->FocusIdxTabCounter--;
2009 }
2010
CalcItemSize(ImVec2 size,float default_x,float default_y)2011 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y)
2012 {
2013 ImGuiContext& g = *GImGui;
2014 ImVec2 content_max;
2015 if (size.x < 0.0f || size.y < 0.0f)
2016 content_max = g.CurrentWindow->Pos + GetContentRegionMax();
2017 if (size.x <= 0.0f)
2018 size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x;
2019 if (size.y <= 0.0f)
2020 size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y;
2021 return size;
2022 }
2023
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)2024 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
2025 {
2026 if (wrap_pos_x < 0.0f)
2027 return 0.0f;
2028
2029 ImGuiWindow* window = GetCurrentWindowRead();
2030 if (wrap_pos_x == 0.0f)
2031 wrap_pos_x = GetContentRegionMax().x + window->Pos.x;
2032 else if (wrap_pos_x > 0.0f)
2033 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
2034
2035 return ImMax(wrap_pos_x - pos.x, 1.0f);
2036 }
2037
2038 //-----------------------------------------------------------------------------
2039
MemAlloc(size_t sz)2040 void* ImGui::MemAlloc(size_t sz)
2041 {
2042 GImGui->IO.MetricsAllocs++;
2043 return GImGui->IO.MemAllocFn(sz);
2044 }
2045
MemFree(void * ptr)2046 void ImGui::MemFree(void* ptr)
2047 {
2048 if (ptr) GImGui->IO.MetricsAllocs--;
2049 return GImGui->IO.MemFreeFn(ptr);
2050 }
2051
GetClipboardText()2052 const char* ImGui::GetClipboardText()
2053 {
2054 return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : "";
2055 }
2056
SetClipboardText(const char * text)2057 void ImGui::SetClipboardText(const char* text)
2058 {
2059 if (GImGui->IO.SetClipboardTextFn)
2060 GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text);
2061 }
2062
GetVersion()2063 const char* ImGui::GetVersion()
2064 {
2065 return IMGUI_VERSION;
2066 }
2067
2068 // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
2069 // Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module
GetCurrentContext()2070 ImGuiContext* ImGui::GetCurrentContext()
2071 {
2072 return GImGui;
2073 }
2074
SetCurrentContext(ImGuiContext * ctx)2075 void ImGui::SetCurrentContext(ImGuiContext* ctx)
2076 {
2077 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
2078 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
2079 #else
2080 GImGui = ctx;
2081 #endif
2082 }
2083
CreateContext(void * (* malloc_fn)(size_t),void (* free_fn)(void *))2084 ImGuiContext* ImGui::CreateContext(void* (*malloc_fn)(size_t), void (*free_fn)(void*))
2085 {
2086 if (!malloc_fn) malloc_fn = malloc;
2087 ImGuiContext* ctx = (ImGuiContext*)malloc_fn(sizeof(ImGuiContext));
2088 IM_PLACEMENT_NEW(ctx) ImGuiContext();
2089 ctx->IO.MemAllocFn = malloc_fn;
2090 ctx->IO.MemFreeFn = free_fn ? free_fn : free;
2091 return ctx;
2092 }
2093
DestroyContext(ImGuiContext * ctx)2094 void ImGui::DestroyContext(ImGuiContext* ctx)
2095 {
2096 void (*free_fn)(void*) = ctx->IO.MemFreeFn;
2097 ctx->~ImGuiContext();
2098 free_fn(ctx);
2099 if (GImGui == ctx)
2100 SetCurrentContext(NULL);
2101 }
2102
GetIO()2103 ImGuiIO& ImGui::GetIO()
2104 {
2105 return GImGui->IO;
2106 }
2107
GetStyle()2108 ImGuiStyle& ImGui::GetStyle()
2109 {
2110 return GImGui->Style;
2111 }
2112
2113 // Same value as passed to your RenderDrawListsFn() function. valid after Render() and until the next call to NewFrame()
GetDrawData()2114 ImDrawData* ImGui::GetDrawData()
2115 {
2116 return GImGui->RenderDrawData.Valid ? &GImGui->RenderDrawData : NULL;
2117 }
2118
GetTime()2119 float ImGui::GetTime()
2120 {
2121 return GImGui->Time;
2122 }
2123
GetFrameCount()2124 int ImGui::GetFrameCount()
2125 {
2126 return GImGui->FrameCount;
2127 }
2128
NewFrame()2129 void ImGui::NewFrame()
2130 {
2131 ImGuiContext& g = *GImGui;
2132
2133 // Check user data
2134 IM_ASSERT(g.IO.DeltaTime >= 0.0f); // Need a positive DeltaTime (zero is tolerated but will cause some timing issues)
2135 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f);
2136 IM_ASSERT(g.IO.Fonts->Fonts.Size > 0); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
2137 IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
2138 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f); // Invalid style setting
2139
2140 if (!g.Initialized)
2141 {
2142 // Initialize on first frame
2143 g.LogClipboard = (ImGuiTextBuffer*)ImGui::MemAlloc(sizeof(ImGuiTextBuffer));
2144 IM_PLACEMENT_NEW(g.LogClipboard) ImGuiTextBuffer();
2145
2146 IM_ASSERT(g.Settings.empty());
2147 LoadIniSettingsFromDisk(g.IO.IniFilename);
2148 g.Initialized = true;
2149 }
2150
2151 SetCurrentFont(GetDefaultFont());
2152 IM_ASSERT(g.Font->IsLoaded());
2153
2154 g.Time += g.IO.DeltaTime;
2155 g.FrameCount += 1;
2156 g.Tooltip[0] = '\0';
2157 g.OverlayDrawList.Clear();
2158 g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID);
2159 g.OverlayDrawList.PushClipRectFullScreen();
2160
2161 // Mark rendering data as invalid to prevent user who may have a handle on it to use it
2162 g.RenderDrawData.Valid = false;
2163 g.RenderDrawData.CmdLists = NULL;
2164 g.RenderDrawData.CmdListsCount = g.RenderDrawData.TotalVtxCount = g.RenderDrawData.TotalIdxCount = 0;
2165
2166 // Update inputs state
2167 if (g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0)
2168 g.IO.MousePos = ImVec2(-9999.0f, -9999.0f);
2169 if ((g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0) || (g.IO.MousePosPrev.x < 0 && g.IO.MousePosPrev.y < 0)) // if mouse just appeared or disappeared (negative coordinate) we cancel out movement in MouseDelta
2170 g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
2171 else
2172 g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
2173 g.IO.MousePosPrev = g.IO.MousePos;
2174 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
2175 {
2176 g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
2177 g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
2178 g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
2179 g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f;
2180 g.IO.MouseDoubleClicked[i] = false;
2181 if (g.IO.MouseClicked[i])
2182 {
2183 if (g.Time - g.IO.MouseClickedTime[i] < g.IO.MouseDoubleClickTime)
2184 {
2185 if (ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
2186 g.IO.MouseDoubleClicked[i] = true;
2187 g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click
2188 }
2189 else
2190 {
2191 g.IO.MouseClickedTime[i] = g.Time;
2192 }
2193 g.IO.MouseClickedPos[i] = g.IO.MousePos;
2194 g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
2195 }
2196 else if (g.IO.MouseDown[i])
2197 {
2198 g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]));
2199 }
2200 }
2201 memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
2202 for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
2203 g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f;
2204
2205 // Calculate frame-rate for the user, as a purely luxurious feature
2206 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
2207 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
2208 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
2209 g.IO.Framerate = 1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame));
2210
2211 // Clear reference to active widget if the widget isn't alive anymore
2212 g.HoveredIdPreviousFrame = g.HoveredId;
2213 g.HoveredId = 0;
2214 g.HoveredIdAllowOverlap = false;
2215 if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
2216 ClearActiveID();
2217 g.ActiveIdPreviousFrame = g.ActiveId;
2218 g.ActiveIdIsAlive = false;
2219 g.ActiveIdIsJustActivated = false;
2220
2221 // Handle user moving window (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows.
2222 if (g.MovedWindowMoveId && g.MovedWindowMoveId == g.ActiveId)
2223 {
2224 KeepAliveID(g.MovedWindowMoveId);
2225 IM_ASSERT(g.MovedWindow && g.MovedWindow->RootWindow);
2226 IM_ASSERT(g.MovedWindow->RootWindow->MoveId == g.MovedWindowMoveId);
2227 if (g.IO.MouseDown[0])
2228 {
2229 if (!(g.MovedWindow->Flags & ImGuiWindowFlags_NoMove))
2230 {
2231 g.MovedWindow->PosFloat += g.IO.MouseDelta;
2232 if (!(g.MovedWindow->Flags & ImGuiWindowFlags_NoSavedSettings) && (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f))
2233 MarkIniSettingsDirty();
2234 }
2235 FocusWindow(g.MovedWindow);
2236 }
2237 else
2238 {
2239 ClearActiveID();
2240 g.MovedWindow = NULL;
2241 g.MovedWindowMoveId = 0;
2242 }
2243 }
2244 else
2245 {
2246 g.MovedWindow = NULL;
2247 g.MovedWindowMoveId = 0;
2248 }
2249
2250 // Delay saving settings so we don't spam disk too much
2251 if (g.SettingsDirtyTimer > 0.0f)
2252 {
2253 g.SettingsDirtyTimer -= g.IO.DeltaTime;
2254 if (g.SettingsDirtyTimer <= 0.0f)
2255 SaveIniSettingsToDisk(g.IO.IniFilename);
2256 }
2257
2258 // Find the window we are hovering. Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow
2259 g.HoveredWindow = g.MovedWindow ? g.MovedWindow : FindHoveredWindow(g.IO.MousePos, false);
2260 if (g.HoveredWindow && (g.HoveredWindow->Flags & ImGuiWindowFlags_ChildWindow))
2261 g.HoveredRootWindow = g.HoveredWindow->RootWindow;
2262 else
2263 g.HoveredRootWindow = g.MovedWindow ? g.MovedWindow->RootWindow : FindHoveredWindow(g.IO.MousePos, true);
2264
2265 if (ImGuiWindow* modal_window = GetFrontMostModalRootWindow())
2266 {
2267 g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f);
2268 ImGuiWindow* window = g.HoveredRootWindow;
2269 while (window && window != modal_window)
2270 window = window->ParentWindow;
2271 if (!window)
2272 g.HoveredRootWindow = g.HoveredWindow = NULL;
2273 }
2274 else
2275 {
2276 g.ModalWindowDarkeningRatio = 0.0f;
2277 }
2278
2279 // Are we using inputs? Tell user so they can capture/discard the inputs away from the rest of their application.
2280 // When clicking outside of a window we assume the click is owned by the application and won't request capture. We need to track click ownership.
2281 int mouse_earliest_button_down = -1;
2282 bool mouse_any_down = false;
2283 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
2284 {
2285 if (g.IO.MouseClicked[i])
2286 g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
2287 mouse_any_down |= g.IO.MouseDown[i];
2288 if (g.IO.MouseDown[i])
2289 if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[mouse_earliest_button_down] > g.IO.MouseClickedTime[i])
2290 mouse_earliest_button_down = i;
2291 }
2292 bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
2293 if (g.CaptureMouseNextFrame != -1)
2294 g.IO.WantCaptureMouse = (g.CaptureMouseNextFrame != 0);
2295 else
2296 g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.ActiveId != 0) || (!g.OpenPopupStack.empty());
2297 g.IO.WantCaptureKeyboard = (g.CaptureKeyboardNextFrame != -1) ? (g.CaptureKeyboardNextFrame != 0) : (g.ActiveId != 0);
2298 g.IO.WantTextInput = (g.ActiveId != 0 && g.InputTextState.Id == g.ActiveId);
2299 g.MouseCursor = ImGuiMouseCursor_Arrow;
2300 g.CaptureMouseNextFrame = g.CaptureKeyboardNextFrame = -1;
2301 g.OsImePosRequest = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
2302
2303 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
2304 if (!mouse_avail_to_imgui)
2305 g.HoveredWindow = g.HoveredRootWindow = NULL;
2306
2307 // Scale & Scrolling
2308 if (g.HoveredWindow && g.IO.MouseWheel != 0.0f && !g.HoveredWindow->Collapsed)
2309 {
2310 ImGuiWindow* window = g.HoveredWindow;
2311 if (g.IO.KeyCtrl)
2312 {
2313 if (g.IO.FontAllowUserScaling)
2314 {
2315 // Zoom / Scale window
2316 float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
2317 float scale = new_font_scale / window->FontWindowScale;
2318 window->FontWindowScale = new_font_scale;
2319
2320 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
2321 window->Pos += offset;
2322 window->PosFloat += offset;
2323 window->Size *= scale;
2324 window->SizeFull *= scale;
2325 }
2326 }
2327 else if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse))
2328 {
2329 // Scroll
2330 const int scroll_lines = (window->Flags & ImGuiWindowFlags_ComboBox) ? 3 : 5;
2331 SetWindowScrollY(window, window->Scroll.y - g.IO.MouseWheel * window->CalcFontSize() * scroll_lines);
2332 }
2333 }
2334
2335 // Pressing TAB activate widget focus
2336 // NB: Don't discard FocusedWindow if it isn't active, so that a window that go on/off programatically won't lose its keyboard focus.
2337 if (g.ActiveId == 0 && g.FocusedWindow != NULL && g.FocusedWindow->Active && IsKeyPressedMap(ImGuiKey_Tab, false))
2338 g.FocusedWindow->FocusIdxTabRequestNext = 0;
2339
2340 // Mark all windows as not visible
2341 for (int i = 0; i != g.Windows.Size; i++)
2342 {
2343 ImGuiWindow* window = g.Windows[i];
2344 window->WasActive = window->Active;
2345 window->Active = false;
2346 window->Accessed = false;
2347 }
2348
2349 // Closing the focused window restore focus to the first active root window in descending z-order
2350 if (g.FocusedWindow && !g.FocusedWindow->WasActive)
2351 for (int i = g.Windows.Size-1; i >= 0; i--)
2352 if (g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow))
2353 {
2354 FocusWindow(g.Windows[i]);
2355 break;
2356 }
2357
2358 // No window should be open at the beginning of the frame.
2359 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
2360 g.CurrentWindowStack.resize(0);
2361 g.CurrentPopupStack.resize(0);
2362 CloseInactivePopups();
2363
2364 // Create implicit window - we will only render it if the user has added something to it.
2365 ImGui::SetNextWindowSize(ImVec2(400,400), ImGuiSetCond_FirstUseEver);
2366 ImGui::Begin("Debug");
2367 }
2368
2369 // NB: behavior of ImGui after Shutdown() is not tested/guaranteed at the moment. This function is merely here to free heap allocations.
Shutdown()2370 void ImGui::Shutdown()
2371 {
2372 ImGuiContext& g = *GImGui;
2373
2374 // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame)
2375 if (g.IO.Fonts) // Testing for NULL to allow user to NULLify in case of running Shutdown() on multiple contexts. Bit hacky.
2376 g.IO.Fonts->Clear();
2377
2378 // Cleanup of other data are conditional on actually having used ImGui.
2379 if (!g.Initialized)
2380 return;
2381
2382 SaveIniSettingsToDisk(g.IO.IniFilename);
2383
2384 for (int i = 0; i < g.Windows.Size; i++)
2385 {
2386 g.Windows[i]->~ImGuiWindow();
2387 ImGui::MemFree(g.Windows[i]);
2388 }
2389 g.Windows.clear();
2390 g.WindowsSortBuffer.clear();
2391 g.CurrentWindow = NULL;
2392 g.CurrentWindowStack.clear();
2393 g.FocusedWindow = NULL;
2394 g.HoveredWindow = NULL;
2395 g.HoveredRootWindow = NULL;
2396 g.ActiveIdWindow = NULL;
2397 g.MovedWindow = NULL;
2398 for (int i = 0; i < g.Settings.Size; i++)
2399 ImGui::MemFree(g.Settings[i].Name);
2400 g.Settings.clear();
2401 g.ColorModifiers.clear();
2402 g.StyleModifiers.clear();
2403 g.FontStack.clear();
2404 g.OpenPopupStack.clear();
2405 g.CurrentPopupStack.clear();
2406 g.SetNextWindowSizeConstraintCallback = NULL;
2407 g.SetNextWindowSizeConstraintCallbackUserData = NULL;
2408 for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2409 g.RenderDrawLists[i].clear();
2410 g.OverlayDrawList.ClearFreeMemory();
2411 g.ColorEditModeStorage.Clear();
2412 if (g.PrivateClipboard)
2413 {
2414 ImGui::MemFree(g.PrivateClipboard);
2415 g.PrivateClipboard = NULL;
2416 }
2417 g.InputTextState.Text.clear();
2418 g.InputTextState.InitialText.clear();
2419 g.InputTextState.TempTextBuffer.clear();
2420
2421 if (g.LogFile && g.LogFile != stdout)
2422 {
2423 fclose(g.LogFile);
2424 g.LogFile = NULL;
2425 }
2426 if (g.LogClipboard)
2427 {
2428 g.LogClipboard->~ImGuiTextBuffer();
2429 ImGui::MemFree(g.LogClipboard);
2430 }
2431
2432 g.Initialized = false;
2433 }
2434
FindWindowSettings(const char * name)2435 static ImGuiIniData* FindWindowSettings(const char* name)
2436 {
2437 ImGuiContext& g = *GImGui;
2438 ImGuiID id = ImHash(name, 0);
2439 for (int i = 0; i != g.Settings.Size; i++)
2440 {
2441 ImGuiIniData* ini = &g.Settings[i];
2442 if (ini->Id == id)
2443 return ini;
2444 }
2445 return NULL;
2446 }
2447
AddWindowSettings(const char * name)2448 static ImGuiIniData* AddWindowSettings(const char* name)
2449 {
2450 GImGui->Settings.resize(GImGui->Settings.Size + 1);
2451 ImGuiIniData* ini = &GImGui->Settings.back();
2452 ini->Name = ImStrdup(name);
2453 ini->Id = ImHash(name, 0);
2454 ini->Collapsed = false;
2455 ini->Pos = ImVec2(FLT_MAX,FLT_MAX);
2456 ini->Size = ImVec2(0,0);
2457 return ini;
2458 }
2459
2460 // Zero-tolerance, poor-man .ini parsing
2461 // FIXME: Write something less rubbish
LoadIniSettingsFromDisk(const char * ini_filename)2462 static void LoadIniSettingsFromDisk(const char* ini_filename)
2463 {
2464 ImGuiContext& g = *GImGui;
2465 if (!ini_filename)
2466 return;
2467
2468 int file_size;
2469 char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_size, 1);
2470 if (!file_data)
2471 return;
2472
2473 ImGuiIniData* settings = NULL;
2474 const char* buf_end = file_data + file_size;
2475 for (const char* line_start = file_data; line_start < buf_end; )
2476 {
2477 const char* line_end = line_start;
2478 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
2479 line_end++;
2480
2481 if (line_start[0] == '[' && line_end > line_start && line_end[-1] == ']')
2482 {
2483 char name[64];
2484 ImFormatString(name, IM_ARRAYSIZE(name), "%.*s", (int)(line_end-line_start-2), line_start+1);
2485 settings = FindWindowSettings(name);
2486 if (!settings)
2487 settings = AddWindowSettings(name);
2488 }
2489 else if (settings)
2490 {
2491 float x, y;
2492 int i;
2493 if (sscanf(line_start, "Pos=%f,%f", &x, &y) == 2)
2494 settings->Pos = ImVec2(x, y);
2495 else if (sscanf(line_start, "Size=%f,%f", &x, &y) == 2)
2496 settings->Size = ImMax(ImVec2(x, y), g.Style.WindowMinSize);
2497 else if (sscanf(line_start, "Collapsed=%d", &i) == 1)
2498 settings->Collapsed = (i != 0);
2499 }
2500
2501 line_start = line_end+1;
2502 }
2503
2504 ImGui::MemFree(file_data);
2505 }
2506
SaveIniSettingsToDisk(const char * ini_filename)2507 static void SaveIniSettingsToDisk(const char* ini_filename)
2508 {
2509 ImGuiContext& g = *GImGui;
2510 g.SettingsDirtyTimer = 0.0f;
2511 if (!ini_filename)
2512 return;
2513
2514 // Gather data from windows that were active during this session
2515 for (int i = 0; i != g.Windows.Size; i++)
2516 {
2517 ImGuiWindow* window = g.Windows[i];
2518 if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
2519 continue;
2520 ImGuiIniData* settings = FindWindowSettings(window->Name);
2521 settings->Pos = window->Pos;
2522 settings->Size = window->SizeFull;
2523 settings->Collapsed = window->Collapsed;
2524 }
2525
2526 // Write .ini file
2527 // If a window wasn't opened in this session we preserve its settings
2528 FILE* f = ImFileOpen(ini_filename, "wt");
2529 if (!f)
2530 return;
2531 for (int i = 0; i != g.Settings.Size; i++)
2532 {
2533 const ImGuiIniData* settings = &g.Settings[i];
2534 if (settings->Pos.x == FLT_MAX)
2535 continue;
2536 const char* name = settings->Name;
2537 if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
2538 name = p;
2539 fprintf(f, "[%s]\n", name);
2540 fprintf(f, "Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
2541 fprintf(f, "Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
2542 fprintf(f, "Collapsed=%d\n", settings->Collapsed);
2543 fprintf(f, "\n");
2544 }
2545
2546 fclose(f);
2547 }
2548
MarkIniSettingsDirty()2549 static void MarkIniSettingsDirty()
2550 {
2551 ImGuiContext& g = *GImGui;
2552 if (g.SettingsDirtyTimer <= 0.0f)
2553 g.SettingsDirtyTimer = g.IO.IniSavingRate;
2554 }
2555
2556 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)2557 static int ChildWindowComparer(const void* lhs, const void* rhs)
2558 {
2559 const ImGuiWindow* a = *(const ImGuiWindow**)lhs;
2560 const ImGuiWindow* b = *(const ImGuiWindow**)rhs;
2561 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
2562 return d;
2563 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
2564 return d;
2565 if (int d = (a->Flags & ImGuiWindowFlags_ComboBox) - (b->Flags & ImGuiWindowFlags_ComboBox))
2566 return d;
2567 return (a->IndexWithinParent - b->IndexWithinParent);
2568 }
2569
AddWindowToSortedBuffer(ImVector<ImGuiWindow * > & out_sorted_windows,ImGuiWindow * window)2570 static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>& out_sorted_windows, ImGuiWindow* window)
2571 {
2572 out_sorted_windows.push_back(window);
2573 if (window->Active)
2574 {
2575 int count = window->DC.ChildWindows.Size;
2576 if (count > 1)
2577 qsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
2578 for (int i = 0; i < count; i++)
2579 {
2580 ImGuiWindow* child = window->DC.ChildWindows[i];
2581 if (child->Active)
2582 AddWindowToSortedBuffer(out_sorted_windows, child);
2583 }
2584 }
2585 }
2586
AddDrawListToRenderList(ImVector<ImDrawList * > & out_render_list,ImDrawList * draw_list)2587 static void AddDrawListToRenderList(ImVector<ImDrawList*>& out_render_list, ImDrawList* draw_list)
2588 {
2589 if (draw_list->CmdBuffer.empty())
2590 return;
2591
2592 // Remove trailing command if unused
2593 ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
2594 if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
2595 {
2596 draw_list->CmdBuffer.pop_back();
2597 if (draw_list->CmdBuffer.empty())
2598 return;
2599 }
2600
2601 // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
2602 IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
2603 IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
2604 IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
2605
2606 // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = 2 bytes = 64K vertices)
2607 // If this assert triggers because you are drawing lots of stuff manually, A) workaround by calling BeginChild()/EndChild() to put your draw commands in multiple draw lists, B) #define ImDrawIdx to a 'unsigned int' in imconfig.h and render accordingly.
2608 IM_ASSERT((int64_t)draw_list->_VtxCurrentIdx <= ((int64_t)1L << (sizeof(ImDrawIdx)*8))); // Too many vertices in same ImDrawList. See comment above.
2609
2610 out_render_list.push_back(draw_list);
2611 GImGui->IO.MetricsRenderVertices += draw_list->VtxBuffer.Size;
2612 GImGui->IO.MetricsRenderIndices += draw_list->IdxBuffer.Size;
2613 }
2614
AddWindowToRenderList(ImVector<ImDrawList * > & out_render_list,ImGuiWindow * window)2615 static void AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiWindow* window)
2616 {
2617 AddDrawListToRenderList(out_render_list, window->DrawList);
2618 for (int i = 0; i < window->DC.ChildWindows.Size; i++)
2619 {
2620 ImGuiWindow* child = window->DC.ChildWindows[i];
2621 if (!child->Active) // clipped children may have been marked not active
2622 continue;
2623 if ((child->Flags & ImGuiWindowFlags_Popup) && child->HiddenFrames > 0)
2624 continue;
2625 AddWindowToRenderList(out_render_list, child);
2626 }
2627 }
2628
2629 // When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result.
PushClipRect(const ImVec2 & clip_rect_min,const ImVec2 & clip_rect_max,bool intersect_with_current_clip_rect)2630 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
2631 {
2632 ImGuiWindow* window = GetCurrentWindow();
2633 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
2634 window->ClipRect = window->DrawList->_ClipRectStack.back();
2635 }
2636
PopClipRect()2637 void ImGui::PopClipRect()
2638 {
2639 ImGuiWindow* window = GetCurrentWindow();
2640 window->DrawList->PopClipRect();
2641 window->ClipRect = window->DrawList->_ClipRectStack.back();
2642 }
2643
2644 // This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal.
EndFrame()2645 void ImGui::EndFrame()
2646 {
2647 ImGuiContext& g = *GImGui;
2648 IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame()
2649 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // ImGui::EndFrame() called multiple times, or forgot to call ImGui::NewFrame() again
2650
2651 // Render tooltip
2652 if (g.Tooltip[0])
2653 {
2654 ImGui::BeginTooltip();
2655 ImGui::TextUnformatted(g.Tooltip);
2656 ImGui::EndTooltip();
2657 }
2658
2659 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
2660 if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.OsImePosRequest - g.OsImePosSet) > 0.0001f)
2661 {
2662 g.IO.ImeSetInputScreenPosFn((int)g.OsImePosRequest.x, (int)g.OsImePosRequest.y);
2663 g.OsImePosSet = g.OsImePosRequest;
2664 }
2665
2666 // Hide implicit "Debug" window if it hasn't been used
2667 IM_ASSERT(g.CurrentWindowStack.Size == 1); // Mismatched Begin()/End() calls
2668 if (g.CurrentWindow && !g.CurrentWindow->Accessed)
2669 g.CurrentWindow->Active = false;
2670 ImGui::End();
2671
2672 // Click to focus window and start moving (after we're done with all our widgets)
2673 if (g.ActiveId == 0 && g.HoveredId == 0 && g.IO.MouseClicked[0])
2674 {
2675 if (!(g.FocusedWindow && !g.FocusedWindow->WasActive && g.FocusedWindow->Active)) // Unless we just made a popup appear
2676 {
2677 if (g.HoveredRootWindow != NULL)
2678 {
2679 FocusWindow(g.HoveredWindow);
2680 if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove))
2681 {
2682 g.MovedWindow = g.HoveredWindow;
2683 g.MovedWindowMoveId = g.HoveredRootWindow->MoveId;
2684 SetActiveID(g.MovedWindowMoveId, g.HoveredRootWindow);
2685 }
2686 }
2687 else if (g.FocusedWindow != NULL && GetFrontMostModalRootWindow() == NULL)
2688 {
2689 // Clicking on void disable focus
2690 FocusWindow(NULL);
2691 }
2692 }
2693 }
2694
2695 // Sort the window list so that all child windows are after their parent
2696 // We cannot do that on FocusWindow() because childs may not exist yet
2697 g.WindowsSortBuffer.resize(0);
2698 g.WindowsSortBuffer.reserve(g.Windows.Size);
2699 for (int i = 0; i != g.Windows.Size; i++)
2700 {
2701 ImGuiWindow* window = g.Windows[i];
2702 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
2703 continue;
2704 AddWindowToSortedBuffer(g.WindowsSortBuffer, window);
2705 }
2706 IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong
2707 g.Windows.swap(g.WindowsSortBuffer);
2708
2709 // Clear Input data for next frame
2710 g.IO.MouseWheel = 0.0f;
2711 memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
2712
2713 g.FrameCountEnded = g.FrameCount;
2714 }
2715
Render()2716 void ImGui::Render()
2717 {
2718 ImGuiContext& g = *GImGui;
2719 IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame()
2720
2721 if (g.FrameCountEnded != g.FrameCount)
2722 ImGui::EndFrame();
2723 g.FrameCountRendered = g.FrameCount;
2724
2725 // Skip render altogether if alpha is 0.0
2726 // Note that vertex buffers have been created and are wasted, so it is best practice that you don't create windows in the first place, or consistently respond to Begin() returning false.
2727 if (g.Style.Alpha > 0.0f)
2728 {
2729 // Gather windows to render
2730 g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0;
2731 for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2732 g.RenderDrawLists[i].resize(0);
2733 for (int i = 0; i != g.Windows.Size; i++)
2734 {
2735 ImGuiWindow* window = g.Windows[i];
2736 if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0)
2737 {
2738 // FIXME: Generalize this with a proper layering system so e.g. user can draw in specific layers, below text, ..
2739 g.IO.MetricsActiveWindows++;
2740 if (window->Flags & ImGuiWindowFlags_Popup)
2741 AddWindowToRenderList(g.RenderDrawLists[1], window);
2742 else if (window->Flags & ImGuiWindowFlags_Tooltip)
2743 AddWindowToRenderList(g.RenderDrawLists[2], window);
2744 else
2745 AddWindowToRenderList(g.RenderDrawLists[0], window);
2746 }
2747 }
2748
2749 // Flatten layers
2750 int n = g.RenderDrawLists[0].Size;
2751 int flattened_size = n;
2752 for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2753 flattened_size += g.RenderDrawLists[i].Size;
2754 g.RenderDrawLists[0].resize(flattened_size);
2755 for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2756 {
2757 ImVector<ImDrawList*>& layer = g.RenderDrawLists[i];
2758 if (layer.empty())
2759 continue;
2760 memcpy(&g.RenderDrawLists[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
2761 n += layer.Size;
2762 }
2763
2764 // Draw software mouse cursor if requested
2765 if (g.IO.MouseDrawCursor)
2766 {
2767 const ImGuiMouseCursorData& cursor_data = g.MouseCursorData[g.MouseCursor];
2768 const ImVec2 pos = g.IO.MousePos - cursor_data.HotOffset;
2769 const ImVec2 size = cursor_data.Size;
2770 const ImTextureID tex_id = g.IO.Fonts->TexID;
2771 g.OverlayDrawList.PushTextureID(tex_id);
2772 g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(1,0), pos+ImVec2(1,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,48)); // Shadow
2773 g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(2,0), pos+ImVec2(2,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,48)); // Shadow
2774 g.OverlayDrawList.AddImage(tex_id, pos, pos + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,255)); // Black border
2775 g.OverlayDrawList.AddImage(tex_id, pos, pos + size, cursor_data.TexUvMin[0], cursor_data.TexUvMax[0], IM_COL32(255,255,255,255)); // White fill
2776 g.OverlayDrawList.PopTextureID();
2777 }
2778 if (!g.OverlayDrawList.VtxBuffer.empty())
2779 AddDrawListToRenderList(g.RenderDrawLists[0], &g.OverlayDrawList);
2780
2781 // Setup draw data
2782 g.RenderDrawData.Valid = true;
2783 g.RenderDrawData.CmdLists = (g.RenderDrawLists[0].Size > 0) ? &g.RenderDrawLists[0][0] : NULL;
2784 g.RenderDrawData.CmdListsCount = g.RenderDrawLists[0].Size;
2785 g.RenderDrawData.TotalVtxCount = g.IO.MetricsRenderVertices;
2786 g.RenderDrawData.TotalIdxCount = g.IO.MetricsRenderIndices;
2787
2788 // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData()
2789 if (g.RenderDrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
2790 g.IO.RenderDrawListsFn(&g.RenderDrawData);
2791 }
2792 }
2793
FindRenderedTextEnd(const char * text,const char * text_end)2794 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2795 {
2796 const char* text_display_end = text;
2797 if (!text_end)
2798 text_end = (const char*)-1;
2799
2800 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2801 text_display_end++;
2802 return text_display_end;
2803 }
2804
2805 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)2806 void ImGui::LogText(const char* fmt, ...)
2807 {
2808 ImGuiContext& g = *GImGui;
2809 if (!g.LogEnabled)
2810 return;
2811
2812 va_list args;
2813 va_start(args, fmt);
2814 if (g.LogFile)
2815 {
2816 vfprintf(g.LogFile, fmt, args);
2817 }
2818 else
2819 {
2820 g.LogClipboard->appendv(fmt, args);
2821 }
2822 va_end(args);
2823 }
2824
2825 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
2826 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 & ref_pos,const char * text,const char * text_end)2827 static void LogRenderedText(const ImVec2& ref_pos, const char* text, const char* text_end)
2828 {
2829 ImGuiContext& g = *GImGui;
2830 ImGuiWindow* window = ImGui::GetCurrentWindowRead();
2831
2832 if (!text_end)
2833 text_end = ImGui::FindRenderedTextEnd(text, text_end);
2834
2835 const bool log_new_line = ref_pos.y > window->DC.LogLinePosY+1;
2836 window->DC.LogLinePosY = ref_pos.y;
2837
2838 const char* text_remaining = text;
2839 if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth
2840 g.LogStartDepth = window->DC.TreeDepth;
2841 const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth);
2842 for (;;)
2843 {
2844 // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
2845 const char* line_end = text_remaining;
2846 while (line_end < text_end)
2847 if (*line_end == '\n')
2848 break;
2849 else
2850 line_end++;
2851 if (line_end >= text_end)
2852 line_end = NULL;
2853
2854 const bool is_first_line = (text == text_remaining);
2855 bool is_last_line = false;
2856 if (line_end == NULL)
2857 {
2858 is_last_line = true;
2859 line_end = text_end;
2860 }
2861 if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0))
2862 {
2863 const int char_count = (int)(line_end - text_remaining);
2864 if (log_new_line || !is_first_line)
2865 ImGui::LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining);
2866 else
2867 ImGui::LogText(" %.*s", char_count, text_remaining);
2868 }
2869
2870 if (is_last_line)
2871 break;
2872 text_remaining = line_end + 1;
2873 }
2874 }
2875
2876 // Internal ImGui functions to render text
2877 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2878 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2879 {
2880 ImGuiContext& g = *GImGui;
2881 ImGuiWindow* window = GetCurrentWindow();
2882
2883 // Hide anything after a '##' string
2884 const char* text_display_end;
2885 if (hide_text_after_hash)
2886 {
2887 text_display_end = FindRenderedTextEnd(text, text_end);
2888 }
2889 else
2890 {
2891 if (!text_end)
2892 text_end = text + strlen(text); // FIXME-OPT
2893 text_display_end = text_end;
2894 }
2895
2896 const int text_len = (int)(text_display_end - text);
2897 if (text_len > 0)
2898 {
2899 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2900 if (g.LogEnabled)
2901 LogRenderedText(pos, text, text_display_end);
2902 }
2903 }
2904
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2905 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2906 {
2907 ImGuiContext& g = *GImGui;
2908 ImGuiWindow* window = GetCurrentWindow();
2909
2910 if (!text_end)
2911 text_end = text + strlen(text); // FIXME-OPT
2912
2913 const int text_len = (int)(text_end - text);
2914 if (text_len > 0)
2915 {
2916 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2917 if (g.LogEnabled)
2918 LogRenderedText(pos, text, text_end);
2919 }
2920 }
2921
2922 // Default clip_rect uses (pos_min,pos_max)
2923 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
RenderTextClipped(const ImVec2 & pos_min,const ImVec2 & pos_max,const char * text,const char * text_end,const ImVec2 * text_size_if_known,const ImVec2 & align,const ImRect * clip_rect)2924 void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
2925 {
2926 // Hide anything after a '##' string
2927 const char* text_display_end = FindRenderedTextEnd(text, text_end);
2928 const int text_len = (int)(text_display_end - text);
2929 if (text_len == 0)
2930 return;
2931
2932 ImGuiContext& g = *GImGui;
2933 ImGuiWindow* window = GetCurrentWindow();
2934
2935 // Perform CPU side clipping for single clipped element to avoid using scissor state
2936 ImVec2 pos = pos_min;
2937 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2938
2939 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2940 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2941 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2942 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2943 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2944
2945 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2946 if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2947 if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2948
2949 // Render
2950 if (need_clipping)
2951 {
2952 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2953 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2954 }
2955 else
2956 {
2957 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2958 }
2959 if (g.LogEnabled)
2960 LogRenderedText(pos, text, text_display_end);
2961 }
2962
2963 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2964 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2965 {
2966 ImGuiWindow* window = GetCurrentWindow();
2967
2968 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2969 if (border && (window->Flags & ImGuiWindowFlags_ShowBorders))
2970 {
2971 window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding);
2972 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding);
2973 }
2974 }
2975
2976 // Render a triangle to denote expanded/collapsed state
RenderCollapseTriangle(ImVec2 p_min,bool is_open,float scale)2977 void ImGui::RenderCollapseTriangle(ImVec2 p_min, bool is_open, float scale)
2978 {
2979 ImGuiContext& g = *GImGui;
2980 ImGuiWindow* window = GetCurrentWindow();
2981
2982 const float h = g.FontSize * 1.00f;
2983 const float r = h * 0.40f * scale;
2984 ImVec2 center = p_min + ImVec2(h*0.50f, h*0.50f*scale);
2985
2986 ImVec2 a, b, c;
2987 if (is_open)
2988 {
2989 center.y -= r*0.25f;
2990 a = center + ImVec2(0,1)*r;
2991 b = center + ImVec2(-0.866f,-0.5f)*r;
2992 c = center + ImVec2(0.866f,-0.5f)*r;
2993 }
2994 else
2995 {
2996 a = center + ImVec2(1,0)*r;
2997 b = center + ImVec2(-0.500f,0.866f)*r;
2998 c = center + ImVec2(-0.500f,-0.866f)*r;
2999 }
3000
3001 window->DrawList->AddTriangleFilled(a, b, c, GetColorU32(ImGuiCol_Text));
3002 }
3003
RenderBullet(ImVec2 pos)3004 void ImGui::RenderBullet(ImVec2 pos)
3005 {
3006 ImGuiWindow* window = GetCurrentWindow();
3007 window->DrawList->AddCircleFilled(pos, GImGui->FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8);
3008 }
3009
RenderCheckMark(ImVec2 pos,ImU32 col)3010 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col)
3011 {
3012 ImGuiContext& g = *GImGui;
3013 ImGuiWindow* window = GetCurrentWindow();
3014
3015 ImVec2 a, b, c;
3016 float start_x = (float)(int)(g.FontSize * 0.307f + 0.5f);
3017 float rem_third = (float)(int)((g.FontSize - start_x) / 3.0f);
3018 a.x = pos.x + 0.5f + start_x;
3019 b.x = a.x + rem_third;
3020 c.x = a.x + rem_third * 3.0f;
3021 b.y = pos.y - 1.0f + (float)(int)(g.Font->Ascent * (g.FontSize / g.Font->FontSize) + 0.5f) + (float)(int)(g.Font->DisplayOffset.y);
3022 a.y = b.y - rem_third;
3023 c.y = b.y - rem_third * 2.0f;
3024
3025 window->DrawList->PathLineTo(a);
3026 window->DrawList->PathLineTo(b);
3027 window->DrawList->PathLineTo(c);
3028 window->DrawList->PathStroke(col, false);
3029 }
3030
3031 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
3032 // CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize)
CalcTextSize(const char * text,const char * text_end,bool hide_text_after_double_hash,float wrap_width)3033 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
3034 {
3035 ImGuiContext& g = *GImGui;
3036
3037 const char* text_display_end;
3038 if (hide_text_after_double_hash)
3039 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
3040 else
3041 text_display_end = text_end;
3042
3043 ImFont* font = g.Font;
3044 const float font_size = g.FontSize;
3045 if (text == text_display_end)
3046 return ImVec2(0.0f, font_size);
3047 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
3048
3049 // Cancel out character spacing for the last character of a line (it is baked into glyph->XAdvance field)
3050 const float font_scale = font_size / font->FontSize;
3051 const float character_spacing_x = 1.0f * font_scale;
3052 if (text_size.x > 0.0f)
3053 text_size.x -= character_spacing_x;
3054 text_size.x = (float)(int)(text_size.x + 0.95f);
3055
3056 return text_size;
3057 }
3058
3059 // Helper to calculate coarse clipping of large list of evenly sized items.
3060 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
3061 // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX
CalcListClipping(int items_count,float items_height,int * out_items_display_start,int * out_items_display_end)3062 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
3063 {
3064 ImGuiContext& g = *GImGui;
3065 ImGuiWindow* window = GetCurrentWindowRead();
3066 if (g.LogEnabled)
3067 {
3068 // If logging is active, do not perform any clipping
3069 *out_items_display_start = 0;
3070 *out_items_display_end = items_count;
3071 return;
3072 }
3073 if (window->SkipItems)
3074 {
3075 *out_items_display_start = *out_items_display_end = 0;
3076 return;
3077 }
3078
3079 const ImVec2 pos = window->DC.CursorPos;
3080 int start = (int)((window->ClipRect.Min.y - pos.y) / items_height);
3081 int end = (int)((window->ClipRect.Max.y - pos.y) / items_height);
3082 start = ImClamp(start, 0, items_count);
3083 end = ImClamp(end + 1, start, items_count);
3084 *out_items_display_start = start;
3085 *out_items_display_end = end;
3086 }
3087
3088 // Find window given position, search front-to-back
3089 // FIXME: Note that we have a lag here because WindowRectClipped is updated in Begin() so windows moved by user via SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is called, aka before the next Begin(). Moving window thankfully isn't affected.
FindHoveredWindow(ImVec2 pos,bool excluding_childs)3090 static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs)
3091 {
3092 ImGuiContext& g = *GImGui;
3093 for (int i = g.Windows.Size-1; i >= 0; i--)
3094 {
3095 ImGuiWindow* window = g.Windows[i];
3096 if (!window->Active)
3097 continue;
3098 if (window->Flags & ImGuiWindowFlags_NoInputs)
3099 continue;
3100 if (excluding_childs && (window->Flags & ImGuiWindowFlags_ChildWindow) != 0)
3101 continue;
3102
3103 // Using the clipped AABB so a child window will typically be clipped by its parent.
3104 ImRect bb(window->WindowRectClipped.Min - g.Style.TouchExtraPadding, window->WindowRectClipped.Max + g.Style.TouchExtraPadding);
3105 if (bb.Contains(pos))
3106 return window;
3107 }
3108 return NULL;
3109 }
3110
3111 // Test if mouse cursor is hovering given rectangle
3112 // NB- Rectangle is clipped by our current clip setting
3113 // NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
IsMouseHoveringRect(const ImVec2 & r_min,const ImVec2 & r_max,bool clip)3114 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
3115 {
3116 ImGuiContext& g = *GImGui;
3117 ImGuiWindow* window = GetCurrentWindowRead();
3118
3119 // Clip
3120 ImRect rect_clipped(r_min, r_max);
3121 if (clip)
3122 rect_clipped.Clip(window->ClipRect);
3123
3124 // Expand for touch input
3125 const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
3126 return rect_for_touch.Contains(g.IO.MousePos);
3127 }
3128
IsMouseHoveringWindow()3129 bool ImGui::IsMouseHoveringWindow()
3130 {
3131 ImGuiContext& g = *GImGui;
3132 return g.HoveredWindow == g.CurrentWindow;
3133 }
3134
IsMouseHoveringAnyWindow()3135 bool ImGui::IsMouseHoveringAnyWindow()
3136 {
3137 ImGuiContext& g = *GImGui;
3138 return g.HoveredWindow != NULL;
3139 }
3140
IsPosHoveringAnyWindow(const ImVec2 & pos)3141 bool ImGui::IsPosHoveringAnyWindow(const ImVec2& pos)
3142 {
3143 return FindHoveredWindow(pos, false) != NULL;
3144 }
3145
IsKeyPressedMap(ImGuiKey key,bool repeat)3146 static bool IsKeyPressedMap(ImGuiKey key, bool repeat)
3147 {
3148 const int key_index = GImGui->IO.KeyMap[key];
3149 return ImGui::IsKeyPressed(key_index, repeat);
3150 }
3151
GetKeyIndex(ImGuiKey imgui_key)3152 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
3153 {
3154 IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
3155 return GImGui->IO.KeyMap[imgui_key];
3156 }
3157
3158 // Note that imgui doesn't know the semantic of each entry of io.KeyDown[]. Use your own indices/enums according to how your backend/engine stored them into KeyDown[]!
IsKeyDown(int user_key_index)3159 bool ImGui::IsKeyDown(int user_key_index)
3160 {
3161 if (user_key_index < 0) return false;
3162 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown));
3163 return GImGui->IO.KeysDown[user_key_index];
3164 }
3165
IsKeyPressed(int user_key_index,bool repeat)3166 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
3167 {
3168 ImGuiContext& g = *GImGui;
3169 if (user_key_index < 0) return false;
3170 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3171 const float t = g.IO.KeysDownDuration[user_key_index];
3172 if (t == 0.0f)
3173 return true;
3174
3175 if (repeat && t > g.IO.KeyRepeatDelay)
3176 {
3177 float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
3178 if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
3179 return true;
3180 }
3181 return false;
3182 }
3183
IsKeyReleased(int user_key_index)3184 bool ImGui::IsKeyReleased(int user_key_index)
3185 {
3186 ImGuiContext& g = *GImGui;
3187 if (user_key_index < 0) return false;
3188 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3189 if (g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index])
3190 return true;
3191 return false;
3192 }
3193
IsMouseDown(int button)3194 bool ImGui::IsMouseDown(int button)
3195 {
3196 ImGuiContext& g = *GImGui;
3197 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3198 return g.IO.MouseDown[button];
3199 }
3200
IsMouseClicked(int button,bool repeat)3201 bool ImGui::IsMouseClicked(int button, bool repeat)
3202 {
3203 ImGuiContext& g = *GImGui;
3204 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3205 const float t = g.IO.MouseDownDuration[button];
3206 if (t == 0.0f)
3207 return true;
3208
3209 if (repeat && t > g.IO.KeyRepeatDelay)
3210 {
3211 float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
3212 if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
3213 return true;
3214 }
3215
3216 return false;
3217 }
3218
IsMouseReleased(int button)3219 bool ImGui::IsMouseReleased(int button)
3220 {
3221 ImGuiContext& g = *GImGui;
3222 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3223 return g.IO.MouseReleased[button];
3224 }
3225
IsMouseDoubleClicked(int button)3226 bool ImGui::IsMouseDoubleClicked(int button)
3227 {
3228 ImGuiContext& g = *GImGui;
3229 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3230 return g.IO.MouseDoubleClicked[button];
3231 }
3232
IsMouseDragging(int button,float lock_threshold)3233 bool ImGui::IsMouseDragging(int button, float lock_threshold)
3234 {
3235 ImGuiContext& g = *GImGui;
3236 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3237 if (!g.IO.MouseDown[button])
3238 return false;
3239 if (lock_threshold < 0.0f)
3240 lock_threshold = g.IO.MouseDragThreshold;
3241 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
3242 }
3243
GetMousePos()3244 ImVec2 ImGui::GetMousePos()
3245 {
3246 return GImGui->IO.MousePos;
3247 }
3248
3249 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()3250 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
3251 {
3252 ImGuiContext& g = *GImGui;
3253 if (g.CurrentPopupStack.Size > 0)
3254 return g.OpenPopupStack[g.CurrentPopupStack.Size-1].MousePosOnOpen;
3255 return g.IO.MousePos;
3256 }
3257
GetMouseDragDelta(int button,float lock_threshold)3258 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
3259 {
3260 ImGuiContext& g = *GImGui;
3261 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3262 if (lock_threshold < 0.0f)
3263 lock_threshold = g.IO.MouseDragThreshold;
3264 if (g.IO.MouseDown[button])
3265 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
3266 return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment).
3267 return ImVec2(0.0f, 0.0f);
3268 }
3269
ResetMouseDragDelta(int button)3270 void ImGui::ResetMouseDragDelta(int button)
3271 {
3272 ImGuiContext& g = *GImGui;
3273 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3274 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
3275 g.IO.MouseClickedPos[button] = g.IO.MousePos;
3276 }
3277
GetMouseCursor()3278 ImGuiMouseCursor ImGui::GetMouseCursor()
3279 {
3280 return GImGui->MouseCursor;
3281 }
3282
SetMouseCursor(ImGuiMouseCursor cursor_type)3283 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
3284 {
3285 GImGui->MouseCursor = cursor_type;
3286 }
3287
CaptureKeyboardFromApp(bool capture)3288 void ImGui::CaptureKeyboardFromApp(bool capture)
3289 {
3290 GImGui->CaptureKeyboardNextFrame = capture ? 1 : 0;
3291 }
3292
CaptureMouseFromApp(bool capture)3293 void ImGui::CaptureMouseFromApp(bool capture)
3294 {
3295 GImGui->CaptureMouseNextFrame = capture ? 1 : 0;
3296 }
3297
IsItemHovered()3298 bool ImGui::IsItemHovered()
3299 {
3300 ImGuiWindow* window = GetCurrentWindowRead();
3301 return window->DC.LastItemHoveredAndUsable;
3302 }
3303
IsItemHoveredRect()3304 bool ImGui::IsItemHoveredRect()
3305 {
3306 ImGuiWindow* window = GetCurrentWindowRead();
3307 return window->DC.LastItemHoveredRect;
3308 }
3309
IsItemActive()3310 bool ImGui::IsItemActive()
3311 {
3312 ImGuiContext& g = *GImGui;
3313 if (g.ActiveId)
3314 {
3315 ImGuiWindow* window = GetCurrentWindowRead();
3316 return g.ActiveId == window->DC.LastItemId;
3317 }
3318 return false;
3319 }
3320
IsItemClicked(int mouse_button)3321 bool ImGui::IsItemClicked(int mouse_button)
3322 {
3323 return IsMouseClicked(mouse_button) && IsItemHovered();
3324 }
3325
IsAnyItemHovered()3326 bool ImGui::IsAnyItemHovered()
3327 {
3328 return GImGui->HoveredId != 0 || GImGui->HoveredIdPreviousFrame != 0;
3329 }
3330
IsAnyItemActive()3331 bool ImGui::IsAnyItemActive()
3332 {
3333 return GImGui->ActiveId != 0;
3334 }
3335
IsItemVisible()3336 bool ImGui::IsItemVisible()
3337 {
3338 ImGuiWindow* window = GetCurrentWindowRead();
3339 ImRect r(window->ClipRect);
3340 return r.Overlaps(window->DC.LastItemRect);
3341 }
3342
3343 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
SetItemAllowOverlap()3344 void ImGui::SetItemAllowOverlap()
3345 {
3346 ImGuiContext& g = *GImGui;
3347 if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
3348 g.HoveredIdAllowOverlap = true;
3349 if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
3350 g.ActiveIdAllowOverlap = true;
3351 }
3352
GetItemRectMin()3353 ImVec2 ImGui::GetItemRectMin()
3354 {
3355 ImGuiWindow* window = GetCurrentWindowRead();
3356 return window->DC.LastItemRect.Min;
3357 }
3358
GetItemRectMax()3359 ImVec2 ImGui::GetItemRectMax()
3360 {
3361 ImGuiWindow* window = GetCurrentWindowRead();
3362 return window->DC.LastItemRect.Max;
3363 }
3364
GetItemRectSize()3365 ImVec2 ImGui::GetItemRectSize()
3366 {
3367 ImGuiWindow* window = GetCurrentWindowRead();
3368 return window->DC.LastItemRect.GetSize();
3369 }
3370
CalcItemRectClosestPoint(const ImVec2 & pos,bool on_edge,float outward)3371 ImVec2 ImGui::CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge, float outward)
3372 {
3373 ImGuiWindow* window = GetCurrentWindowRead();
3374 ImRect rect = window->DC.LastItemRect;
3375 rect.Expand(outward);
3376 return rect.GetClosestPoint(pos, on_edge);
3377 }
3378
3379 // Tooltip is stored and turned into a BeginTooltip()/EndTooltip() sequence at the end of the frame. Each call override previous value.
SetTooltipV(const char * fmt,va_list args)3380 void ImGui::SetTooltipV(const char* fmt, va_list args)
3381 {
3382 ImGuiContext& g = *GImGui;
3383 ImFormatStringV(g.Tooltip, IM_ARRAYSIZE(g.Tooltip), fmt, args);
3384 }
3385
SetTooltip(const char * fmt,...)3386 void ImGui::SetTooltip(const char* fmt, ...)
3387 {
3388 va_list args;
3389 va_start(args, fmt);
3390 SetTooltipV(fmt, args);
3391 va_end(args);
3392 }
3393
GetVisibleRect()3394 static ImRect GetVisibleRect()
3395 {
3396 ImGuiContext& g = *GImGui;
3397 if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y)
3398 return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax);
3399 return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3400 }
3401
BeginTooltip()3402 void ImGui::BeginTooltip()
3403 {
3404 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
3405 ImGui::Begin("##Tooltip", NULL, flags);
3406 }
3407
EndTooltip()3408 void ImGui::EndTooltip()
3409 {
3410 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
3411 ImGui::End();
3412 }
3413
IsPopupOpen(ImGuiID id)3414 static bool IsPopupOpen(ImGuiID id)
3415 {
3416 ImGuiContext& g = *GImGui;
3417 return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id;
3418 }
3419
3420 // Mark popup as open (toggle toward open state).
3421 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
3422 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
3423 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(const char * str_id,bool reopen_existing)3424 void ImGui::OpenPopupEx(const char* str_id, bool reopen_existing)
3425 {
3426 ImGuiContext& g = *GImGui;
3427 ImGuiWindow* window = g.CurrentWindow;
3428 ImGuiID id = window->GetID(str_id);
3429 int current_stack_size = g.CurrentPopupStack.Size;
3430 ImGuiPopupRef popup_ref = ImGuiPopupRef(id, window, window->GetID("##menus"), g.IO.MousePos); // Tagged as new ref because constructor sets Window to NULL (we are passing the ParentWindow info here)
3431 if (g.OpenPopupStack.Size < current_stack_size + 1)
3432 g.OpenPopupStack.push_back(popup_ref);
3433 else if (reopen_existing || g.OpenPopupStack[current_stack_size].PopupId != id)
3434 {
3435 g.OpenPopupStack.resize(current_stack_size+1);
3436 g.OpenPopupStack[current_stack_size] = popup_ref;
3437 }
3438 }
3439
OpenPopup(const char * str_id)3440 void ImGui::OpenPopup(const char* str_id)
3441 {
3442 ImGui::OpenPopupEx(str_id, false);
3443 }
3444
CloseInactivePopups()3445 static void CloseInactivePopups()
3446 {
3447 ImGuiContext& g = *GImGui;
3448 if (g.OpenPopupStack.empty())
3449 return;
3450
3451 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
3452 // Don't close our own child popup windows
3453 int n = 0;
3454 if (g.FocusedWindow)
3455 {
3456 for (n = 0; n < g.OpenPopupStack.Size; n++)
3457 {
3458 ImGuiPopupRef& popup = g.OpenPopupStack[n];
3459 if (!popup.Window)
3460 continue;
3461 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
3462 if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
3463 continue;
3464
3465 bool has_focus = false;
3466 for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++)
3467 has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == g.FocusedWindow->RootWindow);
3468 if (!has_focus)
3469 break;
3470 }
3471 }
3472 if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a useful breakpoint on the line below
3473 g.OpenPopupStack.resize(n);
3474 }
3475
GetFrontMostModalRootWindow()3476 static ImGuiWindow* GetFrontMostModalRootWindow()
3477 {
3478 ImGuiContext& g = *GImGui;
3479 for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
3480 if (ImGuiWindow* front_most_popup = g.OpenPopupStack.Data[n].Window)
3481 if (front_most_popup->Flags & ImGuiWindowFlags_Modal)
3482 return front_most_popup;
3483 return NULL;
3484 }
3485
ClosePopupToLevel(int remaining)3486 static void ClosePopupToLevel(int remaining)
3487 {
3488 ImGuiContext& g = *GImGui;
3489 if (remaining > 0)
3490 ImGui::FocusWindow(g.OpenPopupStack[remaining-1].Window);
3491 else
3492 ImGui::FocusWindow(g.OpenPopupStack[0].ParentWindow);
3493 g.OpenPopupStack.resize(remaining);
3494 }
3495
ClosePopup(ImGuiID id)3496 static void ClosePopup(ImGuiID id)
3497 {
3498 if (!IsPopupOpen(id))
3499 return;
3500 ImGuiContext& g = *GImGui;
3501 ClosePopupToLevel(g.OpenPopupStack.Size - 1);
3502 }
3503
3504 // Close the popup we have begin-ed into.
CloseCurrentPopup()3505 void ImGui::CloseCurrentPopup()
3506 {
3507 ImGuiContext& g = *GImGui;
3508 int popup_idx = g.CurrentPopupStack.Size - 1;
3509 if (popup_idx < 0 || popup_idx > g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
3510 return;
3511 while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu))
3512 popup_idx--;
3513 ClosePopupToLevel(popup_idx);
3514 }
3515
ClearSetNextWindowData()3516 static inline void ClearSetNextWindowData()
3517 {
3518 ImGuiContext& g = *GImGui;
3519 g.SetNextWindowPosCond = g.SetNextWindowSizeCond = g.SetNextWindowContentSizeCond = g.SetNextWindowCollapsedCond = 0;
3520 g.SetNextWindowSizeConstraint = g.SetNextWindowFocus = false;
3521 }
3522
BeginPopupEx(const char * str_id,ImGuiWindowFlags extra_flags)3523 static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags)
3524 {
3525 ImGuiContext& g = *GImGui;
3526 ImGuiWindow* window = g.CurrentWindow;
3527 const ImGuiID id = window->GetID(str_id);
3528 if (!IsPopupOpen(id))
3529 {
3530 ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3531 return false;
3532 }
3533
3534 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
3535 ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
3536
3537 char name[20];
3538 if (flags & ImGuiWindowFlags_ChildMenu)
3539 ImFormatString(name, IM_ARRAYSIZE(name), "##menu_%d", g.CurrentPopupStack.Size); // Recycle windows based on depth
3540 else
3541 ImFormatString(name, IM_ARRAYSIZE(name), "##popup_%08x", id); // Not recycling, so we can close/open during the same frame
3542
3543 bool is_open = ImGui::Begin(name, NULL, flags);
3544 if (!(window->Flags & ImGuiWindowFlags_ShowBorders))
3545 g.CurrentWindow->Flags &= ~ImGuiWindowFlags_ShowBorders;
3546 if (!is_open) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
3547 ImGui::EndPopup();
3548
3549 return is_open;
3550 }
3551
BeginPopup(const char * str_id)3552 bool ImGui::BeginPopup(const char* str_id)
3553 {
3554 if (GImGui->OpenPopupStack.Size <= GImGui->CurrentPopupStack.Size) // Early out for performance
3555 {
3556 ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3557 return false;
3558 }
3559 return BeginPopupEx(str_id, ImGuiWindowFlags_ShowBorders);
3560 }
3561
BeginPopupModal(const char * name,bool * p_open,ImGuiWindowFlags extra_flags)3562 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags extra_flags)
3563 {
3564 ImGuiContext& g = *GImGui;
3565 ImGuiWindow* window = g.CurrentWindow;
3566 const ImGuiID id = window->GetID(name);
3567 if (!IsPopupOpen(id))
3568 {
3569 ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3570 return false;
3571 }
3572
3573 ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoSavedSettings;
3574 bool is_open = ImGui::Begin(name, p_open, flags);
3575 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
3576 {
3577 ImGui::EndPopup();
3578 if (is_open)
3579 ClosePopup(id);
3580 return false;
3581 }
3582
3583 return is_open;
3584 }
3585
EndPopup()3586 void ImGui::EndPopup()
3587 {
3588 ImGuiWindow* window = GetCurrentWindow();
3589 IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
3590 IM_ASSERT(GImGui->CurrentPopupStack.Size > 0);
3591 ImGui::End();
3592 if (!(window->Flags & ImGuiWindowFlags_Modal))
3593 ImGui::PopStyleVar();
3594 }
3595
3596 // This is a helper to handle the most simple case of associating one named popup to one given widget.
3597 // 1. If you have many possible popups (for different "instances" of a same widget, or for wholly different widgets), you may be better off handling
3598 // this yourself so you can store data relative to the widget that opened the popup instead of choosing different popup identifiers.
3599 // 2. If you want right-clicking on the same item to reopen the popup at new location, use the same code replacing IsItemHovered() with IsItemHoveredRect()
3600 // and passing true to the OpenPopupEx().
3601 // Because: hovering an item in a window below the popup won't normally trigger is hovering behavior/coloring. The pattern of ignoring the fact that
3602 // the item isn't interactable (because it is blocked by the active popup) may useful in some situation when e.g. large canvas as one item, content of menu
3603 // driven by click position.
BeginPopupContextItem(const char * str_id,int mouse_button)3604 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
3605 {
3606 if (IsItemHovered() && IsMouseClicked(mouse_button))
3607 OpenPopupEx(str_id, false);
3608 return BeginPopup(str_id);
3609 }
3610
BeginPopupContextWindow(bool also_over_items,const char * str_id,int mouse_button)3611 bool ImGui::BeginPopupContextWindow(bool also_over_items, const char* str_id, int mouse_button)
3612 {
3613 if (!str_id) str_id = "window_context_menu";
3614 if (IsMouseHoveringWindow() && IsMouseClicked(mouse_button))
3615 if (also_over_items || !IsAnyItemHovered())
3616 OpenPopupEx(str_id, true);
3617 return BeginPopup(str_id);
3618 }
3619
BeginPopupContextVoid(const char * str_id,int mouse_button)3620 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
3621 {
3622 if (!str_id) str_id = "void_context_menu";
3623 if (!IsMouseHoveringAnyWindow() && IsMouseClicked(mouse_button))
3624 OpenPopupEx(str_id, true);
3625 return BeginPopup(str_id);
3626 }
3627
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)3628 static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
3629 {
3630 ImGuiWindow* window = ImGui::GetCurrentWindow();
3631 ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
3632
3633 const ImVec2 content_avail = ImGui::GetContentRegionAvail();
3634 ImVec2 size = ImFloor(size_arg);
3635 if (size.x <= 0.0f)
3636 {
3637 if (size.x == 0.0f)
3638 flags |= ImGuiWindowFlags_ChildWindowAutoFitX;
3639 size.x = ImMax(content_avail.x, 4.0f) - fabsf(size.x); // Arbitrary minimum zero-ish child size of 4.0f (0.0f causing too much issues)
3640 }
3641 if (size.y <= 0.0f)
3642 {
3643 if (size.y == 0.0f)
3644 flags |= ImGuiWindowFlags_ChildWindowAutoFitY;
3645 size.y = ImMax(content_avail.y, 4.0f) - fabsf(size.y);
3646 }
3647 if (border)
3648 flags |= ImGuiWindowFlags_ShowBorders;
3649 flags |= extra_flags;
3650
3651 char title[256];
3652 if (name)
3653 ImFormatString(title, IM_ARRAYSIZE(title), "%s.%s.%08X", window->Name, name, id);
3654 else
3655 ImFormatString(title, IM_ARRAYSIZE(title), "%s.%08X", window->Name, id);
3656
3657 bool ret = ImGui::Begin(title, NULL, size, -1.0f, flags);
3658
3659 if (!(window->Flags & ImGuiWindowFlags_ShowBorders))
3660 ImGui::GetCurrentWindow()->Flags &= ~ImGuiWindowFlags_ShowBorders;
3661
3662 return ret;
3663 }
3664
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)3665 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
3666 {
3667 ImGuiWindow* window = GetCurrentWindow();
3668 return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
3669 }
3670
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)3671 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
3672 {
3673 return BeginChildEx(NULL, id, size_arg, border, extra_flags);
3674 }
3675
EndChild()3676 void ImGui::EndChild()
3677 {
3678 ImGuiWindow* window = GetCurrentWindow();
3679
3680 IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() callss
3681 if ((window->Flags & ImGuiWindowFlags_ComboBox) || window->BeginCount > 1)
3682 {
3683 ImGui::End();
3684 }
3685 else
3686 {
3687 // When using auto-filling child window, we don't provide full width/height to ItemSize so that it doesn't feed back into automatic size-fitting.
3688 ImVec2 sz = GetWindowSize();
3689 if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitX) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
3690 sz.x = ImMax(4.0f, sz.x);
3691 if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitY)
3692 sz.y = ImMax(4.0f, sz.y);
3693
3694 ImGui::End();
3695
3696 window = GetCurrentWindow();
3697 ImRect bb(window->DC.CursorPos, window->DC.CursorPos + sz);
3698 ItemSize(sz);
3699 ItemAdd(bb, NULL);
3700 }
3701 }
3702
3703 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)3704 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
3705 {
3706 ImGuiContext& g = *GImGui;
3707 const ImGuiStyle& style = g.Style;
3708 ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, style.Colors[ImGuiCol_FrameBg]);
3709 ImGui::PushStyleVar(ImGuiStyleVar_ChildWindowRounding, style.FrameRounding);
3710 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
3711 return ImGui::BeginChild(id, size, (g.CurrentWindow->Flags & ImGuiWindowFlags_ShowBorders) ? true : false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
3712 }
3713
EndChildFrame()3714 void ImGui::EndChildFrame()
3715 {
3716 ImGui::EndChild();
3717 ImGui::PopStyleVar(2);
3718 ImGui::PopStyleColor();
3719 }
3720
3721 // Save and compare stack sizes on Begin()/End() to detect usage errors
CheckStacksSize(ImGuiWindow * window,bool write)3722 static void CheckStacksSize(ImGuiWindow* window, bool write)
3723 {
3724 // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
3725 ImGuiContext& g = *GImGui;
3726 int* p_backup = &window->DC.StackSizesBackup[0];
3727 { int current = window->IDStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!"); p_backup++; } // Too few or too many PopID()/TreePop()
3728 { int current = window->DC.GroupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!"); p_backup++; } // Too few or too many EndGroup()
3729 { int current = g.CurrentPopupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup()
3730 { int current = g.ColorModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleColor/PopStyleColor Mismatch!"); p_backup++; } // Too few or too many PopStyleColor()
3731 { int current = g.StyleModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleVar/PopStyleVar Mismatch!"); p_backup++; } // Too few or too many PopStyleVar()
3732 { int current = g.FontStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushFont/PopFont Mismatch!"); p_backup++; } // Too few or too many PopFont()
3733 IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
3734 }
3735
FindBestPopupWindowPos(const ImVec2 & base_pos,const ImVec2 & size,int * last_dir,const ImRect & r_inner)3736 static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& r_inner)
3737 {
3738 const ImGuiStyle& style = GImGui->Style;
3739
3740 // Clamp into visible area while not overlapping the cursor. Safety padding is optional if our popup size won't fit without it.
3741 ImVec2 safe_padding = style.DisplaySafeAreaPadding;
3742 ImRect r_outer(GetVisibleRect());
3743 r_outer.Reduce(ImVec2((size.x - r_outer.GetWidth() > safe_padding.x*2) ? safe_padding.x : 0.0f, (size.y - r_outer.GetHeight() > safe_padding.y*2) ? safe_padding.y : 0.0f));
3744 ImVec2 base_pos_clamped = ImClamp(base_pos, r_outer.Min, r_outer.Max - size);
3745
3746 for (int n = (*last_dir != -1) ? -1 : 0; n < 4; n++) // Last, Right, down, up, left. (Favor last used direction).
3747 {
3748 const int dir = (n == -1) ? *last_dir : n;
3749 ImRect rect(dir == 0 ? r_inner.Max.x : r_outer.Min.x, dir == 1 ? r_inner.Max.y : r_outer.Min.y, dir == 3 ? r_inner.Min.x : r_outer.Max.x, dir == 2 ? r_inner.Min.y : r_outer.Max.y);
3750 if (rect.GetWidth() < size.x || rect.GetHeight() < size.y)
3751 continue;
3752 *last_dir = dir;
3753 return ImVec2(dir == 0 ? r_inner.Max.x : dir == 3 ? r_inner.Min.x - size.x : base_pos_clamped.x, dir == 1 ? r_inner.Max.y : dir == 2 ? r_inner.Min.y - size.y : base_pos_clamped.y);
3754 }
3755
3756 // Fallback, try to keep within display
3757 *last_dir = -1;
3758 ImVec2 pos = base_pos;
3759 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
3760 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
3761 return pos;
3762 }
3763
FindWindowByName(const char * name)3764 ImGuiWindow* ImGui::FindWindowByName(const char* name)
3765 {
3766 // FIXME-OPT: Store sorted hashes -> pointers so we can do a bissection in a contiguous block
3767 ImGuiContext& g = *GImGui;
3768 ImGuiID id = ImHash(name, 0);
3769 for (int i = 0; i < g.Windows.Size; i++)
3770 if (g.Windows[i]->ID == id)
3771 return g.Windows[i];
3772 return NULL;
3773 }
3774
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)3775 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
3776 {
3777 ImGuiContext& g = *GImGui;
3778
3779 // Create window the first time
3780 ImGuiWindow* window = (ImGuiWindow*)ImGui::MemAlloc(sizeof(ImGuiWindow));
3781 IM_PLACEMENT_NEW(window) ImGuiWindow(name);
3782 window->Flags = flags;
3783
3784 if (flags & ImGuiWindowFlags_NoSavedSettings)
3785 {
3786 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
3787 window->Size = window->SizeFull = size;
3788 }
3789 else
3790 {
3791 // Retrieve settings from .ini file
3792 // Use SetWindowPos() or SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
3793 window->PosFloat = ImVec2(60, 60);
3794 window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
3795
3796 ImGuiIniData* settings = FindWindowSettings(name);
3797 if (!settings)
3798 {
3799 settings = AddWindowSettings(name);
3800 }
3801 else
3802 {
3803 window->SetWindowPosAllowFlags &= ~ImGuiSetCond_FirstUseEver;
3804 window->SetWindowSizeAllowFlags &= ~ImGuiSetCond_FirstUseEver;
3805 window->SetWindowCollapsedAllowFlags &= ~ImGuiSetCond_FirstUseEver;
3806 }
3807
3808 if (settings->Pos.x != FLT_MAX)
3809 {
3810 window->PosFloat = settings->Pos;
3811 window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
3812 window->Collapsed = settings->Collapsed;
3813 }
3814
3815 if (ImLengthSqr(settings->Size) > 0.00001f && !(flags & ImGuiWindowFlags_NoResize))
3816 size = settings->Size;
3817 window->Size = window->SizeFull = size;
3818 }
3819
3820 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
3821 {
3822 window->AutoFitFramesX = window->AutoFitFramesY = 2;
3823 window->AutoFitOnlyGrows = false;
3824 }
3825 else
3826 {
3827 if (window->Size.x <= 0.0f)
3828 window->AutoFitFramesX = 2;
3829 if (window->Size.y <= 0.0f)
3830 window->AutoFitFramesY = 2;
3831 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
3832 }
3833
3834 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
3835 g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once
3836 else
3837 g.Windows.push_back(window);
3838 return window;
3839 }
3840
ApplySizeFullWithConstraint(ImGuiWindow * window,ImVec2 new_size)3841 static void ApplySizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size)
3842 {
3843 ImGuiContext& g = *GImGui;
3844 if (g.SetNextWindowSizeConstraint)
3845 {
3846 // Using -1,-1 on either X/Y axis to preserve the current size.
3847 ImRect cr = g.SetNextWindowSizeConstraintRect;
3848 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
3849 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
3850 if (g.SetNextWindowSizeConstraintCallback)
3851 {
3852 ImGuiSizeConstraintCallbackData data;
3853 data.UserData = g.SetNextWindowSizeConstraintCallbackUserData;
3854 data.Pos = window->Pos;
3855 data.CurrentSize = window->SizeFull;
3856 data.DesiredSize = new_size;
3857 g.SetNextWindowSizeConstraintCallback(&data);
3858 new_size = data.DesiredSize;
3859 }
3860 }
3861 if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
3862 new_size = ImMax(new_size, g.Style.WindowMinSize);
3863 window->SizeFull = new_size;
3864 }
3865
3866 // Push a new ImGui window to add widgets to.
3867 // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair.
3868 // - Begin/End can be called multiple times during the frame with the same window name to append content.
3869 // - 'size_on_first_use' for a regular window denote the initial size for first-time creation (no saved data) and isn't that useful. Use SetNextWindowSize() prior to calling Begin() for more flexible window manipulation.
3870 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
3871 // You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file.
3872 // - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned.
3873 // - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed.
3874 // - Passing non-zero 'size' is roughly equivalent to calling SetNextWindowSize(size, ImGuiSetCond_FirstUseEver) prior to calling Begin().
Begin(const char * name,bool * p_open,ImGuiWindowFlags flags)3875 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
3876 {
3877 return ImGui::Begin(name, p_open, ImVec2(0.f, 0.f), -1.0f, flags);
3878 }
3879
Begin(const char * name,bool * p_open,const ImVec2 & size_on_first_use,float bg_alpha,ImGuiWindowFlags flags)3880 bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha, ImGuiWindowFlags flags)
3881 {
3882 ImGuiContext& g = *GImGui;
3883 const ImGuiStyle& style = g.Style;
3884 IM_ASSERT(name != NULL); // Window name required
3885 IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame()
3886 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
3887
3888 if (flags & ImGuiWindowFlags_NoInputs)
3889 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
3890
3891 // Find or create
3892 bool window_is_new = false;
3893 ImGuiWindow* window = FindWindowByName(name);
3894 if (!window)
3895 {
3896 window = CreateNewWindow(name, size_on_first_use, flags);
3897 window_is_new = true;
3898 }
3899
3900 const int current_frame = ImGui::GetFrameCount();
3901 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
3902 if (first_begin_of_the_frame)
3903 window->Flags = (ImGuiWindowFlags)flags;
3904 else
3905 flags = window->Flags;
3906
3907 // Add to stack
3908 ImGuiWindow* parent_window = !g.CurrentWindowStack.empty() ? g.CurrentWindowStack.back() : NULL;
3909 g.CurrentWindowStack.push_back(window);
3910 SetCurrentWindow(window);
3911 CheckStacksSize(window, true);
3912 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
3913
3914 bool window_was_active = (window->LastFrameActive == current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
3915 if (flags & ImGuiWindowFlags_Popup)
3916 {
3917 ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
3918 window_was_active &= (window->PopupId == popup_ref.PopupId);
3919 window_was_active &= (window == popup_ref.Window);
3920 popup_ref.Window = window;
3921 g.CurrentPopupStack.push_back(popup_ref);
3922 window->PopupId = popup_ref.PopupId;
3923 }
3924
3925 const bool window_appearing_after_being_hidden = (window->HiddenFrames == 1);
3926
3927 // Process SetNextWindow***() calls
3928 bool window_pos_set_by_api = false, window_size_set_by_api = false;
3929 if (g.SetNextWindowPosCond)
3930 {
3931 const ImVec2 backup_cursor_pos = window->DC.CursorPos; // FIXME: not sure of the exact reason of this saving/restore anymore :( need to look into that.
3932 if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowPosAllowFlags |= ImGuiSetCond_Appearing;
3933 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.SetNextWindowPosCond) != 0;
3934 if (window_pos_set_by_api && ImLengthSqr(g.SetNextWindowPosVal - ImVec2(-FLT_MAX,-FLT_MAX)) < 0.001f)
3935 {
3936 window->SetWindowPosCenterWanted = true; // May be processed on the next frame if this is our first frame and we are measuring size
3937 window->SetWindowPosAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
3938 }
3939 else
3940 {
3941 SetWindowPos(window, g.SetNextWindowPosVal, g.SetNextWindowPosCond);
3942 }
3943 window->DC.CursorPos = backup_cursor_pos;
3944 g.SetNextWindowPosCond = 0;
3945 }
3946 if (g.SetNextWindowSizeCond)
3947 {
3948 if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowSizeAllowFlags |= ImGuiSetCond_Appearing;
3949 window_size_set_by_api = (window->SetWindowSizeAllowFlags & g.SetNextWindowSizeCond) != 0;
3950 SetWindowSize(window, g.SetNextWindowSizeVal, g.SetNextWindowSizeCond);
3951 g.SetNextWindowSizeCond = 0;
3952 }
3953 if (g.SetNextWindowContentSizeCond)
3954 {
3955 window->SizeContentsExplicit = g.SetNextWindowContentSizeVal;
3956 g.SetNextWindowContentSizeCond = 0;
3957 }
3958 else if (first_begin_of_the_frame)
3959 {
3960 window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
3961 }
3962 if (g.SetNextWindowCollapsedCond)
3963 {
3964 if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowCollapsedAllowFlags |= ImGuiSetCond_Appearing;
3965 SetWindowCollapsed(window, g.SetNextWindowCollapsedVal, g.SetNextWindowCollapsedCond);
3966 g.SetNextWindowCollapsedCond = 0;
3967 }
3968 if (g.SetNextWindowFocus)
3969 {
3970 ImGui::SetWindowFocus();
3971 g.SetNextWindowFocus = false;
3972 }
3973
3974 // Update known root window (if we are a child window, otherwise window == window->RootWindow)
3975 int root_idx, root_non_popup_idx;
3976 for (root_idx = g.CurrentWindowStack.Size - 1; root_idx > 0; root_idx--)
3977 if (!(g.CurrentWindowStack[root_idx]->Flags & ImGuiWindowFlags_ChildWindow))
3978 break;
3979 for (root_non_popup_idx = root_idx; root_non_popup_idx > 0; root_non_popup_idx--)
3980 if (!(g.CurrentWindowStack[root_non_popup_idx]->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
3981 break;
3982 window->ParentWindow = parent_window;
3983 window->RootWindow = g.CurrentWindowStack[root_idx];
3984 window->RootNonPopupWindow = g.CurrentWindowStack[root_non_popup_idx]; // This is merely for displaying the TitleBgActive color.
3985
3986 // When reusing window again multiple times a frame, just append content (don't need to setup again)
3987 if (first_begin_of_the_frame)
3988 {
3989 window->Active = true;
3990 window->IndexWithinParent = 0;
3991 window->BeginCount = 0;
3992 window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
3993 window->LastFrameActive = current_frame;
3994 window->IDStack.resize(1);
3995
3996 // Clear draw list, setup texture, outer clipping rectangle
3997 window->DrawList->Clear();
3998 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
3999 ImRect fullscreen_rect(GetVisibleRect());
4000 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_ComboBox|ImGuiWindowFlags_Popup)))
4001 PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
4002 else
4003 PushClipRect(fullscreen_rect.Min, fullscreen_rect.Max, true);
4004
4005 if (!window_was_active)
4006 {
4007 // Popup first latch mouse position, will position itself when it appears next frame
4008 window->AutoPosLastDirection = -1;
4009 if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
4010 window->PosFloat = g.IO.MousePos;
4011 }
4012
4013 // Collapse window by double-clicking on title bar
4014 // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing
4015 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
4016 {
4017 ImRect title_bar_rect = window->TitleBarRect();
4018 if (g.HoveredWindow == window && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
4019 {
4020 window->Collapsed = !window->Collapsed;
4021 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4022 MarkIniSettingsDirty();
4023 FocusWindow(window);
4024 }
4025 }
4026 else
4027 {
4028 window->Collapsed = false;
4029 }
4030
4031 // SIZE
4032
4033 // Save contents size from last frame for auto-fitting (unless explicitly specified)
4034 window->SizeContents.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.x - window->Pos.x) + window->Scroll.x));
4035 window->SizeContents.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.y - window->Pos.y) + window->Scroll.y));
4036
4037 // Hide popup/tooltip window when first appearing while we measure size (because we recycle them)
4038 if (window->HiddenFrames > 0)
4039 window->HiddenFrames--;
4040 if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && !window_was_active)
4041 {
4042 window->HiddenFrames = 1;
4043 if (flags & ImGuiWindowFlags_AlwaysAutoResize)
4044 {
4045 if (!window_size_set_by_api)
4046 window->Size = window->SizeFull = ImVec2(0.f, 0.f);
4047 window->SizeContents = ImVec2(0.f, 0.f);
4048 }
4049 }
4050
4051 // Lock window padding so that altering the ShowBorders flag for children doesn't have side-effects.
4052 window->WindowPadding = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_ComboBox | ImGuiWindowFlags_Popup))) ? ImVec2(0,0) : style.WindowPadding;
4053
4054 // Calculate auto-fit size
4055 ImVec2 size_auto_fit;
4056 if ((flags & ImGuiWindowFlags_Tooltip) != 0)
4057 {
4058 // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose.
4059 size_auto_fit = window->SizeContents + window->WindowPadding - ImVec2(0.0f, style.ItemSpacing.y);
4060 }
4061 else
4062 {
4063 size_auto_fit = ImClamp(window->SizeContents + window->WindowPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding));
4064
4065 // Handling case of auto fit window not fitting in screen on one axis, we are growing auto fit size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
4066 if (size_auto_fit.x < window->SizeContents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar))
4067 size_auto_fit.y += style.ScrollbarSize;
4068 if (size_auto_fit.y < window->SizeContents.y && !(flags & ImGuiWindowFlags_NoScrollbar))
4069 size_auto_fit.x += style.ScrollbarSize;
4070 size_auto_fit.y = ImMax(size_auto_fit.y - style.ItemSpacing.y, 0.0f);
4071 }
4072
4073 // Handle automatic resize
4074 if (window->Collapsed)
4075 {
4076 // We still process initial auto-fit on collapsed windows to get a window width,
4077 // But otherwise we don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
4078 if (window->AutoFitFramesX > 0)
4079 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
4080 if (window->AutoFitFramesY > 0)
4081 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
4082 }
4083 else
4084 {
4085 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window_size_set_by_api)
4086 {
4087 window->SizeFull = size_auto_fit;
4088 }
4089 else if ((window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) && !window_size_set_by_api)
4090 {
4091 // Auto-fit only grows during the first few frames
4092 if (window->AutoFitFramesX > 0)
4093 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
4094 if (window->AutoFitFramesY > 0)
4095 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
4096 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4097 MarkIniSettingsDirty();
4098 }
4099 }
4100
4101 // Apply minimum/maximum window size constraints and final size
4102 ApplySizeFullWithConstraint(window, window->SizeFull);
4103 window->Size = window->Collapsed ? window->TitleBarRect().GetSize() : window->SizeFull;
4104
4105 // POSITION
4106
4107 // Position child window
4108 if (flags & ImGuiWindowFlags_ChildWindow)
4109 {
4110 window->IndexWithinParent = parent_window->DC.ChildWindows.Size;
4111 parent_window->DC.ChildWindows.push_back(window);
4112 }
4113 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup))
4114 {
4115 window->Pos = window->PosFloat = parent_window->DC.CursorPos;
4116 window->Size = window->SizeFull = size_on_first_use; // NB: argument name 'size_on_first_use' misleading here, it's really just 'size' as provided by user passed via BeginChild()->Begin().
4117 }
4118
4119 bool window_pos_center = false;
4120 window_pos_center |= (window->SetWindowPosCenterWanted && window->HiddenFrames == 0);
4121 window_pos_center |= ((flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api && window_appearing_after_being_hidden);
4122 if (window_pos_center)
4123 {
4124 // Center (any sort of window)
4125 SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, fullscreen_rect.GetCenter() - window->SizeFull * 0.5f), 0);
4126 }
4127 else if (flags & ImGuiWindowFlags_ChildMenu)
4128 {
4129 // Child menus typically request _any_ position within the parent menu item, and then our FindBestPopupWindowPos() function will move the new menu outside the parent bounds.
4130 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
4131 IM_ASSERT(window_pos_set_by_api);
4132 float horizontal_overlap = style.ItemSpacing.x; // We want some overlap to convey the relative depth of each popup (currently the amount of overlap it is hard-coded to style.ItemSpacing.x, may need to introduce another style value).
4133 ImRect rect_to_avoid;
4134 if (parent_window->DC.MenuBarAppending)
4135 rect_to_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
4136 else
4137 rect_to_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX);
4138 window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
4139 }
4140 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_appearing_after_being_hidden)
4141 {
4142 ImRect rect_to_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1);
4143 window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
4144 }
4145
4146 // Position tooltip (always follows mouse)
4147 if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api)
4148 {
4149 ImRect rect_to_avoid(g.IO.MousePos.x - 16, g.IO.MousePos.y - 8, g.IO.MousePos.x + 24, g.IO.MousePos.y + 24); // FIXME: Completely hard-coded. Perhaps center on cursor hit-point instead?
4150 window->PosFloat = FindBestPopupWindowPos(g.IO.MousePos, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
4151 if (window->AutoPosLastDirection == -1)
4152 window->PosFloat = g.IO.MousePos + ImVec2(2,2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
4153 }
4154
4155 // Clamp position so it stays visible
4156 if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
4157 {
4158 if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
4159 {
4160 ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
4161 window->PosFloat = ImMax(window->PosFloat + window->Size, padding) - window->Size;
4162 window->PosFloat = ImMin(window->PosFloat, g.IO.DisplaySize - padding);
4163 }
4164 }
4165 window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
4166
4167 // Default item width. Make it proportional to window size if window manually resizes
4168 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
4169 window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
4170 else
4171 window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
4172
4173 // Prepare for focus requests
4174 window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1);
4175 window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1);
4176 window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1;
4177 window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX;
4178
4179 // Apply scrolling
4180 if (window->ScrollTarget.x < FLT_MAX)
4181 {
4182 window->Scroll.x = window->ScrollTarget.x;
4183 window->ScrollTarget.x = FLT_MAX;
4184 }
4185 if (window->ScrollTarget.y < FLT_MAX)
4186 {
4187 float center_ratio = window->ScrollTargetCenterRatio.y;
4188 window->Scroll.y = window->ScrollTarget.y - ((1.0f - center_ratio) * (window->TitleBarHeight() + window->MenuBarHeight())) - (center_ratio * window->SizeFull.y);
4189 window->ScrollTarget.y = FLT_MAX;
4190 }
4191 window->Scroll = ImMax(window->Scroll, ImVec2(0.0f, 0.0f));
4192 if (!window->Collapsed && !window->SkipItems)
4193 window->Scroll = ImMin(window->Scroll, ImMax(ImVec2(0.0f, 0.0f), window->SizeContents - window->SizeFull + window->ScrollbarSizes));
4194
4195 // Modal window darkens what is behind them
4196 if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow())
4197 window->DrawList->AddRectFilled(fullscreen_rect.Min, fullscreen_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio));
4198
4199 // Draw window + handle manual resize
4200 ImRect title_bar_rect = window->TitleBarRect();
4201 const float window_rounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding;
4202 if (window->Collapsed)
4203 {
4204 // Draw title bar only
4205 RenderFrame(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32(ImGuiCol_TitleBgCollapsed), true, window_rounding);
4206 }
4207 else
4208 {
4209 ImU32 resize_col = 0;
4210 const float resize_corner_size = ImMax(g.FontSize * 1.35f, window_rounding + 1.0f + g.FontSize * 0.2f);
4211 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && !(flags & ImGuiWindowFlags_NoResize))
4212 {
4213 // Manual resize
4214 const ImVec2 br = window->Rect().GetBR();
4215 const ImRect resize_rect(br - ImVec2(resize_corner_size * 0.75f, resize_corner_size * 0.75f), br);
4216 const ImGuiID resize_id = window->GetID("#RESIZE");
4217 bool hovered, held;
4218 ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds);
4219 resize_col = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
4220
4221 if (hovered || held)
4222 g.MouseCursor = ImGuiMouseCursor_ResizeNWSE;
4223
4224 if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0])
4225 {
4226 // Manual auto-fit when double-clicking
4227 ApplySizeFullWithConstraint(window, size_auto_fit);
4228 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4229 MarkIniSettingsDirty();
4230 ClearActiveID();
4231 }
4232 else if (held)
4233 {
4234 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
4235 ApplySizeFullWithConstraint(window, (g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize()) - window->Pos);
4236 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4237 MarkIniSettingsDirty();
4238 }
4239
4240 window->Size = window->SizeFull;
4241 title_bar_rect = window->TitleBarRect();
4242 }
4243
4244 // Scrollbars
4245 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > window->Size.y + style.ItemSpacing.y) && !(flags & ImGuiWindowFlags_NoScrollbar));
4246 window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > window->Size.x - (window->ScrollbarY ? style.ScrollbarSize : 0.0f) - window->WindowPadding.x) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
4247 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
4248 window->BorderSize = (flags & ImGuiWindowFlags_ShowBorders) ? 1.0f : 0.0f;
4249
4250 // Window background, Default Alpha
4251 ImGuiCol bg_color_idx = ImGuiCol_WindowBg;
4252 if ((flags & ImGuiWindowFlags_ComboBox) != 0)
4253 bg_color_idx = ImGuiCol_ComboBg;
4254 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 || (flags & ImGuiWindowFlags_Popup) != 0)
4255 bg_color_idx = ImGuiCol_PopupBg;
4256 else if ((flags & ImGuiWindowFlags_ChildWindow) != 0)
4257 bg_color_idx = ImGuiCol_ChildWindowBg;
4258 ImVec4 bg_color = style.Colors[bg_color_idx];
4259 if (bg_alpha >= 0.0f)
4260 bg_color.w = bg_alpha;
4261 bg_color.w *= style.Alpha;
4262 if (bg_color.w > 0.0f)
4263 window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, ColorConvertFloat4ToU32(bg_color), window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImGuiCorner_All : ImGuiCorner_BottomLeft|ImGuiCorner_BottomRight);
4264
4265 // Title bar
4266 if (!(flags & ImGuiWindowFlags_NoTitleBar))
4267 window->DrawList->AddRectFilled(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32((g.FocusedWindow && window->RootNonPopupWindow == g.FocusedWindow->RootNonPopupWindow) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, ImGuiCorner_TopLeft|ImGuiCorner_TopRight);
4268
4269 // Menu bar
4270 if (flags & ImGuiWindowFlags_MenuBar)
4271 {
4272 ImRect menu_bar_rect = window->MenuBarRect();
4273 if (flags & ImGuiWindowFlags_ShowBorders)
4274 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border));
4275 window->DrawList->AddRectFilled(menu_bar_rect.GetTL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImGuiCorner_TopLeft|ImGuiCorner_TopRight);
4276 }
4277
4278 // Scrollbars
4279 if (window->ScrollbarX)
4280 Scrollbar(window, true);
4281 if (window->ScrollbarY)
4282 Scrollbar(window, false);
4283
4284 // Render resize grip
4285 // (after the input handling so we don't have a frame of latency)
4286 if (!(flags & ImGuiWindowFlags_NoResize))
4287 {
4288 const ImVec2 br = window->Rect().GetBR();
4289 window->DrawList->PathLineTo(br + ImVec2(-resize_corner_size, -window->BorderSize));
4290 window->DrawList->PathLineTo(br + ImVec2(-window->BorderSize, -resize_corner_size));
4291 window->DrawList->PathArcToFast(ImVec2(br.x - window_rounding - window->BorderSize, br.y - window_rounding - window->BorderSize), window_rounding, 0, 3);
4292 window->DrawList->PathFillConvex(resize_col);
4293 }
4294
4295 // Borders
4296 if (flags & ImGuiWindowFlags_ShowBorders)
4297 {
4298 window->DrawList->AddRect(window->Pos+ImVec2(1,1), window->Pos+window->Size+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), window_rounding);
4299 window->DrawList->AddRect(window->Pos, window->Pos+window->Size, GetColorU32(ImGuiCol_Border), window_rounding);
4300 if (!(flags & ImGuiWindowFlags_NoTitleBar))
4301 window->DrawList->AddLine(title_bar_rect.GetBL()+ImVec2(1,0), title_bar_rect.GetBR()-ImVec2(1,0), GetColorU32(ImGuiCol_Border));
4302 }
4303 }
4304
4305 // Update ContentsRegionMax. All the variable it depends on are set above in this function.
4306 window->ContentsRegionRect.Min.x = -window->Scroll.x + window->WindowPadding.x;
4307 window->ContentsRegionRect.Min.y = -window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
4308 window->ContentsRegionRect.Max.x = -window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x));
4309 window->ContentsRegionRect.Max.y = -window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y));
4310
4311 // Setup drawing context
4312 window->DC.IndentX = 0.0f + window->WindowPadding.x - window->Scroll.x;
4313 window->DC.GroupOffsetX = 0.0f;
4314 window->DC.ColumnsOffsetX = 0.0f;
4315 window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.IndentX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y);
4316 window->DC.CursorPos = window->DC.CursorStartPos;
4317 window->DC.CursorPosPrevLine = window->DC.CursorPos;
4318 window->DC.CursorMaxPos = window->DC.CursorStartPos;
4319 window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f;
4320 window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
4321 window->DC.MenuBarAppending = false;
4322 window->DC.MenuBarOffsetX = ImMax(window->WindowPadding.x, style.ItemSpacing.x);
4323 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
4324 window->DC.ChildWindows.resize(0);
4325 window->DC.LayoutType = ImGuiLayoutType_Vertical;
4326 window->DC.ItemWidth = window->ItemWidthDefault;
4327 window->DC.TextWrapPos = -1.0f; // disabled
4328 window->DC.AllowKeyboardFocus = true;
4329 window->DC.ButtonRepeat = false;
4330 window->DC.ItemWidthStack.resize(0);
4331 window->DC.AllowKeyboardFocusStack.resize(0);
4332 window->DC.ButtonRepeatStack.resize(0);
4333 window->DC.TextWrapPosStack.resize(0);
4334 window->DC.ColumnsCurrent = 0;
4335 window->DC.ColumnsCount = 1;
4336 window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
4337 window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.ColumnsStartPosY;
4338 window->DC.TreeDepth = 0;
4339 window->DC.StateStorage = &window->StateStorage;
4340 window->DC.GroupStack.resize(0);
4341 window->DC.ColorEditMode = ImGuiColorEditMode_UserSelect;
4342 window->MenuColumns.Update(3, style.ItemSpacing.x, !window_was_active);
4343
4344 if (window->AutoFitFramesX > 0)
4345 window->AutoFitFramesX--;
4346 if (window->AutoFitFramesY > 0)
4347 window->AutoFitFramesY--;
4348
4349 // New windows appears in front (we need to do that AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
4350 if (!window_was_active && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
4351 if (!(flags & (ImGuiWindowFlags_ChildWindow|ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
4352 FocusWindow(window);
4353
4354 // Title bar
4355 if (!(flags & ImGuiWindowFlags_NoTitleBar))
4356 {
4357 if (p_open != NULL)
4358 {
4359 const float pad = 2.0f;
4360 const float rad = (window->TitleBarHeight() - pad*2.0f) * 0.5f;
4361 if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad))
4362 *p_open = false;
4363 }
4364
4365 const ImVec2 text_size = CalcTextSize(name, NULL, true);
4366 if (!(flags & ImGuiWindowFlags_NoCollapse))
4367 RenderCollapseTriangle(window->Pos + style.FramePadding, !window->Collapsed, 1.0f);
4368
4369 ImVec2 text_min = window->Pos;
4370 ImVec2 text_max = window->Pos + ImVec2(window->Size.x, style.FramePadding.y*2 + text_size.y);
4371 ImRect clip_rect;
4372 clip_rect.Max = ImVec2(window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x), text_max.y); // Match the size of CloseWindowButton()
4373 float pad_left = (flags & ImGuiWindowFlags_NoCollapse) == 0 ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x;
4374 float pad_right = (p_open != NULL) ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x;
4375 if (style.WindowTitleAlign.x > 0.0f) pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x);
4376 text_min.x += pad_left;
4377 text_max.x -= pad_right;
4378 clip_rect.Min = ImVec2(text_min.x, window->Pos.y);
4379 RenderTextClipped(text_min, text_max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect);
4380 }
4381
4382 // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
4383 window->WindowRectClipped = window->Rect();
4384 window->WindowRectClipped.Clip(window->ClipRect);
4385
4386 // Pressing CTRL+C while holding on a window copy its content to the clipboard
4387 // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope.
4388 // Maybe we can support CTRL+C on every element?
4389 /*
4390 if (g.ActiveId == move_id)
4391 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
4392 ImGui::LogToClipboard();
4393 */
4394 }
4395
4396 // Inner clipping rectangle
4397 // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
4398 // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior.
4399 const ImRect title_bar_rect = window->TitleBarRect();
4400 const float border_size = window->BorderSize;
4401 ImRect clip_rect; // Force round to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
4402 clip_rect.Min.x = ImFloor(0.5f + title_bar_rect.Min.x + ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
4403 clip_rect.Min.y = ImFloor(0.5f + title_bar_rect.Max.y + window->MenuBarHeight() + border_size);
4404 clip_rect.Max.x = ImFloor(0.5f + window->Pos.x + window->Size.x - window->ScrollbarSizes.x - ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
4405 clip_rect.Max.y = ImFloor(0.5f + window->Pos.y + window->Size.y - window->ScrollbarSizes.y - border_size);
4406 PushClipRect(clip_rect.Min, clip_rect.Max, true);
4407
4408 // Clear 'accessed' flag last thing
4409 if (first_begin_of_the_frame)
4410 window->Accessed = false;
4411 window->BeginCount++;
4412 g.SetNextWindowSizeConstraint = false;
4413
4414 // Child window can be out of sight and have "negative" clip windows.
4415 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar).
4416 if (flags & ImGuiWindowFlags_ChildWindow)
4417 {
4418 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
4419 window->Collapsed = parent_window && parent_window->Collapsed;
4420
4421 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
4422 window->Collapsed |= (window->WindowRectClipped.Min.x >= window->WindowRectClipped.Max.x || window->WindowRectClipped.Min.y >= window->WindowRectClipped.Max.y);
4423
4424 // We also hide the window from rendering because we've already added its border to the command list.
4425 // (we could perform the check earlier in the function but it is simpler at this point)
4426 if (window->Collapsed)
4427 window->Active = false;
4428 }
4429 if (style.Alpha <= 0.0f)
4430 window->Active = false;
4431
4432 // Return false if we don't intend to display anything to allow user to perform an early out optimization
4433 window->SkipItems = (window->Collapsed || !window->Active) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0;
4434 return !window->SkipItems;
4435 }
4436
End()4437 void ImGui::End()
4438 {
4439 ImGuiContext& g = *GImGui;
4440 ImGuiWindow* window = g.CurrentWindow;
4441
4442 if (window->DC.ColumnsCount != 1) // close columns set if any is open
4443 Columns(1, "#CLOSECOLUMNS");
4444 PopClipRect(); // inner window clip rectangle
4445
4446 // Stop logging
4447 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
4448 LogFinish();
4449
4450 // Pop
4451 // NB: we don't clear 'window->RootWindow'. The pointer is allowed to live until the next call to Begin().
4452 g.CurrentWindowStack.pop_back();
4453 if (window->Flags & ImGuiWindowFlags_Popup)
4454 g.CurrentPopupStack.pop_back();
4455 CheckStacksSize(window, false);
4456 SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
4457 }
4458
4459 // Vertical scrollbar
4460 // The entire piece of code below is rather confusing because:
4461 // - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
4462 // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar
4463 // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.
Scrollbar(ImGuiWindow * window,bool horizontal)4464 static void Scrollbar(ImGuiWindow* window, bool horizontal)
4465 {
4466 ImGuiContext& g = *GImGui;
4467 const ImGuiStyle& style = g.Style;
4468 const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY");
4469
4470 // Render background
4471 bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX);
4472 float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f;
4473 const ImRect window_rect = window->Rect();
4474 const float border_size = window->BorderSize;
4475 ImRect bb = horizontal
4476 ? ImRect(window->Pos.x + border_size, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - other_scrollbar_size_w - border_size, window_rect.Max.y - border_size)
4477 : ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size);
4478 if (!horizontal)
4479 bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f);
4480 if (bb.GetWidth() <= 0.0f || bb.GetHeight() <= 0.0f)
4481 return;
4482
4483 float window_rounding = (window->Flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding;
4484 int window_rounding_corners;
4485 if (horizontal)
4486 window_rounding_corners = ImGuiCorner_BottomLeft | (other_scrollbar ? 0 : ImGuiCorner_BottomRight);
4487 else
4488 window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImGuiCorner_TopRight : 0) | (other_scrollbar ? 0 : ImGuiCorner_BottomRight);
4489 window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_ScrollbarBg), window_rounding, window_rounding_corners);
4490 bb.Reduce(ImVec2(ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f)));
4491
4492 // V denote the main axis of the scrollbar
4493 float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight();
4494 float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y;
4495 float win_size_avail_v = (horizontal ? window->Size.x : window->Size.y) - other_scrollbar_size_w;
4496 float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y;
4497
4498 // The grabable box size generally represent the amount visible (vs the total scrollable amount)
4499 // But we maintain a minimum size in pixel to allow for the user to still aim inside.
4500 const float grab_h_pixels = ImMin(ImMax(scrollbar_size_v * ImSaturate(win_size_avail_v / ImMax(win_size_contents_v, win_size_avail_v)), style.GrabMinSize), scrollbar_size_v);
4501 const float grab_h_norm = grab_h_pixels / scrollbar_size_v;
4502
4503 // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().
4504 bool held = false;
4505 bool hovered = false;
4506 const bool previously_held = (g.ActiveId == id);
4507 ImGui::ButtonBehavior(bb, id, &hovered, &held);
4508
4509 float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v);
4510 float scroll_ratio = ImSaturate(scroll_v / scroll_max);
4511 float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
4512 if (held && grab_h_norm < 1.0f)
4513 {
4514 float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y;
4515 float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
4516 float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y;
4517
4518 // Click position in scrollbar normalized space (0.0f->1.0f)
4519 const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
4520 ImGui::SetHoveredID(id);
4521
4522 bool seek_absolute = false;
4523 if (!previously_held)
4524 {
4525 // On initial click calculate the distance between mouse and the center of the grab
4526 if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm)
4527 {
4528 *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
4529 }
4530 else
4531 {
4532 seek_absolute = true;
4533 *click_delta_to_grab_center_v = 0.0f;
4534 }
4535 }
4536
4537 // Apply scroll
4538 // It is ok to modify Scroll here because we are being called in Begin() after the calculation of SizeContents and before setting up our starting position
4539 const float scroll_v_norm = ImSaturate((clicked_v_norm - *click_delta_to_grab_center_v - grab_h_norm*0.5f) / (1.0f - grab_h_norm));
4540 scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v));
4541 if (horizontal)
4542 window->Scroll.x = scroll_v;
4543 else
4544 window->Scroll.y = scroll_v;
4545
4546 // Update values for rendering
4547 scroll_ratio = ImSaturate(scroll_v / scroll_max);
4548 grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
4549
4550 // Update distance to grab now that we have seeked and saturated
4551 if (seek_absolute)
4552 *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
4553 }
4554
4555 // Render
4556 const ImU32 grab_col = ImGui::GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab);
4557 if (horizontal)
4558 window->DrawList->AddRectFilled(ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y), ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y), grab_col, style.ScrollbarRounding);
4559 else
4560 window->DrawList->AddRectFilled(ImVec2(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm)), ImVec2(bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels), grab_col, style.ScrollbarRounding);
4561 }
4562
4563 // Moving window to front of display (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)4564 void ImGui::FocusWindow(ImGuiWindow* window)
4565 {
4566 ImGuiContext& g = *GImGui;
4567
4568 // Always mark the window we passed as focused. This is used for keyboard interactions such as tabbing.
4569 g.FocusedWindow = window;
4570
4571 // Passing NULL allow to disable keyboard focus
4572 if (!window)
4573 return;
4574
4575 // And move its root window to the top of the pile
4576 if (window->RootWindow)
4577 window = window->RootWindow;
4578
4579 // Steal focus on active widgets
4580 if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
4581 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
4582 ClearActiveID();
4583
4584 // Bring to front
4585 if ((window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) || g.Windows.back() == window)
4586 return;
4587 for (int i = 0; i < g.Windows.Size; i++)
4588 if (g.Windows[i] == window)
4589 {
4590 g.Windows.erase(g.Windows.begin() + i);
4591 break;
4592 }
4593 g.Windows.push_back(window);
4594 }
4595
PushItemWidth(float item_width)4596 void ImGui::PushItemWidth(float item_width)
4597 {
4598 ImGuiWindow* window = GetCurrentWindow();
4599 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
4600 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
4601 }
4602
PushMultiItemsWidths(int components,float w_full)4603 static void PushMultiItemsWidths(int components, float w_full)
4604 {
4605 ImGuiWindow* window = ImGui::GetCurrentWindow();
4606 const ImGuiStyle& style = GImGui->Style;
4607 if (w_full <= 0.0f)
4608 w_full = ImGui::CalcItemWidth();
4609 const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
4610 const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
4611 window->DC.ItemWidthStack.push_back(w_item_last);
4612 for (int i = 0; i < components-1; i++)
4613 window->DC.ItemWidthStack.push_back(w_item_one);
4614 window->DC.ItemWidth = window->DC.ItemWidthStack.back();
4615 }
4616
PopItemWidth()4617 void ImGui::PopItemWidth()
4618 {
4619 ImGuiWindow* window = GetCurrentWindow();
4620 window->DC.ItemWidthStack.pop_back();
4621 window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
4622 }
4623
CalcItemWidth()4624 float ImGui::CalcItemWidth()
4625 {
4626 ImGuiWindow* window = GetCurrentWindowRead();
4627 float w = window->DC.ItemWidth;
4628 if (w < 0.0f)
4629 {
4630 // Align to a right-side limit. We include 1 frame padding in the calculation because this is how the width is always used (we add 2 frame padding to it), but we could move that responsibility to the widget as well.
4631 float width_to_right_edge = GetContentRegionAvail().x;
4632 w = ImMax(1.0f, width_to_right_edge + w);
4633 }
4634 w = (float)(int)w;
4635 return w;
4636 }
4637
GetDefaultFont()4638 static ImFont* GetDefaultFont()
4639 {
4640 ImGuiContext& g = *GImGui;
4641 return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0];
4642 }
4643
SetCurrentFont(ImFont * font)4644 static void SetCurrentFont(ImFont* font)
4645 {
4646 ImGuiContext& g = *GImGui;
4647 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
4648 IM_ASSERT(font->Scale > 0.0f);
4649 g.Font = font;
4650 g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale;
4651 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
4652 g.FontTexUvWhitePixel = g.Font->ContainerAtlas->TexUvWhitePixel;
4653 }
4654
PushFont(ImFont * font)4655 void ImGui::PushFont(ImFont* font)
4656 {
4657 ImGuiContext& g = *GImGui;
4658 if (!font)
4659 font = GetDefaultFont();
4660 SetCurrentFont(font);
4661 g.FontStack.push_back(font);
4662 g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
4663 }
4664
PopFont()4665 void ImGui::PopFont()
4666 {
4667 ImGuiContext& g = *GImGui;
4668 g.CurrentWindow->DrawList->PopTextureID();
4669 g.FontStack.pop_back();
4670 SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
4671 }
4672
PushAllowKeyboardFocus(bool allow_keyboard_focus)4673 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
4674 {
4675 ImGuiWindow* window = GetCurrentWindow();
4676 window->DC.AllowKeyboardFocus = allow_keyboard_focus;
4677 window->DC.AllowKeyboardFocusStack.push_back(allow_keyboard_focus);
4678 }
4679
PopAllowKeyboardFocus()4680 void ImGui::PopAllowKeyboardFocus()
4681 {
4682 ImGuiWindow* window = GetCurrentWindow();
4683 window->DC.AllowKeyboardFocusStack.pop_back();
4684 window->DC.AllowKeyboardFocus = window->DC.AllowKeyboardFocusStack.empty() ? true : window->DC.AllowKeyboardFocusStack.back();
4685 }
4686
PushButtonRepeat(bool repeat)4687 void ImGui::PushButtonRepeat(bool repeat)
4688 {
4689 ImGuiWindow* window = GetCurrentWindow();
4690 window->DC.ButtonRepeat = repeat;
4691 window->DC.ButtonRepeatStack.push_back(repeat);
4692 }
4693
PopButtonRepeat()4694 void ImGui::PopButtonRepeat()
4695 {
4696 ImGuiWindow* window = GetCurrentWindow();
4697 window->DC.ButtonRepeatStack.pop_back();
4698 window->DC.ButtonRepeat = window->DC.ButtonRepeatStack.empty() ? false : window->DC.ButtonRepeatStack.back();
4699 }
4700
PushTextWrapPos(float wrap_pos_x)4701 void ImGui::PushTextWrapPos(float wrap_pos_x)
4702 {
4703 ImGuiWindow* window = GetCurrentWindow();
4704 window->DC.TextWrapPos = wrap_pos_x;
4705 window->DC.TextWrapPosStack.push_back(wrap_pos_x);
4706 }
4707
PopTextWrapPos()4708 void ImGui::PopTextWrapPos()
4709 {
4710 ImGuiWindow* window = GetCurrentWindow();
4711 window->DC.TextWrapPosStack.pop_back();
4712 window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
4713 }
4714
PushStyleColor(ImGuiCol idx,const ImVec4 & col)4715 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
4716 {
4717 ImGuiContext& g = *GImGui;
4718 ImGuiColMod backup;
4719 backup.Col = idx;
4720 backup.BackupValue = g.Style.Colors[idx];
4721 g.ColorModifiers.push_back(backup);
4722 g.Style.Colors[idx] = col;
4723 }
4724
PopStyleColor(int count)4725 void ImGui::PopStyleColor(int count)
4726 {
4727 ImGuiContext& g = *GImGui;
4728 while (count > 0)
4729 {
4730 ImGuiColMod& backup = g.ColorModifiers.back();
4731 g.Style.Colors[backup.Col] = backup.BackupValue;
4732 g.ColorModifiers.pop_back();
4733 count--;
4734 }
4735 }
4736
4737 struct ImGuiStyleVarInfo
4738 {
4739 ImGuiDataType Type;
4740 ImU32 Offset;
GetVarPtrImGuiStyleVarInfo4741 void* GetVarPtr() const { return (void*)((unsigned char*)&GImGui->Style + Offset); }
4742 };
4743
4744 static const ImGuiStyleVarInfo GStyleVarInfo[ImGuiStyleVar_Count_] =
4745 {
4746 { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) },
4747 { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) },
4748 { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) },
4749 { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) },
4750 { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildWindowRounding) },
4751 { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) },
4752 { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) },
4753 { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) },
4754 { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) },
4755 { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) },
4756 { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },
4757 { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },
4758 };
4759
GetStyleVarInfo(ImGuiStyleVar idx)4760 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
4761 {
4762 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_Count_);
4763 return &GStyleVarInfo[idx];
4764 }
4765
PushStyleVar(ImGuiStyleVar idx,float val)4766 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
4767 {
4768 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
4769 if (var_info->Type == ImGuiDataType_Float)
4770 {
4771 float* pvar = (float*)var_info->GetVarPtr();
4772 GImGui->StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
4773 *pvar = val;
4774 return;
4775 }
4776 IM_ASSERT(0); // Called function with wrong-type? Variable is not a float.
4777 }
4778
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)4779 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
4780 {
4781 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
4782 if (var_info->Type == ImGuiDataType_Float2)
4783 {
4784 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr();
4785 GImGui->StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
4786 *pvar = val;
4787 return;
4788 }
4789 IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2.
4790 }
4791
PopStyleVar(int count)4792 void ImGui::PopStyleVar(int count)
4793 {
4794 ImGuiContext& g = *GImGui;
4795 while (count > 0)
4796 {
4797 ImGuiStyleMod& backup = g.StyleModifiers.back();
4798 const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
4799 if (info->Type == ImGuiDataType_Float) (*(float*)info->GetVarPtr()) = backup.BackupFloat[0];
4800 else if (info->Type == ImGuiDataType_Float2) (*(ImVec2*)info->GetVarPtr()) = ImVec2(backup.BackupFloat[0], backup.BackupFloat[1]);
4801 else if (info->Type == ImGuiDataType_Int) (*(int*)info->GetVarPtr()) = backup.BackupInt[0];
4802 g.StyleModifiers.pop_back();
4803 count--;
4804 }
4805 }
4806
GetStyleColName(ImGuiCol idx)4807 const char* ImGui::GetStyleColName(ImGuiCol idx)
4808 {
4809 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
4810 switch (idx)
4811 {
4812 case ImGuiCol_Text: return "Text";
4813 case ImGuiCol_TextDisabled: return "TextDisabled";
4814 case ImGuiCol_WindowBg: return "WindowBg";
4815 case ImGuiCol_ChildWindowBg: return "ChildWindowBg";
4816 case ImGuiCol_PopupBg: return "PopupBg";
4817 case ImGuiCol_Border: return "Border";
4818 case ImGuiCol_BorderShadow: return "BorderShadow";
4819 case ImGuiCol_FrameBg: return "FrameBg";
4820 case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
4821 case ImGuiCol_FrameBgActive: return "FrameBgActive";
4822 case ImGuiCol_TitleBg: return "TitleBg";
4823 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
4824 case ImGuiCol_TitleBgActive: return "TitleBgActive";
4825 case ImGuiCol_MenuBarBg: return "MenuBarBg";
4826 case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
4827 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
4828 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
4829 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
4830 case ImGuiCol_ComboBg: return "ComboBg";
4831 case ImGuiCol_CheckMark: return "CheckMark";
4832 case ImGuiCol_SliderGrab: return "SliderGrab";
4833 case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
4834 case ImGuiCol_Button: return "Button";
4835 case ImGuiCol_ButtonHovered: return "ButtonHovered";
4836 case ImGuiCol_ButtonActive: return "ButtonActive";
4837 case ImGuiCol_Header: return "Header";
4838 case ImGuiCol_HeaderHovered: return "HeaderHovered";
4839 case ImGuiCol_HeaderActive: return "HeaderActive";
4840 case ImGuiCol_Column: return "Column";
4841 case ImGuiCol_ColumnHovered: return "ColumnHovered";
4842 case ImGuiCol_ColumnActive: return "ColumnActive";
4843 case ImGuiCol_ResizeGrip: return "ResizeGrip";
4844 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
4845 case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
4846 case ImGuiCol_CloseButton: return "CloseButton";
4847 case ImGuiCol_CloseButtonHovered: return "CloseButtonHovered";
4848 case ImGuiCol_CloseButtonActive: return "CloseButtonActive";
4849 case ImGuiCol_PlotLines: return "PlotLines";
4850 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
4851 case ImGuiCol_PlotHistogram: return "PlotHistogram";
4852 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
4853 case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
4854 case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening";
4855 }
4856 IM_ASSERT(0);
4857 return "Unknown";
4858 }
4859
IsWindowHovered()4860 bool ImGui::IsWindowHovered()
4861 {
4862 ImGuiContext& g = *GImGui;
4863 return g.HoveredWindow == g.CurrentWindow && IsWindowContentHoverable(g.HoveredRootWindow);
4864 }
4865
IsWindowFocused()4866 bool ImGui::IsWindowFocused()
4867 {
4868 ImGuiContext& g = *GImGui;
4869 return g.FocusedWindow == g.CurrentWindow;
4870 }
4871
IsRootWindowFocused()4872 bool ImGui::IsRootWindowFocused()
4873 {
4874 ImGuiContext& g = *GImGui;
4875 return g.FocusedWindow == g.CurrentWindow->RootWindow;
4876 }
4877
IsRootWindowOrAnyChildFocused()4878 bool ImGui::IsRootWindowOrAnyChildFocused()
4879 {
4880 ImGuiContext& g = *GImGui;
4881 return g.FocusedWindow && g.FocusedWindow->RootWindow == g.CurrentWindow->RootWindow;
4882 }
4883
IsRootWindowOrAnyChildHovered()4884 bool ImGui::IsRootWindowOrAnyChildHovered()
4885 {
4886 ImGuiContext& g = *GImGui;
4887 return g.HoveredRootWindow && (g.HoveredRootWindow == g.CurrentWindow->RootWindow) && IsWindowContentHoverable(g.HoveredRootWindow);
4888 }
4889
GetWindowWidth()4890 float ImGui::GetWindowWidth()
4891 {
4892 ImGuiWindow* window = GImGui->CurrentWindow;
4893 return window->Size.x;
4894 }
4895
GetWindowHeight()4896 float ImGui::GetWindowHeight()
4897 {
4898 ImGuiWindow* window = GImGui->CurrentWindow;
4899 return window->Size.y;
4900 }
4901
GetWindowPos()4902 ImVec2 ImGui::GetWindowPos()
4903 {
4904 ImGuiContext& g = *GImGui;
4905 ImGuiWindow* window = g.CurrentWindow;
4906 return window->Pos;
4907 }
4908
SetWindowScrollY(ImGuiWindow * window,float new_scroll_y)4909 static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
4910 {
4911 window->DC.CursorMaxPos.y += window->Scroll.y;
4912 window->Scroll.y = new_scroll_y;
4913 window->DC.CursorMaxPos.y -= window->Scroll.y;
4914 }
4915
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiSetCond cond)4916 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond)
4917 {
4918 // Test condition (NB: bit 0 is always true) and clear flags for next time
4919 if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
4920 return;
4921 window->SetWindowPosAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
4922 window->SetWindowPosCenterWanted = false;
4923
4924 // Set
4925 const ImVec2 old_pos = window->Pos;
4926 window->PosFloat = pos;
4927 window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
4928 window->DC.CursorPos += (window->Pos - old_pos); // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor
4929 window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected.
4930 }
4931
SetWindowPos(const ImVec2 & pos,ImGuiSetCond cond)4932 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiSetCond cond)
4933 {
4934 ImGuiWindow* window = GetCurrentWindowRead();
4935 SetWindowPos(window, pos, cond);
4936 }
4937
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiSetCond cond)4938 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiSetCond cond)
4939 {
4940 if (ImGuiWindow* window = FindWindowByName(name))
4941 SetWindowPos(window, pos, cond);
4942 }
4943
GetWindowSize()4944 ImVec2 ImGui::GetWindowSize()
4945 {
4946 ImGuiWindow* window = GetCurrentWindowRead();
4947 return window->Size;
4948 }
4949
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiSetCond cond)4950 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond)
4951 {
4952 // Test condition (NB: bit 0 is always true) and clear flags for next time
4953 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
4954 return;
4955 window->SetWindowSizeAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
4956
4957 // Set
4958 if (size.x > 0.0f)
4959 {
4960 window->AutoFitFramesX = 0;
4961 window->SizeFull.x = size.x;
4962 }
4963 else
4964 {
4965 window->AutoFitFramesX = 2;
4966 window->AutoFitOnlyGrows = false;
4967 }
4968 if (size.y > 0.0f)
4969 {
4970 window->AutoFitFramesY = 0;
4971 window->SizeFull.y = size.y;
4972 }
4973 else
4974 {
4975 window->AutoFitFramesY = 2;
4976 window->AutoFitOnlyGrows = false;
4977 }
4978 }
4979
SetWindowSize(const ImVec2 & size,ImGuiSetCond cond)4980 void ImGui::SetWindowSize(const ImVec2& size, ImGuiSetCond cond)
4981 {
4982 SetWindowSize(GImGui->CurrentWindow, size, cond);
4983 }
4984
SetWindowSize(const char * name,const ImVec2 & size,ImGuiSetCond cond)4985 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiSetCond cond)
4986 {
4987 ImGuiWindow* window = FindWindowByName(name);
4988 if (window)
4989 SetWindowSize(window, size, cond);
4990 }
4991
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiSetCond cond)4992 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiSetCond cond)
4993 {
4994 // Test condition (NB: bit 0 is always true) and clear flags for next time
4995 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
4996 return;
4997 window->SetWindowCollapsedAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
4998
4999 // Set
5000 window->Collapsed = collapsed;
5001 }
5002
SetWindowCollapsed(bool collapsed,ImGuiSetCond cond)5003 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiSetCond cond)
5004 {
5005 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
5006 }
5007
IsWindowCollapsed()5008 bool ImGui::IsWindowCollapsed()
5009 {
5010 return GImGui->CurrentWindow->Collapsed;
5011 }
5012
SetWindowCollapsed(const char * name,bool collapsed,ImGuiSetCond cond)5013 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiSetCond cond)
5014 {
5015 ImGuiWindow* window = FindWindowByName(name);
5016 if (window)
5017 SetWindowCollapsed(window, collapsed, cond);
5018 }
5019
SetWindowFocus()5020 void ImGui::SetWindowFocus()
5021 {
5022 FocusWindow(GImGui->CurrentWindow);
5023 }
5024
SetWindowFocus(const char * name)5025 void ImGui::SetWindowFocus(const char* name)
5026 {
5027 if (name)
5028 {
5029 if (ImGuiWindow* window = FindWindowByName(name))
5030 FocusWindow(window);
5031 }
5032 else
5033 {
5034 FocusWindow(NULL);
5035 }
5036 }
5037
SetNextWindowPos(const ImVec2 & pos,ImGuiSetCond cond)5038 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiSetCond cond)
5039 {
5040 ImGuiContext& g = *GImGui;
5041 g.SetNextWindowPosVal = pos;
5042 g.SetNextWindowPosCond = cond ? cond : ImGuiSetCond_Always;
5043 }
5044
SetNextWindowPosCenter(ImGuiSetCond cond)5045 void ImGui::SetNextWindowPosCenter(ImGuiSetCond cond)
5046 {
5047 ImGuiContext& g = *GImGui;
5048 g.SetNextWindowPosVal = ImVec2(-FLT_MAX, -FLT_MAX);
5049 g.SetNextWindowPosCond = cond ? cond : ImGuiSetCond_Always;
5050 }
5051
SetNextWindowSize(const ImVec2 & size,ImGuiSetCond cond)5052 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiSetCond cond)
5053 {
5054 ImGuiContext& g = *GImGui;
5055 g.SetNextWindowSizeVal = size;
5056 g.SetNextWindowSizeCond = cond ? cond : ImGuiSetCond_Always;
5057 }
5058
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeConstraintCallback custom_callback,void * custom_callback_user_data)5059 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeConstraintCallback custom_callback, void* custom_callback_user_data)
5060 {
5061 ImGuiContext& g = *GImGui;
5062 g.SetNextWindowSizeConstraint = true;
5063 g.SetNextWindowSizeConstraintRect = ImRect(size_min, size_max);
5064 g.SetNextWindowSizeConstraintCallback = custom_callback;
5065 g.SetNextWindowSizeConstraintCallbackUserData = custom_callback_user_data;
5066 }
5067
SetNextWindowContentSize(const ImVec2 & size)5068 void ImGui::SetNextWindowContentSize(const ImVec2& size)
5069 {
5070 ImGuiContext& g = *GImGui;
5071 g.SetNextWindowContentSizeVal = size;
5072 g.SetNextWindowContentSizeCond = ImGuiSetCond_Always;
5073 }
5074
SetNextWindowContentWidth(float width)5075 void ImGui::SetNextWindowContentWidth(float width)
5076 {
5077 ImGuiContext& g = *GImGui;
5078 g.SetNextWindowContentSizeVal = ImVec2(width, g.SetNextWindowContentSizeCond ? g.SetNextWindowContentSizeVal.y : 0.0f);
5079 g.SetNextWindowContentSizeCond = ImGuiSetCond_Always;
5080 }
5081
SetNextWindowCollapsed(bool collapsed,ImGuiSetCond cond)5082 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiSetCond cond)
5083 {
5084 ImGuiContext& g = *GImGui;
5085 g.SetNextWindowCollapsedVal = collapsed;
5086 g.SetNextWindowCollapsedCond = cond ? cond : ImGuiSetCond_Always;
5087 }
5088
SetNextWindowFocus()5089 void ImGui::SetNextWindowFocus()
5090 {
5091 ImGuiContext& g = *GImGui;
5092 g.SetNextWindowFocus = true;
5093 }
5094
5095 // In window space (not screen space!)
GetContentRegionMax()5096 ImVec2 ImGui::GetContentRegionMax()
5097 {
5098 ImGuiWindow* window = GetCurrentWindowRead();
5099 ImVec2 mx = window->ContentsRegionRect.Max;
5100 if (window->DC.ColumnsCount != 1)
5101 mx.x = GetColumnOffset(window->DC.ColumnsCurrent + 1) - window->WindowPadding.x;
5102 return mx;
5103 }
5104
GetContentRegionAvail()5105 ImVec2 ImGui::GetContentRegionAvail()
5106 {
5107 ImGuiWindow* window = GetCurrentWindowRead();
5108 return GetContentRegionMax() - (window->DC.CursorPos - window->Pos);
5109 }
5110
GetContentRegionAvailWidth()5111 float ImGui::GetContentRegionAvailWidth()
5112 {
5113 return GetContentRegionAvail().x;
5114 }
5115
5116 // In window space (not screen space!)
GetWindowContentRegionMin()5117 ImVec2 ImGui::GetWindowContentRegionMin()
5118 {
5119 ImGuiWindow* window = GetCurrentWindowRead();
5120 return window->ContentsRegionRect.Min;
5121 }
5122
GetWindowContentRegionMax()5123 ImVec2 ImGui::GetWindowContentRegionMax()
5124 {
5125 ImGuiWindow* window = GetCurrentWindowRead();
5126 return window->ContentsRegionRect.Max;
5127 }
5128
GetWindowContentRegionWidth()5129 float ImGui::GetWindowContentRegionWidth()
5130 {
5131 ImGuiWindow* window = GetCurrentWindowRead();
5132 return window->ContentsRegionRect.Max.x - window->ContentsRegionRect.Min.x;
5133 }
5134
GetTextLineHeight()5135 float ImGui::GetTextLineHeight()
5136 {
5137 ImGuiContext& g = *GImGui;
5138 return g.FontSize;
5139 }
5140
GetTextLineHeightWithSpacing()5141 float ImGui::GetTextLineHeightWithSpacing()
5142 {
5143 ImGuiContext& g = *GImGui;
5144 return g.FontSize + g.Style.ItemSpacing.y;
5145 }
5146
GetItemsLineHeightWithSpacing()5147 float ImGui::GetItemsLineHeightWithSpacing()
5148 {
5149 ImGuiContext& g = *GImGui;
5150 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
5151 }
5152
GetWindowDrawList()5153 ImDrawList* ImGui::GetWindowDrawList()
5154 {
5155 ImGuiWindow* window = GetCurrentWindow();
5156 return window->DrawList;
5157 }
5158
GetFont()5159 ImFont* ImGui::GetFont()
5160 {
5161 return GImGui->Font;
5162 }
5163
GetFontSize()5164 float ImGui::GetFontSize()
5165 {
5166 return GImGui->FontSize;
5167 }
5168
GetFontTexUvWhitePixel()5169 ImVec2 ImGui::GetFontTexUvWhitePixel()
5170 {
5171 return GImGui->FontTexUvWhitePixel;
5172 }
5173
SetWindowFontScale(float scale)5174 void ImGui::SetWindowFontScale(float scale)
5175 {
5176 ImGuiContext& g = *GImGui;
5177 ImGuiWindow* window = GetCurrentWindow();
5178 window->FontWindowScale = scale;
5179 g.FontSize = window->CalcFontSize();
5180 }
5181
5182 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
5183 // Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'.
GetCursorPos()5184 ImVec2 ImGui::GetCursorPos()
5185 {
5186 ImGuiWindow* window = GetCurrentWindowRead();
5187 return window->DC.CursorPos - window->Pos + window->Scroll;
5188 }
5189
GetCursorPosX()5190 float ImGui::GetCursorPosX()
5191 {
5192 ImGuiWindow* window = GetCurrentWindowRead();
5193 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
5194 }
5195
GetCursorPosY()5196 float ImGui::GetCursorPosY()
5197 {
5198 ImGuiWindow* window = GetCurrentWindowRead();
5199 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
5200 }
5201
SetCursorPos(const ImVec2 & local_pos)5202 void ImGui::SetCursorPos(const ImVec2& local_pos)
5203 {
5204 ImGuiWindow* window = GetCurrentWindow();
5205 window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
5206 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
5207 }
5208
SetCursorPosX(float x)5209 void ImGui::SetCursorPosX(float x)
5210 {
5211 ImGuiWindow* window = GetCurrentWindow();
5212 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
5213 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
5214 }
5215
SetCursorPosY(float y)5216 void ImGui::SetCursorPosY(float y)
5217 {
5218 ImGuiWindow* window = GetCurrentWindow();
5219 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
5220 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
5221 }
5222
GetCursorStartPos()5223 ImVec2 ImGui::GetCursorStartPos()
5224 {
5225 ImGuiWindow* window = GetCurrentWindowRead();
5226 return window->DC.CursorStartPos - window->Pos;
5227 }
5228
GetCursorScreenPos()5229 ImVec2 ImGui::GetCursorScreenPos()
5230 {
5231 ImGuiWindow* window = GetCurrentWindowRead();
5232 return window->DC.CursorPos;
5233 }
5234
SetCursorScreenPos(const ImVec2 & screen_pos)5235 void ImGui::SetCursorScreenPos(const ImVec2& screen_pos)
5236 {
5237 ImGuiWindow* window = GetCurrentWindow();
5238 window->DC.CursorPos = screen_pos;
5239 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
5240 }
5241
GetScrollX()5242 float ImGui::GetScrollX()
5243 {
5244 return GImGui->CurrentWindow->Scroll.x;
5245 }
5246
GetScrollY()5247 float ImGui::GetScrollY()
5248 {
5249 return GImGui->CurrentWindow->Scroll.y;
5250 }
5251
GetScrollMaxX()5252 float ImGui::GetScrollMaxX()
5253 {
5254 ImGuiWindow* window = GetCurrentWindowRead();
5255 return window->SizeContents.x - window->SizeFull.x - window->ScrollbarSizes.x;
5256 }
5257
GetScrollMaxY()5258 float ImGui::GetScrollMaxY()
5259 {
5260 ImGuiWindow* window = GetCurrentWindowRead();
5261 return window->SizeContents.y - window->SizeFull.y - window->ScrollbarSizes.y;
5262 }
5263
SetScrollX(float scroll_x)5264 void ImGui::SetScrollX(float scroll_x)
5265 {
5266 ImGuiWindow* window = GetCurrentWindow();
5267 window->ScrollTarget.x = scroll_x;
5268 window->ScrollTargetCenterRatio.x = 0.0f;
5269 }
5270
SetScrollY(float scroll_y)5271 void ImGui::SetScrollY(float scroll_y)
5272 {
5273 ImGuiWindow* window = GetCurrentWindow();
5274 window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY
5275 window->ScrollTargetCenterRatio.y = 0.0f;
5276 }
5277
SetScrollFromPosY(float pos_y,float center_y_ratio)5278 void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio)
5279 {
5280 // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
5281 ImGuiWindow* window = GetCurrentWindow();
5282 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
5283 window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y);
5284 if (center_y_ratio <= 0.0f && window->ScrollTarget.y <= window->WindowPadding.y) // Minor hack to make "scroll to top" take account of WindowPadding, else it would scroll to (WindowPadding.y - ItemSpacing.y)
5285 window->ScrollTarget.y = 0.0f;
5286 window->ScrollTargetCenterRatio.y = center_y_ratio;
5287 }
5288
5289 // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
SetScrollHere(float center_y_ratio)5290 void ImGui::SetScrollHere(float center_y_ratio)
5291 {
5292 ImGuiWindow* window = GetCurrentWindow();
5293 float target_y = window->DC.CursorPosPrevLine.y + (window->DC.PrevLineHeight * center_y_ratio) + (GImGui->Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line.
5294 SetScrollFromPosY(target_y - window->Pos.y, center_y_ratio);
5295 }
5296
SetKeyboardFocusHere(int offset)5297 void ImGui::SetKeyboardFocusHere(int offset)
5298 {
5299 ImGuiWindow* window = GetCurrentWindow();
5300 window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
5301 window->FocusIdxTabRequestNext = INT_MAX;
5302 }
5303
SetStateStorage(ImGuiStorage * tree)5304 void ImGui::SetStateStorage(ImGuiStorage* tree)
5305 {
5306 ImGuiWindow* window = GetCurrentWindow();
5307 window->DC.StateStorage = tree ? tree : &window->StateStorage;
5308 }
5309
GetStateStorage()5310 ImGuiStorage* ImGui::GetStateStorage()
5311 {
5312 ImGuiWindow* window = GetCurrentWindowRead();
5313 return window->DC.StateStorage;
5314 }
5315
TextV(const char * fmt,va_list args)5316 void ImGui::TextV(const char* fmt, va_list args)
5317 {
5318 ImGuiWindow* window = GetCurrentWindow();
5319 if (window->SkipItems)
5320 return;
5321
5322 ImGuiContext& g = *GImGui;
5323 const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5324 TextUnformatted(g.TempBuffer, text_end);
5325 }
5326
Text(const char * fmt,...)5327 void ImGui::Text(const char* fmt, ...)
5328 {
5329 va_list args;
5330 va_start(args, fmt);
5331 TextV(fmt, args);
5332 va_end(args);
5333 }
5334
TextColoredV(const ImVec4 & col,const char * fmt,va_list args)5335 void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args)
5336 {
5337 PushStyleColor(ImGuiCol_Text, col);
5338 TextV(fmt, args);
5339 PopStyleColor();
5340 }
5341
TextColored(const ImVec4 & col,const char * fmt,...)5342 void ImGui::TextColored(const ImVec4& col, const char* fmt, ...)
5343 {
5344 va_list args;
5345 va_start(args, fmt);
5346 TextColoredV(col, fmt, args);
5347 va_end(args);
5348 }
5349
TextDisabledV(const char * fmt,va_list args)5350 void ImGui::TextDisabledV(const char* fmt, va_list args)
5351 {
5352 PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]);
5353 TextV(fmt, args);
5354 PopStyleColor();
5355 }
5356
TextDisabled(const char * fmt,...)5357 void ImGui::TextDisabled(const char* fmt, ...)
5358 {
5359 va_list args;
5360 va_start(args, fmt);
5361 TextDisabledV(fmt, args);
5362 va_end(args);
5363 }
5364
TextWrappedV(const char * fmt,va_list args)5365 void ImGui::TextWrappedV(const char* fmt, va_list args)
5366 {
5367 bool need_wrap = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f); // Keep existing wrap position is one ia already set
5368 if (need_wrap) PushTextWrapPos(0.0f);
5369 TextV(fmt, args);
5370 if (need_wrap) PopTextWrapPos();
5371 }
5372
TextWrapped(const char * fmt,...)5373 void ImGui::TextWrapped(const char* fmt, ...)
5374 {
5375 va_list args;
5376 va_start(args, fmt);
5377 TextWrappedV(fmt, args);
5378 va_end(args);
5379 }
5380
TextUnformatted(const char * text,const char * text_end)5381 void ImGui::TextUnformatted(const char* text, const char* text_end)
5382 {
5383 ImGuiWindow* window = GetCurrentWindow();
5384 if (window->SkipItems)
5385 return;
5386
5387 ImGuiContext& g = *GImGui;
5388 IM_ASSERT(text != NULL);
5389 const char* text_begin = text;
5390 if (text_end == NULL)
5391 text_end = text + strlen(text); // FIXME-OPT
5392
5393 const float wrap_pos_x = window->DC.TextWrapPos;
5394 const bool wrap_enabled = wrap_pos_x >= 0.0f;
5395 if (text_end - text > 2000 && !wrap_enabled)
5396 {
5397 // Long text!
5398 // Perform manual coarse clipping to optimize for long multi-line text
5399 // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.
5400 // We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line.
5401 const char* line = text;
5402 const float line_height = GetTextLineHeight();
5403 const ImVec2 text_pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrentLineTextBaseOffset);
5404 const ImRect clip_rect = window->ClipRect;
5405 ImVec2 text_size(0,0);
5406
5407 if (text_pos.y <= clip_rect.Max.y)
5408 {
5409 ImVec2 pos = text_pos;
5410
5411 // Lines to skip (can't skip when logging text)
5412 if (!g.LogEnabled)
5413 {
5414 int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height);
5415 if (lines_skippable > 0)
5416 {
5417 int lines_skipped = 0;
5418 while (line < text_end && lines_skipped < lines_skippable)
5419 {
5420 const char* line_end = strchr(line, '\n');
5421 if (!line_end)
5422 line_end = text_end;
5423 line = line_end + 1;
5424 lines_skipped++;
5425 }
5426 pos.y += lines_skipped * line_height;
5427 }
5428 }
5429
5430 // Lines to render
5431 if (line < text_end)
5432 {
5433 ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height));
5434 while (line < text_end)
5435 {
5436 const char* line_end = strchr(line, '\n');
5437 if (IsClippedEx(line_rect, NULL, false))
5438 break;
5439
5440 const ImVec2 line_size = CalcTextSize(line, line_end, false);
5441 text_size.x = ImMax(text_size.x, line_size.x);
5442 RenderText(pos, line, line_end, false);
5443 if (!line_end)
5444 line_end = text_end;
5445 line = line_end + 1;
5446 line_rect.Min.y += line_height;
5447 line_rect.Max.y += line_height;
5448 pos.y += line_height;
5449 }
5450
5451 // Count remaining lines
5452 int lines_skipped = 0;
5453 while (line < text_end)
5454 {
5455 const char* line_end = strchr(line, '\n');
5456 if (!line_end)
5457 line_end = text_end;
5458 line = line_end + 1;
5459 lines_skipped++;
5460 }
5461 pos.y += lines_skipped * line_height;
5462 }
5463
5464 text_size.y += (pos - text_pos).y;
5465 }
5466
5467 ImRect bb(text_pos, text_pos + text_size);
5468 ItemSize(bb);
5469 ItemAdd(bb, NULL);
5470 }
5471 else
5472 {
5473 const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;
5474 const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);
5475
5476 // Account of baseline offset
5477 ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset);
5478 ImRect bb(text_pos, text_pos + text_size);
5479 ItemSize(text_size);
5480 if (!ItemAdd(bb, NULL))
5481 return;
5482
5483 // Render (we don't hide text after ## in this end-user function)
5484 RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width);
5485 }
5486 }
5487
AlignFirstTextHeightToWidgets()5488 void ImGui::AlignFirstTextHeightToWidgets()
5489 {
5490 ImGuiWindow* window = GetCurrentWindow();
5491 if (window->SkipItems)
5492 return;
5493
5494 // Declare a dummy item size to that upcoming items that are smaller will center-align on the newly expanded line height.
5495 ImGuiContext& g = *GImGui;
5496 ItemSize(ImVec2(0, g.FontSize + g.Style.FramePadding.y*2), g.Style.FramePadding.y);
5497 SameLine(0, 0);
5498 }
5499
5500 // Add a label+text combo aligned to other label+value widgets
LabelTextV(const char * label,const char * fmt,va_list args)5501 void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
5502 {
5503 ImGuiWindow* window = GetCurrentWindow();
5504 if (window->SkipItems)
5505 return;
5506
5507 ImGuiContext& g = *GImGui;
5508 const ImGuiStyle& style = g.Style;
5509 const float w = CalcItemWidth();
5510
5511 const ImVec2 label_size = CalcTextSize(label, NULL, true);
5512 const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2));
5513 const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y*2) + label_size);
5514 ItemSize(total_bb, style.FramePadding.y);
5515 if (!ItemAdd(total_bb, NULL))
5516 return;
5517
5518 // Render
5519 const char* value_text_begin = &g.TempBuffer[0];
5520 const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5521 RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f,0.5f));
5522 if (label_size.x > 0.0f)
5523 RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
5524 }
5525
LabelText(const char * label,const char * fmt,...)5526 void ImGui::LabelText(const char* label, const char* fmt, ...)
5527 {
5528 va_list args;
5529 va_start(args, fmt);
5530 LabelTextV(label, fmt, args);
5531 va_end(args);
5532 }
5533
IsWindowContentHoverable(ImGuiWindow * window)5534 static inline bool IsWindowContentHoverable(ImGuiWindow* window)
5535 {
5536 // An active popup disable hovering on other windows (apart from its own children)
5537 ImGuiContext& g = *GImGui;
5538 if (ImGuiWindow* focused_window = g.FocusedWindow)
5539 if (ImGuiWindow* focused_root_window = focused_window->RootWindow)
5540 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) != 0 && focused_root_window->WasActive && focused_root_window != window->RootWindow)
5541 return false;
5542
5543 return true;
5544 }
5545
ButtonBehavior(const ImRect & bb,ImGuiID id,bool * out_hovered,bool * out_held,ImGuiButtonFlags flags)5546 bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)
5547 {
5548 ImGuiContext& g = *GImGui;
5549 ImGuiWindow* window = GetCurrentWindow();
5550
5551 if (flags & ImGuiButtonFlags_Disabled)
5552 {
5553 if (out_hovered) *out_hovered = false;
5554 if (out_held) *out_held = false;
5555 if (g.ActiveId == id) ClearActiveID();
5556 return false;
5557 }
5558
5559 if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0)
5560 flags |= ImGuiButtonFlags_PressedOnClickRelease;
5561
5562 bool pressed = false;
5563 bool hovered = IsHovered(bb, id, (flags & ImGuiButtonFlags_FlattenChilds) != 0);
5564 if (hovered)
5565 {
5566 SetHoveredID(id);
5567 if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
5568 {
5569 // | CLICKING | HOLDING with ImGuiButtonFlags_Repeat
5570 // PressedOnClickRelease | <on release>* | <on repeat> <on repeat> .. (NOT on release) <-- MOST COMMON! (*) only if both click/release were over bounds
5571 // PressedOnClick | <on click> | <on click> <on repeat> <on repeat> ..
5572 // PressedOnRelease | <on release> | <on repeat> <on repeat> .. (NOT on release)
5573 // PressedOnDoubleClick | <on dclick> | <on dclick> <on repeat> <on repeat> ..
5574 if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0])
5575 {
5576 SetActiveID(id, window); // Hold on ID
5577 FocusWindow(window);
5578 g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;
5579 }
5580 if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0]))
5581 {
5582 pressed = true;
5583 ClearActiveID();
5584 FocusWindow(window);
5585 }
5586 if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0])
5587 {
5588 if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps <on release>
5589 pressed = true;
5590 ClearActiveID();
5591 }
5592
5593 // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above).
5594 // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings.
5595 if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true))
5596 pressed = true;
5597 }
5598 }
5599
5600 bool held = false;
5601 if (g.ActiveId == id)
5602 {
5603 if (g.IO.MouseDown[0])
5604 {
5605 held = true;
5606 }
5607 else
5608 {
5609 if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease))
5610 if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps <on release>
5611 pressed = true;
5612 ClearActiveID();
5613 }
5614 }
5615
5616 // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one.
5617 if (hovered && (flags & ImGuiButtonFlags_AllowOverlapMode) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0))
5618 hovered = pressed = held = false;
5619
5620 if (out_hovered) *out_hovered = hovered;
5621 if (out_held) *out_held = held;
5622
5623 return pressed;
5624 }
5625
ButtonEx(const char * label,const ImVec2 & size_arg,ImGuiButtonFlags flags)5626 bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags)
5627 {
5628 ImGuiWindow* window = GetCurrentWindow();
5629 if (window->SkipItems)
5630 return false;
5631
5632 ImGuiContext& g = *GImGui;
5633 const ImGuiStyle& style = g.Style;
5634 const ImGuiID id = window->GetID(label);
5635 const ImVec2 label_size = CalcTextSize(label, NULL, true);
5636
5637 ImVec2 pos = window->DC.CursorPos;
5638 if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
5639 pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y;
5640 ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);
5641
5642 const ImRect bb(pos, pos + size);
5643 ItemSize(bb, style.FramePadding.y);
5644 if (!ItemAdd(bb, &id))
5645 return false;
5646
5647 if (window->DC.ButtonRepeat) flags |= ImGuiButtonFlags_Repeat;
5648 bool hovered, held;
5649 bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
5650
5651 // Render
5652 const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
5653 RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
5654 RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb);
5655
5656 // Automatically close popups
5657 //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
5658 // CloseCurrentPopup();
5659
5660 return pressed;
5661 }
5662
Button(const char * label,const ImVec2 & size_arg)5663 bool ImGui::Button(const char* label, const ImVec2& size_arg)
5664 {
5665 return ButtonEx(label, size_arg, 0);
5666 }
5667
5668 // Small buttons fits within text without additional vertical spacing.
SmallButton(const char * label)5669 bool ImGui::SmallButton(const char* label)
5670 {
5671 ImGuiContext& g = *GImGui;
5672 float backup_padding_y = g.Style.FramePadding.y;
5673 g.Style.FramePadding.y = 0.0f;
5674 bool pressed = ButtonEx(label, ImVec2(0,0), ImGuiButtonFlags_AlignTextBaseLine);
5675 g.Style.FramePadding.y = backup_padding_y;
5676 return pressed;
5677 }
5678
5679 // Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack.
5680 // Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id)
InvisibleButton(const char * str_id,const ImVec2 & size_arg)5681 bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg)
5682 {
5683 ImGuiWindow* window = GetCurrentWindow();
5684 if (window->SkipItems)
5685 return false;
5686
5687 const ImGuiID id = window->GetID(str_id);
5688 ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f);
5689 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
5690 ItemSize(bb);
5691 if (!ItemAdd(bb, &id))
5692 return false;
5693
5694 bool hovered, held;
5695 bool pressed = ButtonBehavior(bb, id, &hovered, &held);
5696
5697 return pressed;
5698 }
5699
5700 // Upper-right button to close a window.
CloseButton(ImGuiID id,const ImVec2 & pos,float radius)5701 bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius)
5702 {
5703 ImGuiWindow* window = GetCurrentWindow();
5704
5705 const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius));
5706
5707 bool hovered, held;
5708 bool pressed = ButtonBehavior(bb, id, &hovered, &held);
5709
5710 // Render
5711 const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton);
5712 const ImVec2 center = bb.GetCenter();
5713 window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), col, 12);
5714
5715 const float cross_extent = (radius * 0.7071f) - 1.0f;
5716 if (hovered)
5717 {
5718 window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), GetColorU32(ImGuiCol_Text));
5719 window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), GetColorU32(ImGuiCol_Text));
5720 }
5721
5722 return pressed;
5723 }
5724
Image(ImTextureID user_texture_id,const ImVec2 & size,const ImVec2 & uv0,const ImVec2 & uv1,const ImVec4 & tint_col,const ImVec4 & border_col)5725 void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
5726 {
5727 ImGuiWindow* window = GetCurrentWindow();
5728 if (window->SkipItems)
5729 return;
5730
5731 ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
5732 if (border_col.w > 0.0f)
5733 bb.Max += ImVec2(2,2);
5734 ItemSize(bb);
5735 if (!ItemAdd(bb, NULL))
5736 return;
5737
5738 if (border_col.w > 0.0f)
5739 {
5740 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f);
5741 window->DrawList->AddImage(user_texture_id, bb.Min+ImVec2(1,1), bb.Max-ImVec2(1,1), uv0, uv1, GetColorU32(tint_col));
5742 }
5743 else
5744 {
5745 window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col));
5746 }
5747 }
5748
5749 // frame_padding < 0: uses FramePadding from style (default)
5750 // frame_padding = 0: no framing
5751 // frame_padding > 0: set framing size
5752 // The color used are the button colors.
ImageButton(ImTextureID user_texture_id,const ImVec2 & size,const ImVec2 & uv0,const ImVec2 & uv1,int frame_padding,const ImVec4 & bg_col,const ImVec4 & tint_col)5753 bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col)
5754 {
5755 ImGuiWindow* window = GetCurrentWindow();
5756 if (window->SkipItems)
5757 return false;
5758
5759 ImGuiContext& g = *GImGui;
5760 const ImGuiStyle& style = g.Style;
5761
5762 // Default to using texture ID as ID. User can still push string/integer prefixes.
5763 // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV.
5764 PushID((void *)user_texture_id);
5765 const ImGuiID id = window->GetID("#image");
5766 PopID();
5767
5768 const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding;
5769 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding*2);
5770 const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size);
5771 ItemSize(bb);
5772 if (!ItemAdd(bb, &id))
5773 return false;
5774
5775 bool hovered, held;
5776 bool pressed = ButtonBehavior(bb, id, &hovered, &held);
5777
5778 // Render
5779 const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
5780 RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding));
5781 if (bg_col.w > 0.0f)
5782 window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col));
5783 window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col));
5784
5785 return pressed;
5786 }
5787
5788 // Start logging ImGui output to TTY
LogToTTY(int max_depth)5789 void ImGui::LogToTTY(int max_depth)
5790 {
5791 ImGuiContext& g = *GImGui;
5792 if (g.LogEnabled)
5793 return;
5794 ImGuiWindow* window = GetCurrentWindowRead();
5795
5796 g.LogEnabled = true;
5797 g.LogFile = stdout;
5798 g.LogStartDepth = window->DC.TreeDepth;
5799 if (max_depth >= 0)
5800 g.LogAutoExpandMaxDepth = max_depth;
5801 }
5802
5803 // Start logging ImGui output to given file
LogToFile(int max_depth,const char * filename)5804 void ImGui::LogToFile(int max_depth, const char* filename)
5805 {
5806 ImGuiContext& g = *GImGui;
5807 if (g.LogEnabled)
5808 return;
5809 ImGuiWindow* window = GetCurrentWindowRead();
5810
5811 if (!filename)
5812 {
5813 filename = g.IO.LogFilename;
5814 if (!filename)
5815 return;
5816 }
5817
5818 g.LogFile = ImFileOpen(filename, "ab");
5819 if (!g.LogFile)
5820 {
5821 IM_ASSERT(g.LogFile != NULL); // Consider this an error
5822 return;
5823 }
5824 g.LogEnabled = true;
5825 g.LogStartDepth = window->DC.TreeDepth;
5826 if (max_depth >= 0)
5827 g.LogAutoExpandMaxDepth = max_depth;
5828 }
5829
5830 // Start logging ImGui output to clipboard
LogToClipboard(int max_depth)5831 void ImGui::LogToClipboard(int max_depth)
5832 {
5833 ImGuiContext& g = *GImGui;
5834 if (g.LogEnabled)
5835 return;
5836 ImGuiWindow* window = GetCurrentWindowRead();
5837
5838 g.LogEnabled = true;
5839 g.LogFile = NULL;
5840 g.LogStartDepth = window->DC.TreeDepth;
5841 if (max_depth >= 0)
5842 g.LogAutoExpandMaxDepth = max_depth;
5843 }
5844
LogFinish()5845 void ImGui::LogFinish()
5846 {
5847 ImGuiContext& g = *GImGui;
5848 if (!g.LogEnabled)
5849 return;
5850
5851 LogText(IM_NEWLINE);
5852 g.LogEnabled = false;
5853 if (g.LogFile != NULL)
5854 {
5855 if (g.LogFile == stdout)
5856 fflush(g.LogFile);
5857 else
5858 fclose(g.LogFile);
5859 g.LogFile = NULL;
5860 }
5861 if (g.LogClipboard->size() > 1)
5862 {
5863 SetClipboardText(g.LogClipboard->begin());
5864 g.LogClipboard->clear();
5865 }
5866 }
5867
5868 // Helper to display logging buttons
LogButtons()5869 void ImGui::LogButtons()
5870 {
5871 ImGuiContext& g = *GImGui;
5872
5873 PushID("LogButtons");
5874 const bool log_to_tty = Button("Log To TTY"); SameLine();
5875 const bool log_to_file = Button("Log To File"); SameLine();
5876 const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
5877 PushItemWidth(80.0f);
5878 PushAllowKeyboardFocus(false);
5879 SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL);
5880 PopAllowKeyboardFocus();
5881 PopItemWidth();
5882 PopID();
5883
5884 // Start logging at the end of the function so that the buttons don't appear in the log
5885 if (log_to_tty)
5886 LogToTTY(g.LogAutoExpandMaxDepth);
5887 if (log_to_file)
5888 LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename);
5889 if (log_to_clipboard)
5890 LogToClipboard(g.LogAutoExpandMaxDepth);
5891 }
5892
TreeNodeBehaviorIsOpen(ImGuiID id,ImGuiTreeNodeFlags flags)5893 bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags)
5894 {
5895 if (flags & ImGuiTreeNodeFlags_Leaf)
5896 return true;
5897
5898 // We only write to the tree storage if the user clicks (or explicitely use SetNextTreeNode*** functions)
5899 ImGuiContext& g = *GImGui;
5900 ImGuiWindow* window = g.CurrentWindow;
5901 ImGuiStorage* storage = window->DC.StateStorage;
5902
5903 bool is_open;
5904 if (g.SetNextTreeNodeOpenCond != 0)
5905 {
5906 if (g.SetNextTreeNodeOpenCond & ImGuiSetCond_Always)
5907 {
5908 is_open = g.SetNextTreeNodeOpenVal;
5909 storage->SetInt(id, is_open);
5910 }
5911 else
5912 {
5913 // We treat ImGuiSetCondition_Once and ImGuiSetCondition_FirstUseEver the same because tree node state are not saved persistently.
5914 const int stored_value = storage->GetInt(id, -1);
5915 if (stored_value == -1)
5916 {
5917 is_open = g.SetNextTreeNodeOpenVal;
5918 storage->SetInt(id, is_open);
5919 }
5920 else
5921 {
5922 is_open = stored_value != 0;
5923 }
5924 }
5925 g.SetNextTreeNodeOpenCond = 0;
5926 }
5927 else
5928 {
5929 is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;
5930 }
5931
5932 // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior).
5933 // NB- If we are above max depth we still allow manually opened nodes to be logged.
5934 if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth)
5935 is_open = true;
5936
5937 return is_open;
5938 }
5939
TreeNodeBehavior(ImGuiID id,ImGuiTreeNodeFlags flags,const char * label,const char * label_end)5940 bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end)
5941 {
5942 ImGuiWindow* window = GetCurrentWindow();
5943 if (window->SkipItems)
5944 return false;
5945
5946 ImGuiContext& g = *GImGui;
5947 const ImGuiStyle& style = g.Style;
5948 const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;
5949 const ImVec2 padding = display_frame ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f);
5950
5951 if (!label_end)
5952 label_end = FindRenderedTextEnd(label);
5953 const ImVec2 label_size = CalcTextSize(label, label_end, false);
5954
5955 // We vertically grow up to current line height up the typical widget height.
5956 const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset - padding.y); // Latch before ItemSize changes it
5957 const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2);
5958 ImRect bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height));
5959 if (display_frame)
5960 {
5961 // Framed header expand a little outside the default padding
5962 bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1;
5963 bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1;
5964 }
5965
5966 const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2)); // Collapser arrow width + Spacing
5967 const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f); // Include collapser
5968 ItemSize(ImVec2(text_width, frame_height), text_base_offset_y);
5969
5970 // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
5971 // (Ideally we'd want to add a flag for the user to specify we want want the hit test to be done up to the right side of the content or not)
5972 const ImRect interact_bb = display_frame ? bb : ImRect(bb.Min.x, bb.Min.y, bb.Min.x + text_width + style.ItemSpacing.x*2, bb.Max.y);
5973 bool is_open = TreeNodeBehaviorIsOpen(id, flags);
5974 if (!ItemAdd(interact_bb, &id))
5975 {
5976 if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
5977 TreePushRawID(id);
5978 return is_open;
5979 }
5980
5981 // Flags that affects opening behavior:
5982 // - 0(default) ..................... single-click anywhere to open
5983 // - OpenOnDoubleClick .............. double-click anywhere to open
5984 // - OpenOnArrow .................... single-click on arrow to open
5985 // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open
5986 ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowOverlapMode) ? ImGuiButtonFlags_AllowOverlapMode : 0);
5987 if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
5988 button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0);
5989 bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags);
5990 if (pressed && !(flags & ImGuiTreeNodeFlags_Leaf))
5991 {
5992 bool toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick));
5993 if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
5994 toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y));
5995 if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
5996 toggled |= g.IO.MouseDoubleClicked[0];
5997 if (toggled)
5998 {
5999 is_open = !is_open;
6000 window->DC.StateStorage->SetInt(id, is_open);
6001 }
6002 }
6003 if (flags & ImGuiTreeNodeFlags_AllowOverlapMode)
6004 SetItemAllowOverlap();
6005
6006 // Render
6007 const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
6008 const ImVec2 text_pos = bb.Min + ImVec2(text_offset_x, padding.y + text_base_offset_y);
6009 if (display_frame)
6010 {
6011 // Framed type
6012 RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
6013 RenderCollapseTriangle(bb.Min + padding + ImVec2(0.0f, text_base_offset_y), is_open, 1.0f);
6014 if (g.LogEnabled)
6015 {
6016 // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here.
6017 const char log_prefix[] = "\n##";
6018 const char log_suffix[] = "##";
6019 LogRenderedText(text_pos, log_prefix, log_prefix+3);
6020 RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size);
6021 LogRenderedText(text_pos, log_suffix+1, log_suffix+3);
6022 }
6023 else
6024 {
6025 RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size);
6026 }
6027 }
6028 else
6029 {
6030 // Unframed typed for tree nodes
6031 if (hovered || (flags & ImGuiTreeNodeFlags_Selected))
6032 RenderFrame(bb.Min, bb.Max, col, false);
6033
6034 if (flags & ImGuiTreeNodeFlags_Bullet)
6035 RenderBullet(bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y));
6036 else if (!(flags & ImGuiTreeNodeFlags_Leaf))
6037 RenderCollapseTriangle(bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open, 0.70f);
6038 if (g.LogEnabled)
6039 LogRenderedText(text_pos, ">");
6040 RenderText(text_pos, label, label_end, false);
6041 }
6042
6043 if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
6044 TreePushRawID(id);
6045 return is_open;
6046 }
6047
6048 // CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag).
6049 // This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode().
CollapsingHeader(const char * label,ImGuiTreeNodeFlags flags)6050 bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags)
6051 {
6052 ImGuiWindow* window = GetCurrentWindow();
6053 if (window->SkipItems)
6054 return false;
6055
6056 return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen, label);
6057 }
6058
CollapsingHeader(const char * label,bool * p_open,ImGuiTreeNodeFlags flags)6059 bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags)
6060 {
6061 ImGuiWindow* window = GetCurrentWindow();
6062 if (window->SkipItems)
6063 return false;
6064
6065 if (p_open && !*p_open)
6066 return false;
6067
6068 ImGuiID id = window->GetID(label);
6069 bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen | (p_open ? ImGuiTreeNodeFlags_AllowOverlapMode : 0), label);
6070 if (p_open)
6071 {
6072 // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.
6073 ImGuiContext& g = *GImGui;
6074 float button_sz = g.FontSize * 0.5f;
6075 if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_sz, window->DC.LastItemRect.Min.y + g.Style.FramePadding.y + button_sz), button_sz))
6076 *p_open = false;
6077 }
6078
6079 return is_open;
6080 }
6081
TreeNodeEx(const char * label,ImGuiTreeNodeFlags flags)6082 bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags)
6083 {
6084 ImGuiWindow* window = GetCurrentWindow();
6085 if (window->SkipItems)
6086 return false;
6087
6088 return TreeNodeBehavior(window->GetID(label), flags, label, NULL);
6089 }
6090
TreeNodeExV(const char * str_id,ImGuiTreeNodeFlags flags,const char * fmt,va_list args)6091 bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
6092 {
6093 ImGuiWindow* window = GetCurrentWindow();
6094 if (window->SkipItems)
6095 return false;
6096
6097 ImGuiContext& g = *GImGui;
6098 const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
6099 return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end);
6100 }
6101
TreeNodeExV(const void * ptr_id,ImGuiTreeNodeFlags flags,const char * fmt,va_list args)6102 bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
6103 {
6104 ImGuiWindow* window = GetCurrentWindow();
6105 if (window->SkipItems)
6106 return false;
6107
6108 ImGuiContext& g = *GImGui;
6109 const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
6110 return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end);
6111 }
6112
TreeNodeV(const char * str_id,const char * fmt,va_list args)6113 bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)
6114 {
6115 return TreeNodeExV(str_id, 0, fmt, args);
6116 }
6117
TreeNodeV(const void * ptr_id,const char * fmt,va_list args)6118 bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)
6119 {
6120 return TreeNodeExV(ptr_id, 0, fmt, args);
6121 }
6122
TreeNodeEx(const char * str_id,ImGuiTreeNodeFlags flags,const char * fmt,...)6123 bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
6124 {
6125 va_list args;
6126 va_start(args, fmt);
6127 bool is_open = TreeNodeExV(str_id, flags, fmt, args);
6128 va_end(args);
6129 return is_open;
6130 }
6131
TreeNodeEx(const void * ptr_id,ImGuiTreeNodeFlags flags,const char * fmt,...)6132 bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
6133 {
6134 va_list args;
6135 va_start(args, fmt);
6136 bool is_open = TreeNodeExV(ptr_id, flags, fmt, args);
6137 va_end(args);
6138 return is_open;
6139 }
6140
TreeNode(const char * str_id,const char * fmt,...)6141 bool ImGui::TreeNode(const char* str_id, const char* fmt, ...)
6142 {
6143 va_list args;
6144 va_start(args, fmt);
6145 bool is_open = TreeNodeExV(str_id, 0, fmt, args);
6146 va_end(args);
6147 return is_open;
6148 }
6149
TreeNode(const void * ptr_id,const char * fmt,...)6150 bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)
6151 {
6152 va_list args;
6153 va_start(args, fmt);
6154 bool is_open = TreeNodeExV(ptr_id, 0, fmt, args);
6155 va_end(args);
6156 return is_open;
6157 }
6158
TreeNode(const char * label)6159 bool ImGui::TreeNode(const char* label)
6160 {
6161 ImGuiWindow* window = GetCurrentWindow();
6162 if (window->SkipItems)
6163 return false;
6164 return TreeNodeBehavior(window->GetID(label), 0, label, NULL);
6165 }
6166
TreeAdvanceToLabelPos()6167 void ImGui::TreeAdvanceToLabelPos()
6168 {
6169 ImGuiContext& g = *GImGui;
6170 g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing();
6171 }
6172
6173 // Horizontal distance preceding label when using TreeNode() or Bullet()
GetTreeNodeToLabelSpacing()6174 float ImGui::GetTreeNodeToLabelSpacing()
6175 {
6176 ImGuiContext& g = *GImGui;
6177 return g.FontSize + (g.Style.FramePadding.x * 2.0f);
6178 }
6179
SetNextTreeNodeOpen(bool is_open,ImGuiSetCond cond)6180 void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiSetCond cond)
6181 {
6182 ImGuiContext& g = *GImGui;
6183 g.SetNextTreeNodeOpenVal = is_open;
6184 g.SetNextTreeNodeOpenCond = cond ? cond : ImGuiSetCond_Always;
6185 }
6186
PushID(const char * str_id)6187 void ImGui::PushID(const char* str_id)
6188 {
6189 ImGuiWindow* window = GetCurrentWindow();
6190 window->IDStack.push_back(window->GetID(str_id));
6191 }
6192
PushID(const char * str_id_begin,const char * str_id_end)6193 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6194 {
6195 ImGuiWindow* window = GetCurrentWindow();
6196 window->IDStack.push_back(window->GetID(str_id_begin, str_id_end));
6197 }
6198
PushID(const void * ptr_id)6199 void ImGui::PushID(const void* ptr_id)
6200 {
6201 ImGuiWindow* window = GetCurrentWindow();
6202 window->IDStack.push_back(window->GetID(ptr_id));
6203 }
6204
PushID(int int_id)6205 void ImGui::PushID(int int_id)
6206 {
6207 const void* ptr_id = (void*)(intptr_t)int_id;
6208 ImGuiWindow* window = GetCurrentWindow();
6209 window->IDStack.push_back(window->GetID(ptr_id));
6210 }
6211
PopID()6212 void ImGui::PopID()
6213 {
6214 ImGuiWindow* window = GetCurrentWindow();
6215 window->IDStack.pop_back();
6216 }
6217
GetID(const char * str_id)6218 ImGuiID ImGui::GetID(const char* str_id)
6219 {
6220 return GImGui->CurrentWindow->GetID(str_id);
6221 }
6222
GetID(const char * str_id_begin,const char * str_id_end)6223 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6224 {
6225 return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end);
6226 }
6227
GetID(const void * ptr_id)6228 ImGuiID ImGui::GetID(const void* ptr_id)
6229 {
6230 return GImGui->CurrentWindow->GetID(ptr_id);
6231 }
6232
Bullet()6233 void ImGui::Bullet()
6234 {
6235 ImGuiWindow* window = GetCurrentWindow();
6236 if (window->SkipItems)
6237 return;
6238
6239 ImGuiContext& g = *GImGui;
6240 const ImGuiStyle& style = g.Style;
6241 const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
6242 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height));
6243 ItemSize(bb);
6244 if (!ItemAdd(bb, NULL))
6245 {
6246 SameLine(0, style.FramePadding.x*2);
6247 return;
6248 }
6249
6250 // Render and stay on same line
6251 RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
6252 SameLine(0, style.FramePadding.x*2);
6253 }
6254
6255 // Text with a little bullet aligned to the typical tree node.
BulletTextV(const char * fmt,va_list args)6256 void ImGui::BulletTextV(const char* fmt, va_list args)
6257 {
6258 ImGuiWindow* window = GetCurrentWindow();
6259 if (window->SkipItems)
6260 return;
6261
6262 ImGuiContext& g = *GImGui;
6263 const ImGuiStyle& style = g.Style;
6264
6265 const char* text_begin = g.TempBuffer;
6266 const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
6267 const ImVec2 label_size = CalcTextSize(text_begin, text_end, false);
6268 const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
6269 const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
6270 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x*2) : 0.0f), ImMax(line_height, label_size.y))); // Empty text doesn't add padding
6271 ItemSize(bb);
6272 if (!ItemAdd(bb, NULL))
6273 return;
6274
6275 // Render
6276 RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
6277 RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end, false);
6278 }
6279
BulletText(const char * fmt,...)6280 void ImGui::BulletText(const char* fmt, ...)
6281 {
6282 va_list args;
6283 va_start(args, fmt);
6284 BulletTextV(fmt, args);
6285 va_end(args);
6286 }
6287
DataTypeFormatString(ImGuiDataType data_type,void * data_ptr,const char * display_format,char * buf,int buf_size)6288 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size)
6289 {
6290 if (data_type == ImGuiDataType_Int)
6291 ImFormatString(buf, buf_size, display_format, *(int*)data_ptr);
6292 else if (data_type == ImGuiDataType_Float)
6293 ImFormatString(buf, buf_size, display_format, *(float*)data_ptr);
6294 }
6295
DataTypeFormatString(ImGuiDataType data_type,void * data_ptr,int decimal_precision,char * buf,int buf_size)6296 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size)
6297 {
6298 if (data_type == ImGuiDataType_Int)
6299 {
6300 if (decimal_precision < 0)
6301 ImFormatString(buf, buf_size, "%d", *(int*)data_ptr);
6302 else
6303 ImFormatString(buf, buf_size, "%.*d", decimal_precision, *(int*)data_ptr);
6304 }
6305 else if (data_type == ImGuiDataType_Float)
6306 {
6307 if (decimal_precision < 0)
6308 ImFormatString(buf, buf_size, "%f", *(float*)data_ptr); // Ideally we'd have a minimum decimal precision of 1 to visually denote that it is a float, while hiding non-significant digits?
6309 else
6310 ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(float*)data_ptr);
6311 }
6312 }
6313
DataTypeApplyOp(ImGuiDataType data_type,int op,void * value1,const void * value2)6314 static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2)// Store into value1
6315 {
6316 if (data_type == ImGuiDataType_Int)
6317 {
6318 if (op == '+')
6319 *(int*)value1 = *(int*)value1 + *(const int*)value2;
6320 else if (op == '-')
6321 *(int*)value1 = *(int*)value1 - *(const int*)value2;
6322 }
6323 else if (data_type == ImGuiDataType_Float)
6324 {
6325 if (op == '+')
6326 *(float*)value1 = *(float*)value1 + *(const float*)value2;
6327 else if (op == '-')
6328 *(float*)value1 = *(float*)value1 - *(const float*)value2;
6329 }
6330 }
6331
6332 // User can input math operators (e.g. +100) to edit a numerical values.
DataTypeApplyOpFromText(const char * buf,const char * initial_value_buf,ImGuiDataType data_type,void * data_ptr,const char * scalar_format)6333 static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format)
6334 {
6335 while (ImCharIsSpace(*buf))
6336 buf++;
6337
6338 // We don't support '-' op because it would conflict with inputing negative value.
6339 // Instead you can use +-100 to subtract from an existing value
6340 char op = buf[0];
6341 if (op == '+' || op == '*' || op == '/')
6342 {
6343 buf++;
6344 while (ImCharIsSpace(*buf))
6345 buf++;
6346 }
6347 else
6348 {
6349 op = 0;
6350 }
6351 if (!buf[0])
6352 return false;
6353
6354 if (data_type == ImGuiDataType_Int)
6355 {
6356 if (!scalar_format)
6357 scalar_format = "%d";
6358 int* v = (int*)data_ptr;
6359 const int old_v = *v;
6360 int arg0 = *v;
6361 if (op && sscanf(initial_value_buf, scalar_format, &arg0) < 1)
6362 return false;
6363
6364 // Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision
6365 float arg1 = 0.0f;
6366 if (op == '+') { if (sscanf(buf, "%f", &arg1) == 1) *v = (int)(arg0 + arg1); } // Add (use "+-" to subtract)
6367 else if (op == '*') { if (sscanf(buf, "%f", &arg1) == 1) *v = (int)(arg0 * arg1); } // Multiply
6368 else if (op == '/') { if (sscanf(buf, "%f", &arg1) == 1 && arg1 != 0.0f) *v = (int)(arg0 / arg1); }// Divide
6369 else { if (sscanf(buf, scalar_format, &arg0) == 1) *v = arg0; } // Assign constant
6370 return (old_v != *v);
6371 }
6372 else if (data_type == ImGuiDataType_Float)
6373 {
6374 // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in
6375 scalar_format = "%f";
6376 float* v = (float*)data_ptr;
6377 const float old_v = *v;
6378 float arg0 = *v;
6379 if (op && sscanf(initial_value_buf, scalar_format, &arg0) < 1)
6380 return false;
6381
6382 float arg1 = 0.0f;
6383 if (sscanf(buf, scalar_format, &arg1) < 1)
6384 return false;
6385 if (op == '+') { *v = arg0 + arg1; } // Add (use "+-" to subtract)
6386 else if (op == '*') { *v = arg0 * arg1; } // Multiply
6387 else if (op == '/') { if (arg1 != 0.0f) *v = arg0 / arg1; } // Divide
6388 else { *v = arg1; } // Assign constant
6389 return (old_v != *v);
6390 }
6391
6392 return false;
6393 }
6394
6395 // Create text input in place of a slider (when CTRL+Clicking on slider)
InputScalarAsWidgetReplacement(const ImRect & aabb,const char * label,ImGuiDataType data_type,void * data_ptr,ImGuiID id,int decimal_precision)6396 bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision)
6397 {
6398 ImGuiContext& g = *GImGui;
6399 ImGuiWindow* window = GetCurrentWindow();
6400
6401 // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen)
6402 SetActiveID(g.ScalarAsInputTextId, window);
6403 SetHoveredID(0);
6404 FocusableItemUnregister(window);
6405
6406 char buf[32];
6407 DataTypeFormatString(data_type, data_ptr, decimal_precision, buf, IM_ARRAYSIZE(buf));
6408 bool text_value_changed = InputTextEx(label, buf, IM_ARRAYSIZE(buf), aabb.GetSize(), ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll);
6409 if (g.ScalarAsInputTextId == 0)
6410 {
6411 // First frame
6412 IM_ASSERT(g.ActiveId == id); // InputText ID expected to match the Slider ID (else we'd need to store them both, which is also possible)
6413 g.ScalarAsInputTextId = g.ActiveId;
6414 SetHoveredID(id);
6415 }
6416 else if (g.ActiveId != g.ScalarAsInputTextId)
6417 {
6418 // Release
6419 g.ScalarAsInputTextId = 0;
6420 }
6421 if (text_value_changed)
6422 return DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, NULL);
6423 return false;
6424 }
6425
6426 // Parse display precision back from the display format string
ParseFormatPrecision(const char * fmt,int default_precision)6427 int ImGui::ParseFormatPrecision(const char* fmt, int default_precision)
6428 {
6429 int precision = default_precision;
6430 while ((fmt = strchr(fmt, '%')) != NULL)
6431 {
6432 fmt++;
6433 if (fmt[0] == '%') { fmt++; continue; } // Ignore "%%"
6434 while (*fmt >= '0' && *fmt <= '9')
6435 fmt++;
6436 if (*fmt == '.')
6437 {
6438 precision = atoi(fmt + 1);
6439 if (precision < 0 || precision > 10)
6440 precision = default_precision;
6441 }
6442 break;
6443 }
6444 return precision;
6445 }
6446
RoundScalar(float value,int decimal_precision)6447 float ImGui::RoundScalar(float value, int decimal_precision)
6448 {
6449 // Round past decimal precision
6450 // So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0
6451 // FIXME: Investigate better rounding methods
6452 static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f };
6453 float min_step = (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision);
6454 bool negative = value < 0.0f;
6455 value = fabsf(value);
6456 float remainder = fmodf(value, min_step);
6457 if (remainder <= min_step*0.5f)
6458 value -= remainder;
6459 else
6460 value += (min_step - remainder);
6461 return negative ? -value : value;
6462 }
6463
SliderBehaviorCalcRatioFromValue(float v,float v_min,float v_max,float power,float linear_zero_pos)6464 static inline float SliderBehaviorCalcRatioFromValue(float v, float v_min, float v_max, float power, float linear_zero_pos)
6465 {
6466 if (v_min == v_max)
6467 return 0.0f;
6468
6469 const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f);
6470 const float v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min);
6471 if (is_non_linear)
6472 {
6473 if (v_clamped < 0.0f)
6474 {
6475 const float f = 1.0f - (v_clamped - v_min) / (ImMin(0.0f,v_max) - v_min);
6476 return (1.0f - powf(f, 1.0f/power)) * linear_zero_pos;
6477 }
6478 else
6479 {
6480 const float f = (v_clamped - ImMax(0.0f,v_min)) / (v_max - ImMax(0.0f,v_min));
6481 return linear_zero_pos + powf(f, 1.0f/power) * (1.0f - linear_zero_pos);
6482 }
6483 }
6484
6485 // Linear slider
6486 return (v_clamped - v_min) / (v_max - v_min);
6487 }
6488
SliderBehavior(const ImRect & frame_bb,ImGuiID id,float * v,float v_min,float v_max,float power,int decimal_precision,ImGuiSliderFlags flags)6489 bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags)
6490 {
6491 ImGuiContext& g = *GImGui;
6492 ImGuiWindow* window = GetCurrentWindow();
6493 const ImGuiStyle& style = g.Style;
6494
6495 // Draw frame
6496 RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
6497
6498 const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f);
6499 const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0;
6500
6501 const float grab_padding = 2.0f;
6502 const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f);
6503 float grab_sz;
6504 if (decimal_precision > 0)
6505 grab_sz = ImMin(style.GrabMinSize, slider_sz);
6506 else
6507 grab_sz = ImMin(ImMax(1.0f * (slider_sz / ((v_min < v_max ? v_max - v_min : v_min - v_max) + 1.0f)), style.GrabMinSize), slider_sz); // Integer sliders, if possible have the grab size represent 1 unit
6508 const float slider_usable_sz = slider_sz - grab_sz;
6509 const float slider_usable_pos_min = (is_horizontal ? frame_bb.Min.x : frame_bb.Min.y) + grab_padding + grab_sz*0.5f;
6510 const float slider_usable_pos_max = (is_horizontal ? frame_bb.Max.x : frame_bb.Max.y) - grab_padding - grab_sz*0.5f;
6511
6512 // For logarithmic sliders that cross over sign boundary we want the exponential increase to be symmetric around 0.0f
6513 float linear_zero_pos = 0.0f; // 0.0->1.0f
6514 if (v_min * v_max < 0.0f)
6515 {
6516 // Different sign
6517 const float linear_dist_min_to_0 = powf(fabsf(0.0f - v_min), 1.0f/power);
6518 const float linear_dist_max_to_0 = powf(fabsf(v_max - 0.0f), 1.0f/power);
6519 linear_zero_pos = linear_dist_min_to_0 / (linear_dist_min_to_0+linear_dist_max_to_0);
6520 }
6521 else
6522 {
6523 // Same sign
6524 linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f;
6525 }
6526
6527 // Process clicking on the slider
6528 bool value_changed = false;
6529 if (g.ActiveId == id)
6530 {
6531 if (g.IO.MouseDown[0])
6532 {
6533 const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
6534 float clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f;
6535 if (!is_horizontal)
6536 clicked_t = 1.0f - clicked_t;
6537
6538 float new_value;
6539 if (is_non_linear)
6540 {
6541 // Account for logarithmic scale on both sides of the zero
6542 if (clicked_t < linear_zero_pos)
6543 {
6544 // Negative: rescale to the negative range before powering
6545 float a = 1.0f - (clicked_t / linear_zero_pos);
6546 a = powf(a, power);
6547 new_value = ImLerp(ImMin(v_max,0.0f), v_min, a);
6548 }
6549 else
6550 {
6551 // Positive: rescale to the positive range before powering
6552 float a;
6553 if (fabsf(linear_zero_pos - 1.0f) > 1.e-6f)
6554 a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos);
6555 else
6556 a = clicked_t;
6557 a = powf(a, power);
6558 new_value = ImLerp(ImMax(v_min,0.0f), v_max, a);
6559 }
6560 }
6561 else
6562 {
6563 // Linear slider
6564 new_value = ImLerp(v_min, v_max, clicked_t);
6565 }
6566
6567 // Round past decimal precision
6568 new_value = RoundScalar(new_value, decimal_precision);
6569 if (*v != new_value)
6570 {
6571 *v = new_value;
6572 value_changed = true;
6573 }
6574 }
6575 else
6576 {
6577 ClearActiveID();
6578 }
6579 }
6580
6581 // Calculate slider grab positioning
6582 float grab_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos);
6583
6584 // Draw
6585 if (!is_horizontal)
6586 grab_t = 1.0f - grab_t;
6587 const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
6588 ImRect grab_bb;
6589 if (is_horizontal)
6590 grab_bb = ImRect(ImVec2(grab_pos - grab_sz*0.5f, frame_bb.Min.y + grab_padding), ImVec2(grab_pos + grab_sz*0.5f, frame_bb.Max.y - grab_padding));
6591 else
6592 grab_bb = ImRect(ImVec2(frame_bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f), ImVec2(frame_bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f));
6593 window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
6594
6595 return value_changed;
6596 }
6597
6598 // Use power!=1.0 for logarithmic sliders.
6599 // Adjust display_format to decorate the value with a prefix or a suffix.
6600 // "%.3f" 1.234
6601 // "%5.2f secs" 01.23 secs
6602 // "Gold: %.0f" Gold: 1
SliderFloat(const char * label,float * v,float v_min,float v_max,const char * display_format,float power)6603 bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format, float power)
6604 {
6605 ImGuiWindow* window = GetCurrentWindow();
6606 if (window->SkipItems)
6607 return false;
6608
6609 ImGuiContext& g = *GImGui;
6610 const ImGuiStyle& style = g.Style;
6611 const ImGuiID id = window->GetID(label);
6612 const float w = CalcItemWidth();
6613
6614 const ImVec2 label_size = CalcTextSize(label, NULL, true);
6615 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
6616 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
6617
6618 // NB- we don't call ItemSize() yet because we may turn into a text edit box below
6619 if (!ItemAdd(total_bb, &id))
6620 {
6621 ItemSize(total_bb, style.FramePadding.y);
6622 return false;
6623 }
6624
6625 const bool hovered = IsHovered(frame_bb, id);
6626 if (hovered)
6627 SetHoveredID(id);
6628
6629 if (!display_format)
6630 display_format = "%.3f";
6631 int decimal_precision = ParseFormatPrecision(display_format, 3);
6632
6633 // Tabbing or CTRL-clicking on Slider turns it into an input box
6634 bool start_text_input = false;
6635 const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id);
6636 if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]))
6637 {
6638 SetActiveID(id, window);
6639 FocusWindow(window);
6640
6641 if (tab_focus_requested || g.IO.KeyCtrl)
6642 {
6643 start_text_input = true;
6644 g.ScalarAsInputTextId = 0;
6645 }
6646 }
6647 if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
6648 return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
6649
6650 ItemSize(total_bb, style.FramePadding.y);
6651
6652 // Actual slider behavior + render grab
6653 const bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision);
6654
6655 // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
6656 char value_buf[64];
6657 const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
6658 RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
6659
6660 if (label_size.x > 0.0f)
6661 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
6662
6663 return value_changed;
6664 }
6665
VSliderFloat(const char * label,const ImVec2 & size,float * v,float v_min,float v_max,const char * display_format,float power)6666 bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format, float power)
6667 {
6668 ImGuiWindow* window = GetCurrentWindow();
6669 if (window->SkipItems)
6670 return false;
6671
6672 ImGuiContext& g = *GImGui;
6673 const ImGuiStyle& style = g.Style;
6674 const ImGuiID id = window->GetID(label);
6675
6676 const ImVec2 label_size = CalcTextSize(label, NULL, true);
6677 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
6678 const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
6679
6680 ItemSize(bb, style.FramePadding.y);
6681 if (!ItemAdd(frame_bb, &id))
6682 return false;
6683
6684 const bool hovered = IsHovered(frame_bb, id);
6685 if (hovered)
6686 SetHoveredID(id);
6687
6688 if (!display_format)
6689 display_format = "%.3f";
6690 int decimal_precision = ParseFormatPrecision(display_format, 3);
6691
6692 if (hovered && g.IO.MouseClicked[0])
6693 {
6694 SetActiveID(id, window);
6695 FocusWindow(window);
6696 }
6697
6698 // Actual slider behavior + render grab
6699 bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision, ImGuiSliderFlags_Vertical);
6700
6701 // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
6702 // For the vertical slider we allow centered text to overlap the frame padding
6703 char value_buf[64];
6704 char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
6705 RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.0f));
6706 if (label_size.x > 0.0f)
6707 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
6708
6709 return value_changed;
6710 }
6711
SliderAngle(const char * label,float * v_rad,float v_degrees_min,float v_degrees_max)6712 bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max)
6713 {
6714 float v_deg = (*v_rad) * 360.0f / (2*IM_PI);
6715 bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f);
6716 *v_rad = v_deg * (2*IM_PI) / 360.0f;
6717 return value_changed;
6718 }
6719
SliderInt(const char * label,int * v,int v_min,int v_max,const char * display_format)6720 bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format)
6721 {
6722 if (!display_format)
6723 display_format = "%.0f";
6724 float v_f = (float)*v;
6725 bool value_changed = SliderFloat(label, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
6726 *v = (int)v_f;
6727 return value_changed;
6728 }
6729
VSliderInt(const char * label,const ImVec2 & size,int * v,int v_min,int v_max,const char * display_format)6730 bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format)
6731 {
6732 if (!display_format)
6733 display_format = "%.0f";
6734 float v_f = (float)*v;
6735 bool value_changed = VSliderFloat(label, size, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
6736 *v = (int)v_f;
6737 return value_changed;
6738 }
6739
6740 // Add multiple sliders on 1 line for compact edition of multiple components
SliderFloatN(const char * label,float * v,int components,float v_min,float v_max,const char * display_format,float power)6741 bool ImGui::SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power)
6742 {
6743 ImGuiWindow* window = GetCurrentWindow();
6744 if (window->SkipItems)
6745 return false;
6746
6747 ImGuiContext& g = *GImGui;
6748 bool value_changed = false;
6749 BeginGroup();
6750 PushID(label);
6751 PushMultiItemsWidths(components);
6752 for (int i = 0; i < components; i++)
6753 {
6754 PushID(i);
6755 value_changed |= SliderFloat("##v", &v[i], v_min, v_max, display_format, power);
6756 SameLine(0, g.Style.ItemInnerSpacing.x);
6757 PopID();
6758 PopItemWidth();
6759 }
6760 PopID();
6761
6762 TextUnformatted(label, FindRenderedTextEnd(label));
6763 EndGroup();
6764
6765 return value_changed;
6766 }
6767
SliderFloat2(const char * label,float v[2],float v_min,float v_max,const char * display_format,float power)6768 bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format, float power)
6769 {
6770 return SliderFloatN(label, v, 2, v_min, v_max, display_format, power);
6771 }
6772
SliderFloat3(const char * label,float v[3],float v_min,float v_max,const char * display_format,float power)6773 bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format, float power)
6774 {
6775 return SliderFloatN(label, v, 3, v_min, v_max, display_format, power);
6776 }
6777
SliderFloat4(const char * label,float v[4],float v_min,float v_max,const char * display_format,float power)6778 bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format, float power)
6779 {
6780 return SliderFloatN(label, v, 4, v_min, v_max, display_format, power);
6781 }
6782
SliderIntN(const char * label,int * v,int components,int v_min,int v_max,const char * display_format)6783 bool ImGui::SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format)
6784 {
6785 ImGuiWindow* window = GetCurrentWindow();
6786 if (window->SkipItems)
6787 return false;
6788
6789 ImGuiContext& g = *GImGui;
6790 bool value_changed = false;
6791 BeginGroup();
6792 PushID(label);
6793 PushMultiItemsWidths(components);
6794 for (int i = 0; i < components; i++)
6795 {
6796 PushID(i);
6797 value_changed |= SliderInt("##v", &v[i], v_min, v_max, display_format);
6798 SameLine(0, g.Style.ItemInnerSpacing.x);
6799 PopID();
6800 PopItemWidth();
6801 }
6802 PopID();
6803
6804 TextUnformatted(label, FindRenderedTextEnd(label));
6805 EndGroup();
6806
6807 return value_changed;
6808 }
6809
SliderInt2(const char * label,int v[2],int v_min,int v_max,const char * display_format)6810 bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format)
6811 {
6812 return SliderIntN(label, v, 2, v_min, v_max, display_format);
6813 }
6814
SliderInt3(const char * label,int v[3],int v_min,int v_max,const char * display_format)6815 bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format)
6816 {
6817 return SliderIntN(label, v, 3, v_min, v_max, display_format);
6818 }
6819
SliderInt4(const char * label,int v[4],int v_min,int v_max,const char * display_format)6820 bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format)
6821 {
6822 return SliderIntN(label, v, 4, v_min, v_max, display_format);
6823 }
6824
DragBehavior(const ImRect & frame_bb,ImGuiID id,float * v,float v_speed,float v_min,float v_max,int decimal_precision,float power)6825 bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power)
6826 {
6827 ImGuiContext& g = *GImGui;
6828 const ImGuiStyle& style = g.Style;
6829
6830 // Draw frame
6831 const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
6832 RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
6833
6834 bool value_changed = false;
6835
6836 // Process clicking on the drag
6837 if (g.ActiveId == id)
6838 {
6839 if (g.IO.MouseDown[0])
6840 {
6841 if (g.ActiveIdIsJustActivated)
6842 {
6843 // Lock current value on click
6844 g.DragCurrentValue = *v;
6845 g.DragLastMouseDelta = ImVec2(0.f, 0.f);
6846 }
6847
6848 float v_cur = g.DragCurrentValue;
6849 const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f);
6850 if (fabsf(mouse_drag_delta.x - g.DragLastMouseDelta.x) > 0.0f)
6851 {
6852 float speed = v_speed;
6853 if (speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX)
6854 speed = (v_max - v_min) * g.DragSpeedDefaultRatio;
6855 if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f)
6856 speed = speed * g.DragSpeedScaleFast;
6857 if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f)
6858 speed = speed * g.DragSpeedScaleSlow;
6859
6860 float delta = (mouse_drag_delta.x - g.DragLastMouseDelta.x) * speed;
6861 if (fabsf(power - 1.0f) > 0.001f)
6862 {
6863 // Logarithmic curve on both side of 0.0
6864 float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur;
6865 float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f;
6866 float v1 = powf(v0_abs, 1.0f / power) + (delta * v0_sign);
6867 float v1_abs = v1 >= 0.0f ? v1 : -v1;
6868 float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f; // Crossed sign line
6869 v_cur = powf(v1_abs, power) * v0_sign * v1_sign; // Reapply sign
6870 }
6871 else
6872 {
6873 v_cur += delta;
6874 }
6875 g.DragLastMouseDelta.x = mouse_drag_delta.x;
6876
6877 // Clamp
6878 if (v_min < v_max)
6879 v_cur = ImClamp(v_cur, v_min, v_max);
6880 g.DragCurrentValue = v_cur;
6881 }
6882
6883 // Round to user desired precision, then apply
6884 v_cur = RoundScalar(v_cur, decimal_precision);
6885 if (*v != v_cur)
6886 {
6887 *v = v_cur;
6888 value_changed = true;
6889 }
6890 }
6891 else
6892 {
6893 ClearActiveID();
6894 }
6895 }
6896
6897 return value_changed;
6898 }
6899
DragFloat(const char * label,float * v,float v_speed,float v_min,float v_max,const char * display_format,float power)6900 bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* display_format, float power)
6901 {
6902 ImGuiWindow* window = GetCurrentWindow();
6903 if (window->SkipItems)
6904 return false;
6905
6906 ImGuiContext& g = *GImGui;
6907 const ImGuiStyle& style = g.Style;
6908 const ImGuiID id = window->GetID(label);
6909 const float w = CalcItemWidth();
6910
6911 const ImVec2 label_size = CalcTextSize(label, NULL, true);
6912 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
6913 const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
6914 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
6915
6916 // NB- we don't call ItemSize() yet because we may turn into a text edit box below
6917 if (!ItemAdd(total_bb, &id))
6918 {
6919 ItemSize(total_bb, style.FramePadding.y);
6920 return false;
6921 }
6922
6923 const bool hovered = IsHovered(frame_bb, id);
6924 if (hovered)
6925 SetHoveredID(id);
6926
6927 if (!display_format)
6928 display_format = "%.3f";
6929 int decimal_precision = ParseFormatPrecision(display_format, 3);
6930
6931 // Tabbing or CTRL-clicking on Drag turns it into an input box
6932 bool start_text_input = false;
6933 const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id);
6934 if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] | g.IO.MouseDoubleClicked[0])))
6935 {
6936 SetActiveID(id, window);
6937 FocusWindow(window);
6938
6939 if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0])
6940 {
6941 start_text_input = true;
6942 g.ScalarAsInputTextId = 0;
6943 }
6944 }
6945 if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
6946 return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
6947
6948 // Actual drag behavior
6949 ItemSize(total_bb, style.FramePadding.y);
6950 const bool value_changed = DragBehavior(frame_bb, id, v, v_speed, v_min, v_max, decimal_precision, power);
6951
6952 // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
6953 char value_buf[64];
6954 const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
6955 RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
6956
6957 if (label_size.x > 0.0f)
6958 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
6959
6960 return value_changed;
6961 }
6962
DragFloatN(const char * label,float * v,int components,float v_speed,float v_min,float v_max,const char * display_format,float power)6963 bool ImGui::DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power)
6964 {
6965 ImGuiWindow* window = GetCurrentWindow();
6966 if (window->SkipItems)
6967 return false;
6968
6969 ImGuiContext& g = *GImGui;
6970 bool value_changed = false;
6971 BeginGroup();
6972 PushID(label);
6973 PushMultiItemsWidths(components);
6974 for (int i = 0; i < components; i++)
6975 {
6976 PushID(i);
6977 value_changed |= DragFloat("##v", &v[i], v_speed, v_min, v_max, display_format, power);
6978 SameLine(0, g.Style.ItemInnerSpacing.x);
6979 PopID();
6980 PopItemWidth();
6981 }
6982 PopID();
6983
6984 TextUnformatted(label, FindRenderedTextEnd(label));
6985 EndGroup();
6986
6987 return value_changed;
6988 }
6989
DragFloat2(const char * label,float v[2],float v_speed,float v_min,float v_max,const char * display_format,float power)6990 bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power)
6991 {
6992 return DragFloatN(label, v, 2, v_speed, v_min, v_max, display_format, power);
6993 }
6994
DragFloat3(const char * label,float v[3],float v_speed,float v_min,float v_max,const char * display_format,float power)6995 bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* display_format, float power)
6996 {
6997 return DragFloatN(label, v, 3, v_speed, v_min, v_max, display_format, power);
6998 }
6999
DragFloat4(const char * label,float v[4],float v_speed,float v_min,float v_max,const char * display_format,float power)7000 bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* display_format, float power)
7001 {
7002 return DragFloatN(label, v, 4, v_speed, v_min, v_max, display_format, power);
7003 }
7004
DragFloatRange2(const char * label,float * v_current_min,float * v_current_max,float v_speed,float v_min,float v_max,const char * display_format,const char * display_format_max,float power)7005 bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* display_format, const char* display_format_max, float power)
7006 {
7007 ImGuiWindow* window = GetCurrentWindow();
7008 if (window->SkipItems)
7009 return false;
7010
7011 ImGuiContext& g = *GImGui;
7012 PushID(label);
7013 BeginGroup();
7014 PushMultiItemsWidths(2);
7015
7016 bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format, power);
7017 PopItemWidth();
7018 SameLine(0, g.Style.ItemInnerSpacing.x);
7019 value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, display_format_max ? display_format_max : display_format, power);
7020 PopItemWidth();
7021 SameLine(0, g.Style.ItemInnerSpacing.x);
7022
7023 TextUnformatted(label, FindRenderedTextEnd(label));
7024 EndGroup();
7025 PopID();
7026
7027 return value_changed;
7028 }
7029
7030 // NB: v_speed is float to allow adjusting the drag speed with more precision
DragInt(const char * label,int * v,float v_speed,int v_min,int v_max,const char * display_format)7031 bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* display_format)
7032 {
7033 if (!display_format)
7034 display_format = "%.0f";
7035 float v_f = (float)*v;
7036 bool value_changed = DragFloat(label, &v_f, v_speed, (float)v_min, (float)v_max, display_format);
7037 *v = (int)v_f;
7038 return value_changed;
7039 }
7040
DragIntN(const char * label,int * v,int components,float v_speed,int v_min,int v_max,const char * display_format)7041 bool ImGui::DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format)
7042 {
7043 ImGuiWindow* window = GetCurrentWindow();
7044 if (window->SkipItems)
7045 return false;
7046
7047 ImGuiContext& g = *GImGui;
7048 bool value_changed = false;
7049 BeginGroup();
7050 PushID(label);
7051 PushMultiItemsWidths(components);
7052 for (int i = 0; i < components; i++)
7053 {
7054 PushID(i);
7055 value_changed |= DragInt("##v", &v[i], v_speed, v_min, v_max, display_format);
7056 SameLine(0, g.Style.ItemInnerSpacing.x);
7057 PopID();
7058 PopItemWidth();
7059 }
7060 PopID();
7061
7062 TextUnformatted(label, FindRenderedTextEnd(label));
7063 EndGroup();
7064
7065 return value_changed;
7066 }
7067
DragInt2(const char * label,int v[2],float v_speed,int v_min,int v_max,const char * display_format)7068 bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* display_format)
7069 {
7070 return DragIntN(label, v, 2, v_speed, v_min, v_max, display_format);
7071 }
7072
DragInt3(const char * label,int v[3],float v_speed,int v_min,int v_max,const char * display_format)7073 bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* display_format)
7074 {
7075 return DragIntN(label, v, 3, v_speed, v_min, v_max, display_format);
7076 }
7077
DragInt4(const char * label,int v[4],float v_speed,int v_min,int v_max,const char * display_format)7078 bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* display_format)
7079 {
7080 return DragIntN(label, v, 4, v_speed, v_min, v_max, display_format);
7081 }
7082
DragIntRange2(const char * label,int * v_current_min,int * v_current_max,float v_speed,int v_min,int v_max,const char * display_format,const char * display_format_max)7083 bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* display_format, const char* display_format_max)
7084 {
7085 ImGuiWindow* window = GetCurrentWindow();
7086 if (window->SkipItems)
7087 return false;
7088
7089 ImGuiContext& g = *GImGui;
7090 PushID(label);
7091 BeginGroup();
7092 PushMultiItemsWidths(2);
7093
7094 bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format);
7095 PopItemWidth();
7096 SameLine(0, g.Style.ItemInnerSpacing.x);
7097 value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, display_format_max ? display_format_max : display_format);
7098 PopItemWidth();
7099 SameLine(0, g.Style.ItemInnerSpacing.x);
7100
7101 TextUnformatted(label, FindRenderedTextEnd(label));
7102 EndGroup();
7103 PopID();
7104
7105 return value_changed;
7106 }
7107
PlotEx(ImGuiPlotType plot_type,const char * label,float (* values_getter)(void * data,int idx),void * data,int values_count,int values_offset,const char * overlay_text,float scale_min,float scale_max,ImVec2 graph_size)7108 void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
7109 {
7110 ImGuiWindow* window = GetCurrentWindow();
7111 if (window->SkipItems)
7112 return;
7113
7114 ImGuiContext& g = *GImGui;
7115 const ImGuiStyle& style = g.Style;
7116
7117 const ImVec2 label_size = CalcTextSize(label, NULL, true);
7118 if (graph_size.x == 0.0f)
7119 graph_size.x = CalcItemWidth();
7120 if (graph_size.y == 0.0f)
7121 graph_size.y = label_size.y + (style.FramePadding.y * 2);
7122
7123 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y));
7124 const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
7125 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0));
7126 ItemSize(total_bb, style.FramePadding.y);
7127 if (!ItemAdd(total_bb, NULL))
7128 return;
7129
7130 // Determine scale from values if not specified
7131 if (scale_min == FLT_MAX || scale_max == FLT_MAX)
7132 {
7133 float v_min = FLT_MAX;
7134 float v_max = -FLT_MAX;
7135 for (int i = 0; i < values_count; i++)
7136 {
7137 const float v = values_getter(data, i);
7138 v_min = ImMin(v_min, v);
7139 v_max = ImMax(v_max, v);
7140 }
7141 if (scale_min == FLT_MAX)
7142 scale_min = v_min;
7143 if (scale_max == FLT_MAX)
7144 scale_max = v_max;
7145 }
7146
7147 RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
7148
7149 if (values_count > 0)
7150 {
7151 int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
7152 int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
7153
7154 // Tooltip on hover
7155 int v_hovered = -1;
7156 if (IsHovered(inner_bb, 0))
7157 {
7158 const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);
7159 const int v_idx = (int)(t * item_count);
7160 IM_ASSERT(v_idx >= 0 && v_idx < values_count);
7161
7162 const float v0 = values_getter(data, (v_idx + values_offset) % values_count);
7163 const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count);
7164 if (plot_type == ImGuiPlotType_Lines)
7165 SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1);
7166 else if (plot_type == ImGuiPlotType_Histogram)
7167 SetTooltip("%d: %8.4g", v_idx, v0);
7168 v_hovered = v_idx;
7169 }
7170
7171 const float t_step = 1.0f / (float)res_w;
7172
7173 float v0 = values_getter(data, (0 + values_offset) % values_count);
7174 float t0 = 0.0f;
7175 ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) / (scale_max - scale_min)) ); // Point in the normalized space of our target rectangle
7176
7177 const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram);
7178 const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered);
7179
7180 for (int n = 0; n < res_w; n++)
7181 {
7182 const float t1 = t0 + t_step;
7183 const int v1_idx = (int)(t0 * item_count + 0.5f);
7184 IM_ASSERT(v1_idx >= 0 && v1_idx < values_count);
7185 const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count);
7186 const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) / (scale_max - scale_min)) );
7187
7188 // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU.
7189 ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0);
7190 ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, 1.0f));
7191 if (plot_type == ImGuiPlotType_Lines)
7192 {
7193 window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
7194 }
7195 else if (plot_type == ImGuiPlotType_Histogram)
7196 {
7197 if (pos1.x >= pos0.x + 2.0f)
7198 pos1.x -= 1.0f;
7199 window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
7200 }
7201
7202 t0 = t1;
7203 tp0 = tp1;
7204 }
7205 }
7206
7207 // Text overlay
7208 if (overlay_text)
7209 RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f,0.0f));
7210
7211 if (label_size.x > 0.0f)
7212 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
7213 }
7214
7215 struct ImGuiPlotArrayGetterData
7216 {
7217 const float* Values;
7218 int Stride;
7219
ImGuiPlotArrayGetterDataImGuiPlotArrayGetterData7220 ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; }
7221 };
7222
Plot_ArrayGetter(void * data,int idx)7223 static float Plot_ArrayGetter(void* data, int idx)
7224 {
7225 ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data;
7226 const float v = *(float*)(void*)((unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);
7227 return v;
7228 }
7229
PlotLines(const char * label,const float * values,int values_count,int values_offset,const char * overlay_text,float scale_min,float scale_max,ImVec2 graph_size,int stride)7230 void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)
7231 {
7232 ImGuiPlotArrayGetterData data(values, stride);
7233 PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
7234 }
7235
PlotLines(const char * label,float (* values_getter)(void * data,int idx),void * data,int values_count,int values_offset,const char * overlay_text,float scale_min,float scale_max,ImVec2 graph_size)7236 void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
7237 {
7238 PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
7239 }
7240
PlotHistogram(const char * label,const float * values,int values_count,int values_offset,const char * overlay_text,float scale_min,float scale_max,ImVec2 graph_size,int stride)7241 void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)
7242 {
7243 ImGuiPlotArrayGetterData data(values, stride);
7244 PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
7245 }
7246
PlotHistogram(const char * label,float (* values_getter)(void * data,int idx),void * data,int values_count,int values_offset,const char * overlay_text,float scale_min,float scale_max,ImVec2 graph_size)7247 void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
7248 {
7249 PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
7250 }
7251
7252 // size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size
ProgressBar(float fraction,const ImVec2 & size_arg,const char * overlay)7253 void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay)
7254 {
7255 ImGuiWindow* window = GetCurrentWindow();
7256 if (window->SkipItems)
7257 return;
7258
7259 ImGuiContext& g = *GImGui;
7260 const ImGuiStyle& style = g.Style;
7261
7262 ImVec2 pos = window->DC.CursorPos;
7263 ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f));
7264 ItemSize(bb, style.FramePadding.y);
7265 if (!ItemAdd(bb, NULL))
7266 return;
7267
7268 // Render
7269 fraction = ImSaturate(fraction);
7270 RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
7271 bb.Reduce(ImVec2(window->BorderSize, window->BorderSize));
7272 const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y);
7273 RenderFrame(bb.Min, fill_br, GetColorU32(ImGuiCol_PlotHistogram), false, style.FrameRounding);
7274
7275 // Default displaying the fraction as percentage string, but user can override it
7276 char overlay_buf[32];
7277 if (!overlay)
7278 {
7279 ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f);
7280 overlay = overlay_buf;
7281 }
7282
7283 ImVec2 overlay_size = CalcTextSize(overlay, NULL);
7284 if (overlay_size.x > 0.0f)
7285 RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f,0.5f), &bb);
7286 }
7287
Checkbox(const char * label,bool * v)7288 bool ImGui::Checkbox(const char* label, bool* v)
7289 {
7290 ImGuiWindow* window = GetCurrentWindow();
7291 if (window->SkipItems)
7292 return false;
7293
7294 ImGuiContext& g = *GImGui;
7295 const ImGuiStyle& style = g.Style;
7296 const ImGuiID id = window->GetID(label);
7297 const ImVec2 label_size = CalcTextSize(label, NULL, true);
7298
7299 const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2, label_size.y + style.FramePadding.y*2));
7300 ItemSize(check_bb, style.FramePadding.y);
7301
7302 ImRect total_bb = check_bb;
7303 if (label_size.x > 0)
7304 SameLine(0, style.ItemInnerSpacing.x);
7305 const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size);
7306 if (label_size.x > 0)
7307 {
7308 ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
7309 total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max));
7310 }
7311
7312 if (!ItemAdd(total_bb, &id))
7313 return false;
7314
7315 bool hovered, held;
7316 bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
7317 if (pressed)
7318 *v = !(*v);
7319
7320 RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
7321 if (*v)
7322 {
7323 const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
7324 const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
7325 window->DrawList->AddRectFilled(check_bb.Min+ImVec2(pad,pad), check_bb.Max-ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), style.FrameRounding);
7326 }
7327
7328 if (g.LogEnabled)
7329 LogRenderedText(text_bb.GetTL(), *v ? "[x]" : "[ ]");
7330 if (label_size.x > 0.0f)
7331 RenderText(text_bb.GetTL(), label);
7332
7333 return pressed;
7334 }
7335
CheckboxFlags(const char * label,unsigned int * flags,unsigned int flags_value)7336 bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value)
7337 {
7338 bool v = ((*flags & flags_value) == flags_value);
7339 bool pressed = Checkbox(label, &v);
7340 if (pressed)
7341 {
7342 if (v)
7343 *flags |= flags_value;
7344 else
7345 *flags &= ~flags_value;
7346 }
7347
7348 return pressed;
7349 }
7350
RadioButton(const char * label,bool active)7351 bool ImGui::RadioButton(const char* label, bool active)
7352 {
7353 ImGuiWindow* window = GetCurrentWindow();
7354 if (window->SkipItems)
7355 return false;
7356
7357 ImGuiContext& g = *GImGui;
7358 const ImGuiStyle& style = g.Style;
7359 const ImGuiID id = window->GetID(label);
7360 const ImVec2 label_size = CalcTextSize(label, NULL, true);
7361
7362 const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2-1, label_size.y + style.FramePadding.y*2-1));
7363 ItemSize(check_bb, style.FramePadding.y);
7364
7365 ImRect total_bb = check_bb;
7366 if (label_size.x > 0)
7367 SameLine(0, style.ItemInnerSpacing.x);
7368 const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size);
7369 if (label_size.x > 0)
7370 {
7371 ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
7372 total_bb.Add(text_bb);
7373 }
7374
7375 if (!ItemAdd(total_bb, &id))
7376 return false;
7377
7378 ImVec2 center = check_bb.GetCenter();
7379 center.x = (float)(int)center.x + 0.5f;
7380 center.y = (float)(int)center.y + 0.5f;
7381 const float radius = check_bb.GetHeight() * 0.5f;
7382
7383 bool hovered, held;
7384 bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
7385
7386 window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16);
7387 if (active)
7388 {
7389 const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
7390 const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
7391 window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16);
7392 }
7393
7394 if (window->Flags & ImGuiWindowFlags_ShowBorders)
7395 {
7396 window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16);
7397 window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16);
7398 }
7399
7400 if (g.LogEnabled)
7401 LogRenderedText(text_bb.GetTL(), active ? "(x)" : "( )");
7402 if (label_size.x > 0.0f)
7403 RenderText(text_bb.GetTL(), label);
7404
7405 return pressed;
7406 }
7407
RadioButton(const char * label,int * v,int v_button)7408 bool ImGui::RadioButton(const char* label, int* v, int v_button)
7409 {
7410 const bool pressed = RadioButton(label, *v == v_button);
7411 if (pressed)
7412 {
7413 *v = v_button;
7414 }
7415 return pressed;
7416 }
7417
InputTextCalcTextLenAndLineCount(const char * text_begin,const char ** out_text_end)7418 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
7419 {
7420 int line_count = 0;
7421 const char* s = text_begin;
7422 while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding
7423 if (c == '\n')
7424 line_count++;
7425 s--;
7426 if (s[0] != '\n' && s[0] != '\r')
7427 line_count++;
7428 *out_text_end = s;
7429 return line_count;
7430 }
7431
InputTextCalcTextSizeW(const ImWchar * text_begin,const ImWchar * text_end,const ImWchar ** remaining,ImVec2 * out_offset,bool stop_on_new_line)7432 static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line)
7433 {
7434 ImFont* font = GImGui->Font;
7435 const float line_height = GImGui->FontSize;
7436 const float scale = line_height / font->FontSize;
7437
7438 ImVec2 text_size = ImVec2(0,0);
7439 float line_width = 0.0f;
7440
7441 const ImWchar* s = text_begin;
7442 while (s < text_end)
7443 {
7444 unsigned int c = (unsigned int)(*s++);
7445 if (c == '\n')
7446 {
7447 text_size.x = ImMax(text_size.x, line_width);
7448 text_size.y += line_height;
7449 line_width = 0.0f;
7450 if (stop_on_new_line)
7451 break;
7452 continue;
7453 }
7454 if (c == '\r')
7455 continue;
7456
7457 const float char_width = font->GetCharAdvance((unsigned short)c) * scale;
7458 line_width += char_width;
7459 }
7460
7461 if (text_size.x < line_width)
7462 text_size.x = line_width;
7463
7464 if (out_offset)
7465 *out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n
7466
7467 if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n
7468 text_size.y += line_height;
7469
7470 if (remaining)
7471 *remaining = s;
7472
7473 return text_size;
7474 }
7475
7476 // Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar)
7477 namespace ImGuiStb
7478 {
7479
STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING * obj)7480 static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; }
STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING * obj,int idx)7481 static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->Text[idx]; }
STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING * obj,int line_start_idx,int char_idx)7482 static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->Text[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); }
STB_TEXTEDIT_KEYTOTEXT(int key)7483 static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; }
7484 static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
STB_TEXTEDIT_LAYOUTROW(StbTexteditRow * r,STB_TEXTEDIT_STRING * obj,int line_start_idx)7485 static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
7486 {
7487 const ImWchar* text = obj->Text.Data;
7488 const ImWchar* text_remaining = NULL;
7489 const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true);
7490 r->x0 = 0.0f;
7491 r->x1 = size.x;
7492 r->baseline_y_delta = size.y;
7493 r->ymin = 0.0f;
7494 r->ymax = size.y;
7495 r->num_chars = (int)(text_remaining - (text + line_start_idx));
7496 }
7497
is_separator(unsigned int c)7498 static bool is_separator(unsigned int c) { return ImCharIsSpace(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; }
is_word_boundary_from_right(STB_TEXTEDIT_STRING * obj,int idx)7499 static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator( obj->Text[idx-1] ) && !is_separator( obj->Text[idx] ) ) : 1; }
STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING * obj,int idx)7500 static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
7501 #ifdef __APPLE__ // FIXME: Move setting to IO structure
is_word_boundary_from_left(STB_TEXTEDIT_STRING * obj,int idx)7502 static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator( obj->Text[idx-1] ) && is_separator( obj->Text[idx] ) ) : 1; }
STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING * obj,int idx)7503 static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
7504 #else
STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING * obj,int idx)7505 static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
7506 #endif
7507 #define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h
7508 #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
7509
STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING * obj,int pos,int n)7510 static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
7511 {
7512 ImWchar* dst = obj->Text.Data + pos;
7513
7514 // We maintain our buffer length in both UTF-8 and wchar formats
7515 obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);
7516 obj->CurLenW -= n;
7517
7518 // Offset remaining text
7519 const ImWchar* src = obj->Text.Data + pos + n;
7520 while (ImWchar c = *src++)
7521 *dst++ = c;
7522 *dst = '\0';
7523 }
7524
STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING * obj,int pos,const ImWchar * new_text,int new_text_len)7525 static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len)
7526 {
7527 const int text_len = obj->CurLenW;
7528 IM_ASSERT(pos <= text_len);
7529 if (new_text_len + text_len + 1 > obj->Text.Size)
7530 return false;
7531
7532 const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
7533 if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA)
7534 return false;
7535
7536 ImWchar* text = obj->Text.Data;
7537 if (pos != text_len)
7538 memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));
7539 memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
7540
7541 obj->CurLenW += new_text_len;
7542 obj->CurLenA += new_text_len_utf8;
7543 obj->Text[obj->CurLenW] = '\0';
7544
7545 return true;
7546 }
7547
7548 // We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols)
7549 #define STB_TEXTEDIT_K_LEFT 0x10000 // keyboard input to move cursor left
7550 #define STB_TEXTEDIT_K_RIGHT 0x10001 // keyboard input to move cursor right
7551 #define STB_TEXTEDIT_K_UP 0x10002 // keyboard input to move cursor up
7552 #define STB_TEXTEDIT_K_DOWN 0x10003 // keyboard input to move cursor down
7553 #define STB_TEXTEDIT_K_LINESTART 0x10004 // keyboard input to move cursor to start of line
7554 #define STB_TEXTEDIT_K_LINEEND 0x10005 // keyboard input to move cursor to end of line
7555 #define STB_TEXTEDIT_K_TEXTSTART 0x10006 // keyboard input to move cursor to start of text
7556 #define STB_TEXTEDIT_K_TEXTEND 0x10007 // keyboard input to move cursor to end of text
7557 #define STB_TEXTEDIT_K_DELETE 0x10008 // keyboard input to delete selection or character under cursor
7558 #define STB_TEXTEDIT_K_BACKSPACE 0x10009 // keyboard input to delete selection or character left of cursor
7559 #define STB_TEXTEDIT_K_UNDO 0x1000A // keyboard input to perform undo
7560 #define STB_TEXTEDIT_K_REDO 0x1000B // keyboard input to perform redo
7561 #define STB_TEXTEDIT_K_WORDLEFT 0x1000C // keyboard input to move cursor left one word
7562 #define STB_TEXTEDIT_K_WORDRIGHT 0x1000D // keyboard input to move cursor right one word
7563 #define STB_TEXTEDIT_K_SHIFT 0x20000
7564
7565 #define STB_TEXTEDIT_IMPLEMENTATION
7566 #include "stb_textedit.h"
7567
7568 }
7569
OnKeyPressed(int key)7570 void ImGuiTextEditState::OnKeyPressed(int key)
7571 {
7572 stb_textedit_key(this, &StbState, key);
7573 CursorFollow = true;
7574 CursorAnimReset();
7575 }
7576
7577 // Public API to manipulate UTF-8 text
7578 // We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar)
7579 // FIXME: The existence of this rarely exercised code path is a bit of a nuisance.
DeleteChars(int pos,int bytes_count)7580 void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count)
7581 {
7582 IM_ASSERT(pos + bytes_count <= BufTextLen);
7583 char* dst = Buf + pos;
7584 const char* src = Buf + pos + bytes_count;
7585 while (char c = *src++)
7586 *dst++ = c;
7587 *dst = '\0';
7588
7589 if (CursorPos + bytes_count >= pos)
7590 CursorPos -= bytes_count;
7591 else if (CursorPos >= pos)
7592 CursorPos = pos;
7593 SelectionStart = SelectionEnd = CursorPos;
7594 BufDirty = true;
7595 BufTextLen -= bytes_count;
7596 }
7597
InsertChars(int pos,const char * new_text,const char * new_text_end)7598 void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)
7599 {
7600 const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
7601 if (new_text_len + BufTextLen + 1 >= BufSize)
7602 return;
7603
7604 if (BufTextLen != pos)
7605 memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos));
7606 memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char));
7607 Buf[BufTextLen + new_text_len] = '\0';
7608
7609 if (CursorPos >= pos)
7610 CursorPos += new_text_len;
7611 SelectionStart = SelectionEnd = CursorPos;
7612 BufDirty = true;
7613 BufTextLen += new_text_len;
7614 }
7615
7616 // Return false to discard a character.
InputTextFilterCharacter(unsigned int * p_char,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)7617 static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
7618 {
7619 unsigned int c = *p_char;
7620
7621 if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF)))
7622 {
7623 bool pass = false;
7624 pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline));
7625 pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput));
7626 if (!pass)
7627 return false;
7628 }
7629
7630 if (c >= 0xE000 && c <= 0xF8FF) // Filter private Unicode range. I don't imagine anybody would want to input them. GLFW on OSX seems to send private characters for special keys like arrow keys.
7631 return false;
7632
7633 if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank))
7634 {
7635 if (flags & ImGuiInputTextFlags_CharsDecimal)
7636 if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/'))
7637 return false;
7638
7639 if (flags & ImGuiInputTextFlags_CharsHexadecimal)
7640 if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F'))
7641 return false;
7642
7643 if (flags & ImGuiInputTextFlags_CharsUppercase)
7644 if (c >= 'a' && c <= 'z')
7645 *p_char = (c += (unsigned int)('A'-'a'));
7646
7647 if (flags & ImGuiInputTextFlags_CharsNoBlank)
7648 if (ImCharIsSpace(c))
7649 return false;
7650 }
7651
7652 if (flags & ImGuiInputTextFlags_CallbackCharFilter)
7653 {
7654 ImGuiTextEditCallbackData callback_data;
7655 memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
7656 callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;
7657 callback_data.EventChar = (ImWchar)c;
7658 callback_data.Flags = flags;
7659 callback_data.UserData = user_data;
7660 if (callback(&callback_data) != 0)
7661 return false;
7662 *p_char = callback_data.EventChar;
7663 if (!callback_data.EventChar)
7664 return false;
7665 }
7666
7667 return true;
7668 }
7669
7670 // Edit a string of text
7671 // NB: when active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while active has no effect.
7672 // FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188
InputTextEx(const char * label,char * buf,int buf_size,const ImVec2 & size_arg,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)7673 bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
7674 {
7675 ImGuiWindow* window = GetCurrentWindow();
7676 if (window->SkipItems)
7677 return false;
7678
7679 IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys)
7680 IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)
7681
7682 ImGuiContext& g = *GImGui;
7683 const ImGuiIO& io = g.IO;
7684 const ImGuiStyle& style = g.Style;
7685
7686 const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;
7687 const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0;
7688 const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
7689
7690 if (is_multiline) // Open group before calling GetID() because groups tracks id created during their spawn
7691 BeginGroup();
7692 const ImGuiID id = window->GetID(label);
7693 const ImVec2 label_size = CalcTextSize(label, NULL, true);
7694 ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line
7695 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
7696 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f, 0.0f));
7697
7698 ImGuiWindow* draw_window = window;
7699 if (is_multiline)
7700 {
7701 if (!BeginChildFrame(id, frame_bb.GetSize()))
7702 {
7703 EndChildFrame();
7704 EndGroup();
7705 return false;
7706 }
7707 draw_window = GetCurrentWindow();
7708 size.x -= draw_window->ScrollbarSizes.x;
7709 }
7710 else
7711 {
7712 ItemSize(total_bb, style.FramePadding.y);
7713 if (!ItemAdd(total_bb, &id))
7714 return false;
7715 }
7716
7717 // Password pushes a temporary font with only a fallback glyph
7718 if (is_password)
7719 {
7720 const ImFont::Glyph* glyph = g.Font->FindGlyph('*');
7721 ImFont* password_font = &g.InputTextPasswordFont;
7722 password_font->FontSize = g.Font->FontSize;
7723 password_font->Scale = g.Font->Scale;
7724 password_font->DisplayOffset = g.Font->DisplayOffset;
7725 password_font->Ascent = g.Font->Ascent;
7726 password_font->Descent = g.Font->Descent;
7727 password_font->ContainerAtlas = g.Font->ContainerAtlas;
7728 password_font->FallbackGlyph = glyph;
7729 password_font->FallbackXAdvance = glyph->XAdvance;
7730 IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexXAdvance.empty() && password_font->IndexLookup.empty());
7731 PushFont(password_font);
7732 }
7733
7734 // NB: we are only allowed to access 'edit_state' if we are the active widget.
7735 ImGuiTextEditState& edit_state = g.InputTextState;
7736
7737 const bool focus_requested = FocusableItemRegister(window, g.ActiveId == id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing
7738 const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent);
7739 const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code;
7740
7741 const bool hovered = IsHovered(frame_bb, id);
7742 if (hovered)
7743 {
7744 SetHoveredID(id);
7745 g.MouseCursor = ImGuiMouseCursor_TextInput;
7746 }
7747 const bool user_clicked = hovered && io.MouseClicked[0];
7748 const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY");
7749
7750 bool select_all = (g.ActiveId != id) && (flags & ImGuiInputTextFlags_AutoSelectAll) != 0;
7751 if (focus_requested || user_clicked || user_scrolled)
7752 {
7753 if (g.ActiveId != id)
7754 {
7755 // Start edition
7756 // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)
7757 // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode)
7758 const int prev_len_w = edit_state.CurLenW;
7759 edit_state.Text.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash.
7760 edit_state.InitialText.resize(buf_size+1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash.
7761 ImStrncpy(edit_state.InitialText.Data, buf, edit_state.InitialText.Size);
7762 const char* buf_end = NULL;
7763 edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
7764 edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImFormatString() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8.
7765 edit_state.CursorAnimReset();
7766
7767 // Preserve cursor position and undo/redo stack if we come back to same widget
7768 // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar).
7769 const bool recycle_state = (edit_state.Id == id) && (prev_len_w == edit_state.CurLenW);
7770 if (recycle_state)
7771 {
7772 // Recycle existing cursor/selection/undo stack but clamp position
7773 // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
7774 edit_state.CursorClamp();
7775 }
7776 else
7777 {
7778 edit_state.Id = id;
7779 edit_state.ScrollX = 0.0f;
7780 stb_textedit_initialize_state(&edit_state.StbState, !is_multiline);
7781 if (!is_multiline && focus_requested_by_code)
7782 select_all = true;
7783 }
7784 if (flags & ImGuiInputTextFlags_AlwaysInsertMode)
7785 edit_state.StbState.insert_mode = true;
7786 if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl)))
7787 select_all = true;
7788 }
7789 SetActiveID(id, window);
7790 FocusWindow(window);
7791 }
7792 else if (io.MouseClicked[0])
7793 {
7794 // Release focus when we click outside
7795 if (g.ActiveId == id)
7796 ClearActiveID();
7797 }
7798
7799 bool value_changed = false;
7800 bool enter_pressed = false;
7801
7802 if (g.ActiveId == id)
7803 {
7804 if (!is_editable && !g.ActiveIdIsJustActivated)
7805 {
7806 // When read-only we always use the live data passed to the function
7807 edit_state.Text.resize(buf_size+1);
7808 const char* buf_end = NULL;
7809 edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
7810 edit_state.CurLenA = (int)(buf_end - buf);
7811 edit_state.CursorClamp();
7812 }
7813
7814 edit_state.BufSizeA = buf_size;
7815
7816 // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
7817 // Down the line we should have a cleaner library-wide concept of Selected vs Active.
7818 g.ActiveIdAllowOverlap = !io.MouseDown[0];
7819
7820 // Edit in progress
7821 const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX;
7822 const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f));
7823
7824 const bool osx_double_click_selects_words = io.OSXBehaviors; // OS X style: Double click selects by word instead of selecting whole text
7825 if (select_all || (hovered && !osx_double_click_selects_words && io.MouseDoubleClicked[0]))
7826 {
7827 edit_state.SelectAll();
7828 edit_state.SelectedAllMouseLock = true;
7829 }
7830 else if (hovered && osx_double_click_selects_words && io.MouseDoubleClicked[0])
7831 {
7832 // Select a word only, OS X style (by simulating keystrokes)
7833 edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT);
7834 edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);
7835 }
7836 else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock)
7837 {
7838 stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
7839 edit_state.CursorAnimReset();
7840 }
7841 else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f))
7842 {
7843 stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
7844 edit_state.CursorAnimReset();
7845 edit_state.CursorFollow = true;
7846 }
7847 if (edit_state.SelectedAllMouseLock && !io.MouseDown[0])
7848 edit_state.SelectedAllMouseLock = false;
7849
7850 if (io.InputCharacters[0])
7851 {
7852 // Process text input (before we check for Return because using some IME will effectively send a Return?)
7853 // We ignore CTRL inputs, but need to allow CTRL+ALT as some keyboards (e.g. German) use AltGR - which is Alt+Ctrl - to input certain characters.
7854 if (!(io.KeyCtrl && !io.KeyAlt) && is_editable)
7855 {
7856 for (int n = 0; n < IM_ARRAYSIZE(io.InputCharacters) && io.InputCharacters[n]; n++)
7857 if (unsigned int c = (unsigned int)io.InputCharacters[n])
7858 {
7859 // Insert character if they pass filtering
7860 if (!InputTextFilterCharacter(&c, flags, callback, user_data))
7861 continue;
7862 edit_state.OnKeyPressed((int)c);
7863 }
7864 }
7865
7866 // Consume characters
7867 memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
7868 }
7869
7870 // Handle various key-presses
7871 bool cancel_edit = false;
7872 const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
7873 const bool is_shortcut_key_only = (io.OSXBehaviors ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl
7874 const bool is_wordmove_key_down = io.OSXBehaviors ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl
7875 const bool is_startend_key_down = io.OSXBehaviors && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
7876
7877 if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
7878 else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
7879 else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
7880 else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }
7881 else if (IsKeyPressedMap(ImGuiKey_Home)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
7882 else if (IsKeyPressedMap(ImGuiKey_End)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
7883 else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
7884 else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable)
7885 {
7886 if (!edit_state.HasSelection())
7887 {
7888 if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT);
7889 else if (io.OSXBehaviors && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT);
7890 }
7891 edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);
7892 }
7893 else if (IsKeyPressedMap(ImGuiKey_Enter))
7894 {
7895 bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;
7896 if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl))
7897 {
7898 ClearActiveID();
7899 enter_pressed = true;
7900 }
7901 else if (is_editable)
7902 {
7903 unsigned int c = '\n'; // Insert new line
7904 if (InputTextFilterCharacter(&c, flags, callback, user_data))
7905 edit_state.OnKeyPressed((int)c);
7906 }
7907 }
7908 else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable)
7909 {
7910 unsigned int c = '\t'; // Insert TAB
7911 if (InputTextFilterCharacter(&c, flags, callback, user_data))
7912 edit_state.OnKeyPressed((int)c);
7913 }
7914 else if (IsKeyPressedMap(ImGuiKey_Escape)) { ClearActiveID(); cancel_edit = true; }
7915 else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); }
7916 else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); }
7917 else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_A)) { edit_state.SelectAll(); edit_state.CursorFollow = true; }
7918 else if (is_shortcut_key_only && !is_password && ((IsKeyPressedMap(ImGuiKey_X) && is_editable) || IsKeyPressedMap(ImGuiKey_C)) && (!is_multiline || edit_state.HasSelection()))
7919 {
7920 // Cut, Copy
7921 const bool cut = IsKeyPressedMap(ImGuiKey_X);
7922 if (cut && !edit_state.HasSelection())
7923 edit_state.SelectAll();
7924
7925 if (io.SetClipboardTextFn)
7926 {
7927 const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0;
7928 const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW;
7929 edit_state.TempTextBuffer.resize((ie-ib) * 4 + 1);
7930 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data+ib, edit_state.Text.Data+ie);
7931 SetClipboardText(edit_state.TempTextBuffer.Data);
7932 }
7933
7934 if (cut)
7935 {
7936 edit_state.CursorFollow = true;
7937 stb_textedit_cut(&edit_state, &edit_state.StbState);
7938 }
7939 }
7940 else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_V) && is_editable)
7941 {
7942 // Paste
7943 if (const char* clipboard = GetClipboardText())
7944 {
7945 // Filter pasted buffer
7946 const int clipboard_len = (int)strlen(clipboard);
7947 ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar));
7948 int clipboard_filtered_len = 0;
7949 for (const char* s = clipboard; *s; )
7950 {
7951 unsigned int c;
7952 s += ImTextCharFromUtf8(&c, s, NULL);
7953 if (c == 0)
7954 break;
7955 if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, user_data))
7956 continue;
7957 clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;
7958 }
7959 clipboard_filtered[clipboard_filtered_len] = 0;
7960 if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation
7961 {
7962 stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len);
7963 edit_state.CursorFollow = true;
7964 }
7965 ImGui::MemFree(clipboard_filtered);
7966 }
7967 }
7968
7969 if (cancel_edit)
7970 {
7971 // Restore initial value
7972 if (is_editable)
7973 {
7974 ImStrncpy(buf, edit_state.InitialText.Data, buf_size);
7975 value_changed = true;
7976 }
7977 }
7978 else
7979 {
7980 // Apply new value immediately - copy modified buffer back
7981 // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer
7982 // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect.
7983 // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks.
7984 if (is_editable)
7985 {
7986 edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4);
7987 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL);
7988 }
7989
7990 // User callback
7991 if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0)
7992 {
7993 IM_ASSERT(callback != NULL);
7994
7995 // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
7996 ImGuiInputTextFlags event_flag = 0;
7997 ImGuiKey event_key = ImGuiKey_COUNT;
7998 if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab))
7999 {
8000 event_flag = ImGuiInputTextFlags_CallbackCompletion;
8001 event_key = ImGuiKey_Tab;
8002 }
8003 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow))
8004 {
8005 event_flag = ImGuiInputTextFlags_CallbackHistory;
8006 event_key = ImGuiKey_UpArrow;
8007 }
8008 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow))
8009 {
8010 event_flag = ImGuiInputTextFlags_CallbackHistory;
8011 event_key = ImGuiKey_DownArrow;
8012 }
8013 else if (flags & ImGuiInputTextFlags_CallbackAlways)
8014 event_flag = ImGuiInputTextFlags_CallbackAlways;
8015
8016 if (event_flag)
8017 {
8018 ImGuiTextEditCallbackData callback_data;
8019 memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
8020 callback_data.EventFlag = event_flag;
8021 callback_data.Flags = flags;
8022 callback_data.UserData = user_data;
8023 callback_data.ReadOnly = !is_editable;
8024
8025 callback_data.EventKey = event_key;
8026 callback_data.Buf = edit_state.TempTextBuffer.Data;
8027 callback_data.BufTextLen = edit_state.CurLenA;
8028 callback_data.BufSize = edit_state.BufSizeA;
8029 callback_data.BufDirty = false;
8030
8031 // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188)
8032 ImWchar* text = edit_state.Text.Data;
8033 const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor);
8034 const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start);
8035 const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end);
8036
8037 // Call user code
8038 callback(&callback_data);
8039
8040 // Read back what user may have modified
8041 IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data); // Invalid to modify those fields
8042 IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA);
8043 IM_ASSERT(callback_data.Flags == flags);
8044 if (callback_data.CursorPos != utf8_cursor_pos) edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos);
8045 if (callback_data.SelectionStart != utf8_selection_start) edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart);
8046 if (callback_data.SelectionEnd != utf8_selection_end) edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd);
8047 if (callback_data.BufDirty)
8048 {
8049 IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
8050 edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, callback_data.Buf, NULL);
8051 edit_state.CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
8052 edit_state.CursorAnimReset();
8053 }
8054 }
8055 }
8056
8057 // Copy back to user buffer
8058 if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0)
8059 {
8060 ImStrncpy(buf, edit_state.TempTextBuffer.Data, buf_size);
8061 value_changed = true;
8062 }
8063 }
8064 }
8065
8066 // Render
8067 // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on.
8068 const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf; buf = NULL;
8069
8070 if (!is_multiline)
8071 RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
8072
8073 const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size
8074 ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;
8075 ImVec2 text_size(0.f, 0.f);
8076 const bool is_currently_scrolling = (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY"));
8077 if (g.ActiveId == id || is_currently_scrolling)
8078 {
8079 edit_state.CursorAnim += io.DeltaTime;
8080
8081 // This is going to be messy. We need to:
8082 // - Display the text (this alone can be more easily clipped)
8083 // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)
8084 // - Measure text height (for scrollbar)
8085 // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)
8086 // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
8087 const ImWchar* text_begin = edit_state.Text.Data;
8088 ImVec2 cursor_offset, select_start_offset;
8089
8090 {
8091 // Count lines + find lines numbers straddling 'cursor' and 'select_start' position.
8092 const ImWchar* searches_input_ptr[2];
8093 searches_input_ptr[0] = text_begin + edit_state.StbState.cursor;
8094 searches_input_ptr[1] = NULL;
8095 int searches_remaining = 1;
8096 int searches_result_line_number[2] = { -1, -999 };
8097 if (edit_state.StbState.select_start != edit_state.StbState.select_end)
8098 {
8099 searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
8100 searches_result_line_number[1] = -1;
8101 searches_remaining++;
8102 }
8103
8104 // Iterate all lines to find our line numbers
8105 // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter.
8106 searches_remaining += is_multiline ? 1 : 0;
8107 int line_count = 0;
8108 for (const ImWchar* s = text_begin; *s != 0; s++)
8109 if (*s == '\n')
8110 {
8111 line_count++;
8112 if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; }
8113 if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; }
8114 }
8115 line_count++;
8116 if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count;
8117 if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count;
8118
8119 // Calculate 2d position by finding the beginning of the line and measuring distance
8120 cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;
8121 cursor_offset.y = searches_result_line_number[0] * g.FontSize;
8122 if (searches_result_line_number[1] >= 0)
8123 {
8124 select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;
8125 select_start_offset.y = searches_result_line_number[1] * g.FontSize;
8126 }
8127
8128 // Calculate text height
8129 if (is_multiline)
8130 text_size = ImVec2(size.x, line_count * g.FontSize);
8131 }
8132
8133 // Scroll
8134 if (edit_state.CursorFollow)
8135 {
8136 // Horizontal scroll in chunks of quarter width
8137 if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll))
8138 {
8139 const float scroll_increment_x = size.x * 0.25f;
8140 if (cursor_offset.x < edit_state.ScrollX)
8141 edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x);
8142 else if (cursor_offset.x - size.x >= edit_state.ScrollX)
8143 edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x);
8144 }
8145 else
8146 {
8147 edit_state.ScrollX = 0.0f;
8148 }
8149
8150 // Vertical scroll
8151 if (is_multiline)
8152 {
8153 float scroll_y = draw_window->Scroll.y;
8154 if (cursor_offset.y - g.FontSize < scroll_y)
8155 scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
8156 else if (cursor_offset.y - size.y >= scroll_y)
8157 scroll_y = cursor_offset.y - size.y;
8158 draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y); // To avoid a frame of lag
8159 draw_window->Scroll.y = scroll_y;
8160 render_pos.y = draw_window->DC.CursorPos.y;
8161 }
8162 }
8163 edit_state.CursorFollow = false;
8164 const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f);
8165
8166 // Draw selection
8167 if (edit_state.StbState.select_start != edit_state.StbState.select_end)
8168 {
8169 const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
8170 const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end);
8171
8172 float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection.
8173 float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
8174 ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg);
8175 ImVec2 rect_pos = render_pos + select_start_offset - render_scroll;
8176 for (const ImWchar* p = text_selected_begin; p < text_selected_end; )
8177 {
8178 if (rect_pos.y > clip_rect.w + g.FontSize)
8179 break;
8180 if (rect_pos.y < clip_rect.y)
8181 {
8182 while (p < text_selected_end)
8183 if (*p++ == '\n')
8184 break;
8185 }
8186 else
8187 {
8188 ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true);
8189 if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((unsigned short)' ') * 0.50f); // So we can see selected empty lines
8190 ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn));
8191 rect.Clip(clip_rect);
8192 if (rect.Overlaps(clip_rect))
8193 draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
8194 }
8195 rect_pos.x = render_pos.x - render_scroll.x;
8196 rect_pos.y += g.FontSize;
8197 }
8198 }
8199
8200 draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect);
8201
8202 // Draw blinking cursor
8203 bool cursor_is_visible = (g.InputTextState.CursorAnim <= 0.0f) || fmodf(g.InputTextState.CursorAnim, 1.20f) <= 0.80f;
8204 ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll;
8205 ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y-g.FontSize+0.5f, cursor_screen_pos.x+1.0f, cursor_screen_pos.y-1.5f);
8206 if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
8207 draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
8208
8209 // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
8210 if (is_editable)
8211 g.OsImePosRequest = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize);
8212 }
8213 else
8214 {
8215 // Render text only
8216 const char* buf_end = NULL;
8217 if (is_multiline)
8218 text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width
8219 draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect);
8220 }
8221
8222 if (is_multiline)
8223 {
8224 Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line
8225 EndChildFrame();
8226 EndGroup();
8227 }
8228
8229 if (is_password)
8230 PopFont();
8231
8232 // Log as text
8233 if (g.LogEnabled && !is_password)
8234 LogRenderedText(render_pos, buf_display, NULL);
8235
8236 if (label_size.x > 0)
8237 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
8238
8239 if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)
8240 return enter_pressed;
8241 else
8242 return value_changed;
8243 }
8244
InputText(const char * label,char * buf,size_t buf_size,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)8245 bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
8246 {
8247 IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline()
8248 return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data);
8249 }
8250
InputTextMultiline(const char * label,char * buf,size_t buf_size,const ImVec2 & size,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)8251 bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
8252 {
8253 return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);
8254 }
8255
8256 // NB: scalar_format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "display_format" argument)
InputScalarEx(const char * label,ImGuiDataType data_type,void * data_ptr,void * step_ptr,void * step_fast_ptr,const char * scalar_format,ImGuiInputTextFlags extra_flags)8257 bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags)
8258 {
8259 ImGuiWindow* window = GetCurrentWindow();
8260 if (window->SkipItems)
8261 return false;
8262
8263 ImGuiContext& g = *GImGui;
8264 const ImGuiStyle& style = g.Style;
8265 const ImVec2 label_size = CalcTextSize(label, NULL, true);
8266
8267 BeginGroup();
8268 PushID(label);
8269 const ImVec2 button_sz = ImVec2(g.FontSize, g.FontSize) + style.FramePadding*2.0f;
8270 if (step_ptr)
8271 PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_sz.x + style.ItemInnerSpacing.x)*2));
8272
8273 char buf[64];
8274 DataTypeFormatString(data_type, data_ptr, scalar_format, buf, IM_ARRAYSIZE(buf));
8275
8276 bool value_changed = false;
8277 if (!(extra_flags & ImGuiInputTextFlags_CharsHexadecimal))
8278 extra_flags |= ImGuiInputTextFlags_CharsDecimal;
8279 extra_flags |= ImGuiInputTextFlags_AutoSelectAll;
8280 if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags)) // PushId(label) + "" gives us the expected ID from outside point of view
8281 value_changed = DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, scalar_format);
8282
8283 // Step buttons
8284 if (step_ptr)
8285 {
8286 PopItemWidth();
8287 SameLine(0, style.ItemInnerSpacing.x);
8288 if (ButtonEx("-", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
8289 {
8290 DataTypeApplyOp(data_type, '-', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
8291 value_changed = true;
8292 }
8293 SameLine(0, style.ItemInnerSpacing.x);
8294 if (ButtonEx("+", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
8295 {
8296 DataTypeApplyOp(data_type, '+', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
8297 value_changed = true;
8298 }
8299 }
8300 PopID();
8301
8302 if (label_size.x > 0)
8303 {
8304 SameLine(0, style.ItemInnerSpacing.x);
8305 RenderText(ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + style.FramePadding.y), label);
8306 ItemSize(label_size, style.FramePadding.y);
8307 }
8308 EndGroup();
8309
8310 return value_changed;
8311 }
8312
InputFloat(const char * label,float * v,float step,float step_fast,int decimal_precision,ImGuiInputTextFlags extra_flags)8313 bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags)
8314 {
8315 char display_format[16];
8316 if (decimal_precision < 0)
8317 strcpy(display_format, "%f"); // Ideally we'd have a minimum decimal precision of 1 to visually denote that this is a float, while hiding non-significant digits? %f doesn't have a minimum of 1
8318 else
8319 ImFormatString(display_format, IM_ARRAYSIZE(display_format), "%%.%df", decimal_precision);
8320 return InputScalarEx(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), display_format, extra_flags);
8321 }
8322
InputInt(const char * label,int * v,int step,int step_fast,ImGuiInputTextFlags extra_flags)8323 bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags)
8324 {
8325 // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes.
8326 const char* scalar_format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d";
8327 return InputScalarEx(label, ImGuiDataType_Int, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), scalar_format, extra_flags);
8328 }
8329
InputFloatN(const char * label,float * v,int components,int decimal_precision,ImGuiInputTextFlags extra_flags)8330 bool ImGui::InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags)
8331 {
8332 ImGuiWindow* window = GetCurrentWindow();
8333 if (window->SkipItems)
8334 return false;
8335
8336 ImGuiContext& g = *GImGui;
8337 bool value_changed = false;
8338 BeginGroup();
8339 PushID(label);
8340 PushMultiItemsWidths(components);
8341 for (int i = 0; i < components; i++)
8342 {
8343 PushID(i);
8344 value_changed |= InputFloat("##v", &v[i], 0, 0, decimal_precision, extra_flags);
8345 SameLine(0, g.Style.ItemInnerSpacing.x);
8346 PopID();
8347 PopItemWidth();
8348 }
8349 PopID();
8350
8351 window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
8352 TextUnformatted(label, FindRenderedTextEnd(label));
8353 EndGroup();
8354
8355 return value_changed;
8356 }
8357
InputFloat2(const char * label,float v[2],int decimal_precision,ImGuiInputTextFlags extra_flags)8358 bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags)
8359 {
8360 return InputFloatN(label, v, 2, decimal_precision, extra_flags);
8361 }
8362
InputFloat3(const char * label,float v[3],int decimal_precision,ImGuiInputTextFlags extra_flags)8363 bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags)
8364 {
8365 return InputFloatN(label, v, 3, decimal_precision, extra_flags);
8366 }
8367
InputFloat4(const char * label,float v[4],int decimal_precision,ImGuiInputTextFlags extra_flags)8368 bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags)
8369 {
8370 return InputFloatN(label, v, 4, decimal_precision, extra_flags);
8371 }
8372
InputIntN(const char * label,int * v,int components,ImGuiInputTextFlags extra_flags)8373 bool ImGui::InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags)
8374 {
8375 ImGuiWindow* window = GetCurrentWindow();
8376 if (window->SkipItems)
8377 return false;
8378
8379 ImGuiContext& g = *GImGui;
8380 bool value_changed = false;
8381 BeginGroup();
8382 PushID(label);
8383 PushMultiItemsWidths(components);
8384 for (int i = 0; i < components; i++)
8385 {
8386 PushID(i);
8387 value_changed |= InputInt("##v", &v[i], 0, 0, extra_flags);
8388 SameLine(0, g.Style.ItemInnerSpacing.x);
8389 PopID();
8390 PopItemWidth();
8391 }
8392 PopID();
8393
8394 window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
8395 TextUnformatted(label, FindRenderedTextEnd(label));
8396 EndGroup();
8397
8398 return value_changed;
8399 }
8400
InputInt2(const char * label,int v[2],ImGuiInputTextFlags extra_flags)8401 bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags)
8402 {
8403 return InputIntN(label, v, 2, extra_flags);
8404 }
8405
InputInt3(const char * label,int v[3],ImGuiInputTextFlags extra_flags)8406 bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags)
8407 {
8408 return InputIntN(label, v, 3, extra_flags);
8409 }
8410
InputInt4(const char * label,int v[4],ImGuiInputTextFlags extra_flags)8411 bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags)
8412 {
8413 return InputIntN(label, v, 4, extra_flags);
8414 }
8415
Items_ArrayGetter(void * data,int idx,const char ** out_text)8416 static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
8417 {
8418 const char* const* items = (const char* const*)data;
8419 if (out_text)
8420 *out_text = items[idx];
8421 return true;
8422 }
8423
Items_SingleStringGetter(void * data,int idx,const char ** out_text)8424 static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
8425 {
8426 // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
8427 const char* items_separated_by_zeros = (const char*)data;
8428 int items_count = 0;
8429 const char* p = items_separated_by_zeros;
8430 while (*p)
8431 {
8432 if (idx == items_count)
8433 break;
8434 p += strlen(p) + 1;
8435 items_count++;
8436 }
8437 if (!*p)
8438 return false;
8439 if (out_text)
8440 *out_text = p;
8441 return true;
8442 }
8443
8444 // Combo box helper allowing to pass an array of strings.
Combo(const char * label,int * current_item,const char * const * items,int items_count,int height_in_items)8445 bool ImGui::Combo(const char* label, int* current_item, const char* const* items, int items_count, int height_in_items)
8446 {
8447 const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
8448 return value_changed;
8449 }
8450
8451 // Combo box helper allowing to pass all items in a single string.
Combo(const char * label,int * current_item,const char * items_separated_by_zeros,int height_in_items)8452 bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
8453 {
8454 int items_count = 0;
8455 const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open
8456 while (*p)
8457 {
8458 p += strlen(p) + 1;
8459 items_count++;
8460 }
8461 bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
8462 return value_changed;
8463 }
8464
8465 // Combo box function.
Combo(const char * label,int * current_item,bool (* items_getter)(void *,int,const char **),void * data,int items_count,int height_in_items)8466 bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
8467 {
8468 ImGuiWindow* window = GetCurrentWindow();
8469 if (window->SkipItems)
8470 return false;
8471
8472 ImGuiContext& g = *GImGui;
8473 const ImGuiStyle& style = g.Style;
8474 const ImGuiID id = window->GetID(label);
8475 const float w = CalcItemWidth();
8476
8477 const ImVec2 label_size = CalcTextSize(label, NULL, true);
8478 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
8479 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
8480 ItemSize(total_bb, style.FramePadding.y);
8481 if (!ItemAdd(total_bb, &id))
8482 return false;
8483
8484 const float arrow_size = (g.FontSize + style.FramePadding.x * 2.0f);
8485 const bool hovered = IsHovered(frame_bb, id);
8486 bool popup_open = IsPopupOpen(id);
8487 bool popup_opened_now = false;
8488
8489 const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
8490 RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
8491 RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(popup_open || hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding); // FIXME-ROUNDING
8492 RenderCollapseTriangle(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y) + style.FramePadding, true);
8493
8494 if (*current_item >= 0 && *current_item < items_count)
8495 {
8496 const char* item_text;
8497 if (items_getter(data, *current_item, &item_text))
8498 RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, item_text, NULL, NULL, ImVec2(0.0f,0.0f));
8499 }
8500
8501 if (label_size.x > 0)
8502 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
8503
8504 if (hovered)
8505 {
8506 SetHoveredID(id);
8507 if (g.IO.MouseClicked[0])
8508 {
8509 ClearActiveID();
8510 if (IsPopupOpen(id))
8511 {
8512 ClosePopup(id);
8513 }
8514 else
8515 {
8516 FocusWindow(window);
8517 OpenPopup(label);
8518 popup_open = popup_opened_now = true;
8519 }
8520 }
8521 }
8522
8523 bool value_changed = false;
8524 if (IsPopupOpen(id))
8525 {
8526 // Size default to hold ~7 items
8527 if (height_in_items < 0)
8528 height_in_items = 7;
8529
8530 float popup_height = (label_size.y + style.ItemSpacing.y) * ImMin(items_count, height_in_items) + (style.FramePadding.y * 3);
8531 float popup_y1 = frame_bb.Max.y;
8532 float popup_y2 = ImClamp(popup_y1 + popup_height, popup_y1, g.IO.DisplaySize.y - style.DisplaySafeAreaPadding.y);
8533 if ((popup_y2 - popup_y1) < ImMin(popup_height, frame_bb.Min.y - style.DisplaySafeAreaPadding.y))
8534 {
8535 // Position our combo ABOVE because there's more space to fit! (FIXME: Handle in Begin() or use a shared helper. We have similar code in Begin() for popup placement)
8536 popup_y1 = ImClamp(frame_bb.Min.y - popup_height, style.DisplaySafeAreaPadding.y, frame_bb.Min.y);
8537 popup_y2 = frame_bb.Min.y;
8538 }
8539 ImRect popup_rect(ImVec2(frame_bb.Min.x, popup_y1), ImVec2(frame_bb.Max.x, popup_y2));
8540 SetNextWindowPos(popup_rect.Min);
8541 SetNextWindowSize(popup_rect.GetSize());
8542 PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
8543
8544 const ImGuiWindowFlags flags = ImGuiWindowFlags_ComboBox | ((window->Flags & ImGuiWindowFlags_ShowBorders) ? ImGuiWindowFlags_ShowBorders : 0);
8545 if (BeginPopupEx(label, flags))
8546 {
8547 // Display items
8548 Spacing();
8549 for (int i = 0; i < items_count; i++)
8550 {
8551 PushID((void*)(intptr_t)i);
8552 const bool item_selected = (i == *current_item);
8553 const char* item_text;
8554 if (!items_getter(data, i, &item_text))
8555 item_text = "*Unknown item*";
8556 if (Selectable(item_text, item_selected))
8557 {
8558 ClearActiveID();
8559 value_changed = true;
8560 *current_item = i;
8561 }
8562 if (item_selected && popup_opened_now)
8563 SetScrollHere();
8564 PopID();
8565 }
8566 EndPopup();
8567 }
8568 PopStyleVar();
8569 }
8570 return value_changed;
8571 }
8572
8573 // Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image.
8574 // But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID.
Selectable(const char * label,bool selected,ImGuiSelectableFlags flags,const ImVec2 & size_arg)8575 bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
8576 {
8577 ImGuiWindow* window = GetCurrentWindow();
8578 if (window->SkipItems)
8579 return false;
8580
8581 ImGuiContext& g = *GImGui;
8582 const ImGuiStyle& style = g.Style;
8583
8584 if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
8585 PopClipRect();
8586
8587 ImGuiID id = window->GetID(label);
8588 ImVec2 label_size = CalcTextSize(label, NULL, true);
8589 ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
8590 ImVec2 pos = window->DC.CursorPos;
8591 pos.y += window->DC.CurrentLineTextBaseOffset;
8592 ImRect bb(pos, pos + size);
8593 ItemSize(bb);
8594
8595 // Fill horizontal space.
8596 ImVec2 window_padding = window->WindowPadding;
8597 float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x;
8598 float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x);
8599 ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y);
8600 ImRect bb_with_spacing(pos, pos + size_draw);
8601 if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth))
8602 bb_with_spacing.Max.x += window_padding.x;
8603
8604 // Selectables are tightly packed together, we extend the box to cover spacing between selectable.
8605 float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f);
8606 float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f);
8607 float spacing_R = style.ItemSpacing.x - spacing_L;
8608 float spacing_D = style.ItemSpacing.y - spacing_U;
8609 bb_with_spacing.Min.x -= spacing_L;
8610 bb_with_spacing.Min.y -= spacing_U;
8611 bb_with_spacing.Max.x += spacing_R;
8612 bb_with_spacing.Max.y += spacing_D;
8613 if (!ItemAdd(bb_with_spacing, &id))
8614 {
8615 if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
8616 PushColumnClipRect();
8617 return false;
8618 }
8619
8620 ImGuiButtonFlags button_flags = 0;
8621 if (flags & ImGuiSelectableFlags_Menu) button_flags |= ImGuiButtonFlags_PressedOnClick;
8622 if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnClick|ImGuiButtonFlags_PressedOnRelease;
8623 if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled;
8624 if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
8625 bool hovered, held;
8626 bool pressed = ButtonBehavior(bb_with_spacing, id, &hovered, &held, button_flags);
8627 if (flags & ImGuiSelectableFlags_Disabled)
8628 selected = false;
8629
8630 // Render
8631 if (hovered || selected)
8632 {
8633 const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
8634 RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, 0.0f);
8635 }
8636
8637 if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
8638 {
8639 PushColumnClipRect();
8640 bb_with_spacing.Max.x -= (GetContentRegionMax().x - max_x);
8641 }
8642
8643 if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
8644 RenderTextClipped(bb.Min, bb_with_spacing.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f));
8645 if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor();
8646
8647 // Automatically close popups
8648 if (pressed && !(flags & ImGuiSelectableFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
8649 CloseCurrentPopup();
8650 return pressed;
8651 }
8652
Selectable(const char * label,bool * p_selected,ImGuiSelectableFlags flags,const ImVec2 & size_arg)8653 bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
8654 {
8655 if (Selectable(label, *p_selected, flags, size_arg))
8656 {
8657 *p_selected = !*p_selected;
8658 return true;
8659 }
8660 return false;
8661 }
8662
8663 // Helper to calculate the size of a listbox and display a label on the right.
8664 // Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty"
ListBoxHeader(const char * label,const ImVec2 & size_arg)8665 bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
8666 {
8667 ImGuiWindow* window = GetCurrentWindow();
8668 if (window->SkipItems)
8669 return false;
8670
8671 const ImGuiStyle& style = GetStyle();
8672 const ImGuiID id = GetID(label);
8673 const ImVec2 label_size = CalcTextSize(label, NULL, true);
8674
8675 // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
8676 ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y);
8677 ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y));
8678 ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
8679 ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
8680 window->DC.LastItemRect = bb;
8681
8682 BeginGroup();
8683 if (label_size.x > 0)
8684 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
8685
8686 BeginChildFrame(id, frame_bb.GetSize());
8687 return true;
8688 }
8689
ListBoxHeader(const char * label,int items_count,int height_in_items)8690 bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items)
8691 {
8692 // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
8693 // However we don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size.
8694 // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution.
8695 if (height_in_items < 0)
8696 height_in_items = ImMin(items_count, 7);
8697 float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f);
8698
8699 // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild().
8700 ImVec2 size;
8701 size.x = 0.0f;
8702 size.y = GetTextLineHeightWithSpacing() * height_in_items_f + GetStyle().ItemSpacing.y;
8703 return ListBoxHeader(label, size);
8704 }
8705
ListBoxFooter()8706 void ImGui::ListBoxFooter()
8707 {
8708 ImGuiWindow* parent_window = GetParentWindow();
8709 const ImRect bb = parent_window->DC.LastItemRect;
8710 const ImGuiStyle& style = GetStyle();
8711
8712 EndChildFrame();
8713
8714 // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect)
8715 // We call SameLine() to restore DC.CurrentLine* data
8716 SameLine();
8717 parent_window->DC.CursorPos = bb.Min;
8718 ItemSize(bb, style.FramePadding.y);
8719 EndGroup();
8720 }
8721
ListBox(const char * label,int * current_item,const char * const * items,int items_count,int height_items)8722 bool ImGui::ListBox(const char* label, int* current_item, const char* const* items, int items_count, int height_items)
8723 {
8724 const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items);
8725 return value_changed;
8726 }
8727
ListBox(const char * label,int * current_item,bool (* items_getter)(void *,int,const char **),void * data,int items_count,int height_in_items)8728 bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
8729 {
8730 if (!ListBoxHeader(label, items_count, height_in_items))
8731 return false;
8732
8733 // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper.
8734 bool value_changed = false;
8735 ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing());
8736 while (clipper.Step())
8737 for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
8738 {
8739 const bool item_selected = (i == *current_item);
8740 const char* item_text;
8741 if (!items_getter(data, i, &item_text))
8742 item_text = "*Unknown item*";
8743
8744 PushID(i);
8745 if (Selectable(item_text, item_selected))
8746 {
8747 *current_item = i;
8748 value_changed = true;
8749 }
8750 PopID();
8751 }
8752 ListBoxFooter();
8753 return value_changed;
8754 }
8755
MenuItem(const char * label,const char * shortcut,bool selected,bool enabled)8756 bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)
8757 {
8758 ImGuiWindow* window = GetCurrentWindow();
8759 if (window->SkipItems)
8760 return false;
8761
8762 ImGuiContext& g = *GImGui;
8763 ImVec2 pos = window->DC.CursorPos;
8764 ImVec2 label_size = CalcTextSize(label, NULL, true);
8765 ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f);
8766 float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame
8767 float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
8768
8769 bool pressed = Selectable(label, false, ImGuiSelectableFlags_MenuItem | ImGuiSelectableFlags_DrawFillAvailWidth | (enabled ? 0 : ImGuiSelectableFlags_Disabled), ImVec2(w, 0.0f));
8770 if (shortcut_size.x > 0.0f)
8771 {
8772 PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
8773 RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
8774 PopStyleColor();
8775 }
8776
8777 if (selected)
8778 RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled));
8779
8780 return pressed;
8781 }
8782
MenuItem(const char * label,const char * shortcut,bool * p_selected,bool enabled)8783 bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled)
8784 {
8785 if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled))
8786 {
8787 if (p_selected)
8788 *p_selected = !*p_selected;
8789 return true;
8790 }
8791 return false;
8792 }
8793
BeginMainMenuBar()8794 bool ImGui::BeginMainMenuBar()
8795 {
8796 ImGuiContext& g = *GImGui;
8797 SetNextWindowPos(ImVec2(0.0f, 0.0f));
8798 SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.FontBaseSize + g.Style.FramePadding.y * 2.0f));
8799 PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
8800 PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0));
8801 if (!Begin("##MainMenuBar", NULL, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_MenuBar)
8802 || !BeginMenuBar())
8803 {
8804 End();
8805 PopStyleVar(2);
8806 return false;
8807 }
8808 g.CurrentWindow->DC.MenuBarOffsetX += g.Style.DisplaySafeAreaPadding.x;
8809 return true;
8810 }
8811
EndMainMenuBar()8812 void ImGui::EndMainMenuBar()
8813 {
8814 EndMenuBar();
8815 End();
8816 PopStyleVar(2);
8817 }
8818
BeginMenuBar()8819 bool ImGui::BeginMenuBar()
8820 {
8821 ImGuiWindow* window = GetCurrentWindow();
8822 if (window->SkipItems)
8823 return false;
8824 if (!(window->Flags & ImGuiWindowFlags_MenuBar))
8825 return false;
8826
8827 IM_ASSERT(!window->DC.MenuBarAppending);
8828 BeginGroup(); // Save position
8829 PushID("##menubar");
8830 ImRect rect = window->MenuBarRect();
8831 PushClipRect(ImVec2(ImFloor(rect.Min.x+0.5f), ImFloor(rect.Min.y + window->BorderSize + 0.5f)), ImVec2(ImFloor(rect.Max.x+0.5f), ImFloor(rect.Max.y+0.5f)), false);
8832 window->DC.CursorPos = ImVec2(rect.Min.x + window->DC.MenuBarOffsetX, rect.Min.y);// + g.Style.FramePadding.y);
8833 window->DC.LayoutType = ImGuiLayoutType_Horizontal;
8834 window->DC.MenuBarAppending = true;
8835 AlignFirstTextHeightToWidgets();
8836 return true;
8837 }
8838
EndMenuBar()8839 void ImGui::EndMenuBar()
8840 {
8841 ImGuiWindow* window = GetCurrentWindow();
8842 if (window->SkipItems)
8843 return;
8844
8845 IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);
8846 IM_ASSERT(window->DC.MenuBarAppending);
8847 PopClipRect();
8848 PopID();
8849 window->DC.MenuBarOffsetX = window->DC.CursorPos.x - window->MenuBarRect().Min.x;
8850 window->DC.GroupStack.back().AdvanceCursor = false;
8851 EndGroup();
8852 window->DC.LayoutType = ImGuiLayoutType_Vertical;
8853 window->DC.MenuBarAppending = false;
8854 }
8855
BeginMenu(const char * label,bool enabled)8856 bool ImGui::BeginMenu(const char* label, bool enabled)
8857 {
8858 ImGuiWindow* window = GetCurrentWindow();
8859 if (window->SkipItems)
8860 return false;
8861
8862 ImGuiContext& g = *GImGui;
8863 const ImGuiStyle& style = g.Style;
8864 const ImGuiID id = window->GetID(label);
8865
8866 ImVec2 label_size = CalcTextSize(label, NULL, true);
8867 ImGuiWindow* backed_focused_window = g.FocusedWindow;
8868
8869 bool pressed;
8870 bool menu_is_open = IsPopupOpen(id);
8871 bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentMenuSet == window->GetID("##menus"));
8872 if (menuset_is_open)
8873 g.FocusedWindow = window;
8874
8875 // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestPopupWindowPos).
8876 ImVec2 popup_pos, pos = window->DC.CursorPos;
8877 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
8878 {
8879 popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight());
8880 window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
8881 PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
8882 float w = label_size.x;
8883 pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
8884 PopStyleVar();
8885 SameLine();
8886 window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
8887 }
8888 else
8889 {
8890 popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
8891 float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame
8892 float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
8893 pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
8894 if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
8895 RenderCollapseTriangle(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), false);
8896 if (!enabled) PopStyleColor();
8897 }
8898
8899 bool hovered = enabled && IsHovered(window->DC.LastItemRect, id);
8900 if (menuset_is_open)
8901 g.FocusedWindow = backed_focused_window;
8902
8903 bool want_open = false, want_close = false;
8904 if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
8905 {
8906 // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
8907 bool moving_within_opened_triangle = false;
8908 if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window)
8909 {
8910 if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window)
8911 {
8912 ImRect next_window_rect = next_window->Rect();
8913 ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
8914 ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
8915 ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
8916 float extra = ImClamp(fabsf(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
8917 ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues
8918 tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
8919 tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
8920 moving_within_opened_triangle = ImIsPointInTriangle(g.IO.MousePos, ta, tb, tc);
8921 //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug
8922 }
8923 }
8924
8925 want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle);
8926 want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed);
8927 }
8928 else if (menu_is_open && pressed && menuset_is_open) // menu-bar: click open menu to close
8929 {
8930 want_close = true;
8931 want_open = menu_is_open = false;
8932 }
8933 else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // menu-bar: first click to open, then hover to open others
8934 want_open = true;
8935 if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }'
8936 want_close = true;
8937 if (want_close && IsPopupOpen(id))
8938 ClosePopupToLevel(GImGui->CurrentPopupStack.Size);
8939
8940 if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size)
8941 {
8942 // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
8943 OpenPopup(label);
8944 return false;
8945 }
8946
8947 menu_is_open |= want_open;
8948 if (want_open)
8949 OpenPopup(label);
8950
8951 if (menu_is_open)
8952 {
8953 SetNextWindowPos(popup_pos, ImGuiSetCond_Always);
8954 ImGuiWindowFlags flags = ImGuiWindowFlags_ShowBorders | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu);
8955 menu_is_open = BeginPopupEx(label, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
8956 }
8957
8958 return menu_is_open;
8959 }
8960
EndMenu()8961 void ImGui::EndMenu()
8962 {
8963 EndPopup();
8964 }
8965
8966 // A little colored square. Return true when clicked.
8967 // FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip.
ColorButton(const ImVec4 & col,bool small_height,bool outline_border)8968 bool ImGui::ColorButton(const ImVec4& col, bool small_height, bool outline_border)
8969 {
8970 ImGuiWindow* window = GetCurrentWindow();
8971 if (window->SkipItems)
8972 return false;
8973
8974 ImGuiContext& g = *GImGui;
8975 const ImGuiStyle& style = g.Style;
8976 const ImGuiID id = window->GetID("#colorbutton");
8977 const float square_size = g.FontSize;
8978 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(square_size + style.FramePadding.y*2, square_size + (small_height ? 0 : style.FramePadding.y*2)));
8979 ItemSize(bb, small_height ? 0.0f : style.FramePadding.y);
8980 if (!ItemAdd(bb, &id))
8981 return false;
8982
8983 bool hovered, held;
8984 bool pressed = ButtonBehavior(bb, id, &hovered, &held);
8985 RenderFrame(bb.Min, bb.Max, GetColorU32(col), outline_border, style.FrameRounding);
8986
8987 if (hovered)
8988 SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col.x, col.y, col.z, col.w, IM_F32_TO_INT8_SAT(col.x), IM_F32_TO_INT8_SAT(col.y), IM_F32_TO_INT8_SAT(col.z), IM_F32_TO_INT8_SAT(col.w));
8989
8990 return pressed;
8991 }
8992
ColorEdit3(const char * label,float col[3])8993 bool ImGui::ColorEdit3(const char* label, float col[3])
8994 {
8995 float col4[4];
8996 col4[0] = col[0];
8997 col4[1] = col[1];
8998 col4[2] = col[2];
8999 col4[3] = 1.0f;
9000 const bool value_changed = ColorEdit4(label, col4, false);
9001 col[0] = col4[0];
9002 col[1] = col4[1];
9003 col[2] = col4[2];
9004 return value_changed;
9005 }
9006
9007 // Edit colors components (each component in 0.0f..1.0f range
9008 // Use CTRL-Click to input value and TAB to go to next item.
ColorEdit4(const char * label,float col[4],bool alpha)9009 bool ImGui::ColorEdit4(const char* label, float col[4], bool alpha)
9010 {
9011 ImGuiWindow* window = GetCurrentWindow();
9012 if (window->SkipItems)
9013 return false;
9014
9015 ImGuiContext& g = *GImGui;
9016 const ImGuiStyle& style = g.Style;
9017 const ImGuiID id = window->GetID(label);
9018 const float w_full = CalcItemWidth();
9019 const float square_sz = (g.FontSize + style.FramePadding.y * 2.0f);
9020
9021 ImGuiColorEditMode edit_mode = window->DC.ColorEditMode;
9022 if (edit_mode == ImGuiColorEditMode_UserSelect || edit_mode == ImGuiColorEditMode_UserSelectShowButton)
9023 edit_mode = g.ColorEditModeStorage.GetInt(id, 0) % 3;
9024
9025 float f[4] = { col[0], col[1], col[2], col[3] };
9026 if (edit_mode == ImGuiColorEditMode_HSV)
9027 ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
9028
9029 int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) };
9030
9031 int components = alpha ? 4 : 3;
9032 bool value_changed = false;
9033
9034 BeginGroup();
9035 PushID(label);
9036
9037 const bool hsv = (edit_mode == 1);
9038 switch (edit_mode)
9039 {
9040 case ImGuiColorEditMode_RGB:
9041 case ImGuiColorEditMode_HSV:
9042 {
9043 // RGB/HSV 0..255 Sliders
9044 const float w_items_all = w_full - (square_sz + style.ItemInnerSpacing.x);
9045 const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
9046 const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
9047
9048 const bool hide_prefix = (w_item_one <= CalcTextSize("M:999").x);
9049 const char* ids[4] = { "##X", "##Y", "##Z", "##W" };
9050 const char* fmt_table[3][4] =
9051 {
9052 { "%3.0f", "%3.0f", "%3.0f", "%3.0f" },
9053 { "R:%3.0f", "G:%3.0f", "B:%3.0f", "A:%3.0f" },
9054 { "H:%3.0f", "S:%3.0f", "V:%3.0f", "A:%3.0f" }
9055 };
9056 const char** fmt = hide_prefix ? fmt_table[0] : hsv ? fmt_table[2] : fmt_table[1];
9057
9058 PushItemWidth(w_item_one);
9059 for (int n = 0; n < components; n++)
9060 {
9061 if (n > 0)
9062 SameLine(0, style.ItemInnerSpacing.x);
9063 if (n + 1 == components)
9064 PushItemWidth(w_item_last);
9065 value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, 255, fmt[n]);
9066 }
9067 PopItemWidth();
9068 PopItemWidth();
9069 }
9070 break;
9071 case ImGuiColorEditMode_HEX:
9072 {
9073 // RGB Hexadecimal Input
9074 const float w_slider_all = w_full - square_sz;
9075 char buf[64];
9076 if (alpha)
9077 ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", i[0], i[1], i[2], i[3]);
9078 else
9079 ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", i[0], i[1], i[2]);
9080 PushItemWidth(w_slider_all - style.ItemInnerSpacing.x);
9081 if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase))
9082 {
9083 value_changed |= true;
9084 char* p = buf;
9085 while (*p == '#' || ImCharIsSpace(*p))
9086 p++;
9087 i[0] = i[1] = i[2] = i[3] = 0;
9088 if (alpha)
9089 sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned)
9090 else
9091 sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
9092 }
9093 PopItemWidth();
9094 }
9095 break;
9096 }
9097
9098 SameLine(0, style.ItemInnerSpacing.x);
9099
9100 const ImVec4 col_display(col[0], col[1], col[2], 1.0f);
9101 if (ColorButton(col_display))
9102 g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away!
9103
9104 // Recreate our own tooltip over's ColorButton() one because we want to display correct alpha here
9105 if (IsItemHovered())
9106 SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col[0], col[1], col[2], col[3], IM_F32_TO_INT8_SAT(col[0]), IM_F32_TO_INT8_SAT(col[1]), IM_F32_TO_INT8_SAT(col[2]), IM_F32_TO_INT8_SAT(col[3]));
9107
9108 if (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton)
9109 {
9110 SameLine(0, style.ItemInnerSpacing.x);
9111 const char* button_titles[3] = { "RGB", "HSV", "HEX" };
9112 if (ButtonEx(button_titles[edit_mode], ImVec2(0,0), ImGuiButtonFlags_DontClosePopups))
9113 g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away!
9114 }
9115
9116 const char* label_display_end = FindRenderedTextEnd(label);
9117 if (label != label_display_end)
9118 {
9119 SameLine(0, (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton) ? -1.0f : style.ItemInnerSpacing.x);
9120 TextUnformatted(label, label_display_end);
9121 }
9122
9123 // Convert back
9124 for (int n = 0; n < 4; n++)
9125 f[n] = i[n] / 255.0f;
9126 if (edit_mode == 1)
9127 ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
9128
9129 if (value_changed)
9130 {
9131 col[0] = f[0];
9132 col[1] = f[1];
9133 col[2] = f[2];
9134 if (alpha)
9135 col[3] = f[3];
9136 }
9137
9138 PopID();
9139 EndGroup();
9140
9141 return value_changed;
9142 }
9143
ColorEditMode(ImGuiColorEditMode mode)9144 void ImGui::ColorEditMode(ImGuiColorEditMode mode)
9145 {
9146 ImGuiWindow* window = GetCurrentWindow();
9147 window->DC.ColorEditMode = mode;
9148 }
9149
9150 // Horizontal separating line.
Separator()9151 void ImGui::Separator()
9152 {
9153 ImGuiWindow* window = GetCurrentWindow();
9154 if (window->SkipItems)
9155 return;
9156
9157 if (window->DC.ColumnsCount > 1)
9158 PopClipRect();
9159
9160 float x1 = window->Pos.x;
9161 float x2 = window->Pos.x + window->Size.x;
9162 if (!window->DC.GroupStack.empty())
9163 x1 += window->DC.IndentX;
9164
9165 const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f));
9166 ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit, we don't provide height to not alter layout.
9167 if (!ItemAdd(bb, NULL))
9168 {
9169 if (window->DC.ColumnsCount > 1)
9170 PushColumnClipRect();
9171 return;
9172 }
9173
9174 window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Border));
9175
9176 ImGuiContext& g = *GImGui;
9177 if (g.LogEnabled)
9178 LogText(IM_NEWLINE "--------------------------------");
9179
9180 if (window->DC.ColumnsCount > 1)
9181 {
9182 PushColumnClipRect();
9183 window->DC.ColumnsCellMinY = window->DC.CursorPos.y;
9184 }
9185 }
9186
Spacing()9187 void ImGui::Spacing()
9188 {
9189 ImGuiWindow* window = GetCurrentWindow();
9190 if (window->SkipItems)
9191 return;
9192 ItemSize(ImVec2(0,0));
9193 }
9194
Dummy(const ImVec2 & size)9195 void ImGui::Dummy(const ImVec2& size)
9196 {
9197 ImGuiWindow* window = GetCurrentWindow();
9198 if (window->SkipItems)
9199 return;
9200
9201 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
9202 ItemSize(bb);
9203 ItemAdd(bb, NULL);
9204 }
9205
IsRectVisible(const ImVec2 & size)9206 bool ImGui::IsRectVisible(const ImVec2& size)
9207 {
9208 ImGuiWindow* window = GetCurrentWindowRead();
9209 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
9210 }
9211
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)9212 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
9213 {
9214 ImGuiWindow* window = GetCurrentWindowRead();
9215 return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
9216 }
9217
9218 // Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
BeginGroup()9219 void ImGui::BeginGroup()
9220 {
9221 ImGuiWindow* window = GetCurrentWindow();
9222
9223 window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
9224 ImGuiGroupData& group_data = window->DC.GroupStack.back();
9225 group_data.BackupCursorPos = window->DC.CursorPos;
9226 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
9227 group_data.BackupIndentX = window->DC.IndentX;
9228 group_data.BackupGroupOffsetX = window->DC.GroupOffsetX;
9229 group_data.BackupCurrentLineHeight = window->DC.CurrentLineHeight;
9230 group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
9231 group_data.BackupLogLinePosY = window->DC.LogLinePosY;
9232 group_data.BackupActiveIdIsAlive = GImGui->ActiveIdIsAlive;
9233 group_data.AdvanceCursor = true;
9234
9235 window->DC.GroupOffsetX = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffsetX;
9236 window->DC.IndentX = window->DC.GroupOffsetX;
9237 window->DC.CursorMaxPos = window->DC.CursorPos;
9238 window->DC.CurrentLineHeight = 0.0f;
9239 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
9240 }
9241
EndGroup()9242 void ImGui::EndGroup()
9243 {
9244 ImGuiContext& g = *GImGui;
9245 ImGuiWindow* window = GetCurrentWindow();
9246
9247 IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls
9248
9249 ImGuiGroupData& group_data = window->DC.GroupStack.back();
9250
9251 ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
9252 group_bb.Max.y -= g.Style.ItemSpacing.y; // Cancel out last vertical spacing because we are adding one ourselves.
9253 group_bb.Max = ImMax(group_bb.Min, group_bb.Max);
9254
9255 window->DC.CursorPos = group_data.BackupCursorPos;
9256 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
9257 window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight;
9258 window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
9259 window->DC.IndentX = group_data.BackupIndentX;
9260 window->DC.GroupOffsetX = group_data.BackupGroupOffsetX;
9261 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
9262
9263 if (group_data.AdvanceCursor)
9264 {
9265 window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrentLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.
9266 ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset);
9267 ItemAdd(group_bb, NULL);
9268 }
9269
9270 // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive() will function on the entire group.
9271 // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but if you search for LastItemId you'll notice it is only used in that context.
9272 const bool active_id_within_group = (!group_data.BackupActiveIdIsAlive && g.ActiveIdIsAlive && g.ActiveId && g.ActiveIdWindow->RootWindow == window->RootWindow);
9273 if (active_id_within_group)
9274 window->DC.LastItemId = g.ActiveId;
9275 if (active_id_within_group && g.HoveredId == g.ActiveId)
9276 window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = true;
9277
9278 window->DC.GroupStack.pop_back();
9279
9280 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // Debug
9281 }
9282
9283 // Gets back to previous line and continue with horizontal layout
9284 // pos_x == 0 : follow right after previous item
9285 // pos_x != 0 : align to specified x position (relative to window/group left)
9286 // spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0
9287 // spacing_w >= 0 : enforce spacing amount
SameLine(float pos_x,float spacing_w)9288 void ImGui::SameLine(float pos_x, float spacing_w)
9289 {
9290 ImGuiWindow* window = GetCurrentWindow();
9291 if (window->SkipItems)
9292 return;
9293
9294 ImGuiContext& g = *GImGui;
9295 if (pos_x != 0.0f)
9296 {
9297 if (spacing_w < 0.0f) spacing_w = 0.0f;
9298 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffsetX + window->DC.ColumnsOffsetX;
9299 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
9300 }
9301 else
9302 {
9303 if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
9304 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
9305 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
9306 }
9307 window->DC.CurrentLineHeight = window->DC.PrevLineHeight;
9308 window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
9309 }
9310
NewLine()9311 void ImGui::NewLine()
9312 {
9313 ImGuiWindow* window = GetCurrentWindow();
9314 if (window->SkipItems)
9315 return;
9316 if (window->DC.CurrentLineHeight > 0.0f) // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height.
9317 ItemSize(ImVec2(0,0));
9318 else
9319 ItemSize(ImVec2(0.0f, GImGui->FontSize));
9320 }
9321
NextColumn()9322 void ImGui::NextColumn()
9323 {
9324 ImGuiWindow* window = GetCurrentWindow();
9325 if (window->SkipItems || window->DC.ColumnsCount <= 1)
9326 return;
9327
9328 ImGuiContext& g = *GImGui;
9329 PopItemWidth();
9330 PopClipRect();
9331
9332 window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
9333 if (++window->DC.ColumnsCurrent < window->DC.ColumnsCount)
9334 {
9335 // Columns 1+ cancel out IndentX
9336 window->DC.ColumnsOffsetX = GetColumnOffset(window->DC.ColumnsCurrent) - window->DC.IndentX + g.Style.ItemSpacing.x;
9337 window->DrawList->ChannelsSetCurrent(window->DC.ColumnsCurrent);
9338 }
9339 else
9340 {
9341 window->DC.ColumnsCurrent = 0;
9342 window->DC.ColumnsOffsetX = 0.0f;
9343 window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY;
9344 window->DrawList->ChannelsSetCurrent(0);
9345 }
9346 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
9347 window->DC.CursorPos.y = window->DC.ColumnsCellMinY;
9348 window->DC.CurrentLineHeight = 0.0f;
9349 window->DC.CurrentLineTextBaseOffset = 0.0f;
9350
9351 PushColumnClipRect();
9352 PushItemWidth(GetColumnWidth() * 0.65f); // FIXME: Move on columns setup
9353 }
9354
GetColumnIndex()9355 int ImGui::GetColumnIndex()
9356 {
9357 ImGuiWindow* window = GetCurrentWindowRead();
9358 return window->DC.ColumnsCurrent;
9359 }
9360
GetColumnsCount()9361 int ImGui::GetColumnsCount()
9362 {
9363 ImGuiWindow* window = GetCurrentWindowRead();
9364 return window->DC.ColumnsCount;
9365 }
9366
GetDraggedColumnOffset(int column_index)9367 static float GetDraggedColumnOffset(int column_index)
9368 {
9369 // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
9370 // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
9371 ImGuiContext& g = *GImGui;
9372 ImGuiWindow* window = ImGui::GetCurrentWindowRead();
9373 IM_ASSERT(column_index > 0); // We cannot drag column 0. If you get this assert you may have a conflict between the ID of your columns and another widgets.
9374 IM_ASSERT(g.ActiveId == window->DC.ColumnsSetId + ImGuiID(column_index));
9375
9376 float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x - window->Pos.x;
9377 x = ImClamp(x, ImGui::GetColumnOffset(column_index-1)+g.Style.ColumnsMinSpacing, ImGui::GetColumnOffset(column_index+1)-g.Style.ColumnsMinSpacing);
9378
9379 return (float)(int)x;
9380 }
9381
GetColumnOffset(int column_index)9382 float ImGui::GetColumnOffset(int column_index)
9383 {
9384 ImGuiContext& g = *GImGui;
9385 ImGuiWindow* window = GetCurrentWindowRead();
9386 if (column_index < 0)
9387 column_index = window->DC.ColumnsCurrent;
9388
9389 if (g.ActiveId)
9390 {
9391 const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
9392 if (g.ActiveId == column_id)
9393 return GetDraggedColumnOffset(column_index);
9394 }
9395
9396 IM_ASSERT(column_index < window->DC.ColumnsData.Size);
9397 const float t = window->DC.ColumnsData[column_index].OffsetNorm;
9398 const float x_offset = window->DC.ColumnsMinX + t * (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
9399 return (float)(int)x_offset;
9400 }
9401
SetColumnOffset(int column_index,float offset)9402 void ImGui::SetColumnOffset(int column_index, float offset)
9403 {
9404 ImGuiWindow* window = GetCurrentWindow();
9405 if (column_index < 0)
9406 column_index = window->DC.ColumnsCurrent;
9407
9408 IM_ASSERT(column_index < window->DC.ColumnsData.Size);
9409 const float t = (offset - window->DC.ColumnsMinX) / (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
9410 window->DC.ColumnsData[column_index].OffsetNorm = t;
9411
9412 const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
9413 window->DC.StateStorage->SetFloat(column_id, t);
9414 }
9415
GetColumnWidth(int column_index)9416 float ImGui::GetColumnWidth(int column_index)
9417 {
9418 ImGuiWindow* window = GetCurrentWindowRead();
9419 if (column_index < 0)
9420 column_index = window->DC.ColumnsCurrent;
9421
9422 float w = GetColumnOffset(column_index+1) - GetColumnOffset(column_index);
9423 return w;
9424 }
9425
PushColumnClipRect(int column_index)9426 static void PushColumnClipRect(int column_index)
9427 {
9428 ImGuiWindow* window = ImGui::GetCurrentWindow();
9429 if (column_index < 0)
9430 column_index = window->DC.ColumnsCurrent;
9431
9432 float x1 = ImFloor(0.5f + window->Pos.x + ImGui::GetColumnOffset(column_index) - 1.0f);
9433 float x2 = ImFloor(0.5f + window->Pos.x + ImGui::GetColumnOffset(column_index+1) - 1.0f);
9434 ImGui::PushClipRect(ImVec2(x1,-FLT_MAX), ImVec2(x2,+FLT_MAX), true);
9435 }
9436
Columns(int columns_count,const char * id,bool border)9437 void ImGui::Columns(int columns_count, const char* id, bool border)
9438 {
9439 ImGuiContext& g = *GImGui;
9440 ImGuiWindow* window = GetCurrentWindow();
9441 IM_ASSERT(columns_count >= 1);
9442
9443 if (window->DC.ColumnsCount != 1)
9444 {
9445 if (window->DC.ColumnsCurrent != 0)
9446 ItemSize(ImVec2(0,0)); // Advance to column 0
9447 PopItemWidth();
9448 PopClipRect();
9449 window->DrawList->ChannelsMerge();
9450
9451 window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
9452 window->DC.CursorPos.y = window->DC.ColumnsCellMaxY;
9453 }
9454
9455 // Draw columns borders and handle resize at the time of "closing" a columns set
9456 if (window->DC.ColumnsCount != columns_count && window->DC.ColumnsCount != 1 && window->DC.ColumnsShowBorders && !window->SkipItems)
9457 {
9458 const float y1 = window->DC.ColumnsStartPosY;
9459 const float y2 = window->DC.CursorPos.y;
9460 for (int i = 1; i < window->DC.ColumnsCount; i++)
9461 {
9462 float x = window->Pos.x + GetColumnOffset(i);
9463 const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(i);
9464 const ImRect column_rect(ImVec2(x-4,y1),ImVec2(x+4,y2));
9465 if (IsClippedEx(column_rect, &column_id, false))
9466 continue;
9467
9468 bool hovered, held;
9469 ButtonBehavior(column_rect, column_id, &hovered, &held);
9470 if (hovered || held)
9471 g.MouseCursor = ImGuiMouseCursor_ResizeEW;
9472
9473 // Draw before resize so our items positioning are in sync with the line being drawn
9474 const ImU32 col = GetColorU32(held ? ImGuiCol_ColumnActive : hovered ? ImGuiCol_ColumnHovered : ImGuiCol_Column);
9475 const float xi = (float)(int)x;
9476 window->DrawList->AddLine(ImVec2(xi, y1+1.0f), ImVec2(xi, y2), col);
9477
9478 if (held)
9479 {
9480 if (g.ActiveIdIsJustActivated)
9481 g.ActiveIdClickOffset.x -= 4; // Store from center of column line (we used a 8 wide rect for columns clicking)
9482 x = GetDraggedColumnOffset(i);
9483 SetColumnOffset(i, x);
9484 }
9485 }
9486 }
9487
9488 // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
9489 // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
9490 PushID(0x11223347 + (id ? 0 : columns_count));
9491 window->DC.ColumnsSetId = window->GetID(id ? id : "columns");
9492 PopID();
9493
9494 // Set state for first column
9495 window->DC.ColumnsCurrent = 0;
9496 window->DC.ColumnsCount = columns_count;
9497 window->DC.ColumnsShowBorders = border;
9498
9499 const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : window->Size.x;
9500 window->DC.ColumnsMinX = window->DC.IndentX; // Lock our horizontal range
9501 window->DC.ColumnsMaxX = content_region_width - window->Scroll.x - ((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x;
9502 window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
9503 window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.CursorPos.y;
9504 window->DC.ColumnsOffsetX = 0.0f;
9505 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
9506
9507 if (window->DC.ColumnsCount != 1)
9508 {
9509 // Cache column offsets
9510 window->DC.ColumnsData.resize(columns_count + 1);
9511 for (int column_index = 0; column_index < columns_count + 1; column_index++)
9512 {
9513 const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
9514 KeepAliveID(column_id);
9515 const float default_t = column_index / (float)window->DC.ColumnsCount;
9516 const float t = window->DC.StateStorage->GetFloat(column_id, default_t); // Cheaply store our floating point value inside the integer (could store a union into the map?)
9517 window->DC.ColumnsData[column_index].OffsetNorm = t;
9518 }
9519 window->DrawList->ChannelsSplit(window->DC.ColumnsCount);
9520 PushColumnClipRect();
9521 PushItemWidth(GetColumnWidth() * 0.65f);
9522 }
9523 else
9524 {
9525 window->DC.ColumnsData.resize(0);
9526 }
9527 }
9528
Indent(float indent_w)9529 void ImGui::Indent(float indent_w)
9530 {
9531 ImGuiContext& g = *GImGui;
9532 ImGuiWindow* window = GetCurrentWindow();
9533 window->DC.IndentX += (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing;
9534 window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
9535 }
9536
Unindent(float indent_w)9537 void ImGui::Unindent(float indent_w)
9538 {
9539 ImGuiContext& g = *GImGui;
9540 ImGuiWindow* window = GetCurrentWindow();
9541 window->DC.IndentX -= (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing;
9542 window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
9543 }
9544
TreePush(const char * str_id)9545 void ImGui::TreePush(const char* str_id)
9546 {
9547 ImGuiWindow* window = GetCurrentWindow();
9548 Indent();
9549 window->DC.TreeDepth++;
9550 PushID(str_id ? str_id : "#TreePush");
9551 }
9552
TreePush(const void * ptr_id)9553 void ImGui::TreePush(const void* ptr_id)
9554 {
9555 ImGuiWindow* window = GetCurrentWindow();
9556 Indent();
9557 window->DC.TreeDepth++;
9558 PushID(ptr_id ? ptr_id : (const void*)"#TreePush");
9559 }
9560
TreePushRawID(ImGuiID id)9561 void ImGui::TreePushRawID(ImGuiID id)
9562 {
9563 ImGuiWindow* window = GetCurrentWindow();
9564 Indent();
9565 window->DC.TreeDepth++;
9566 window->IDStack.push_back(id);
9567 }
9568
TreePop()9569 void ImGui::TreePop()
9570 {
9571 ImGuiWindow* window = GetCurrentWindow();
9572 Unindent();
9573 window->DC.TreeDepth--;
9574 PopID();
9575 }
9576
Value(const char * prefix,bool b)9577 void ImGui::Value(const char* prefix, bool b)
9578 {
9579 Text("%s: %s", prefix, (b ? "true" : "false"));
9580 }
9581
Value(const char * prefix,int v)9582 void ImGui::Value(const char* prefix, int v)
9583 {
9584 Text("%s: %d", prefix, v);
9585 }
9586
Value(const char * prefix,unsigned int v)9587 void ImGui::Value(const char* prefix, unsigned int v)
9588 {
9589 Text("%s: %d", prefix, v);
9590 }
9591
Value(const char * prefix,float v,const char * float_format)9592 void ImGui::Value(const char* prefix, float v, const char* float_format)
9593 {
9594 if (float_format)
9595 {
9596 char fmt[64];
9597 ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format);
9598 Text(fmt, prefix, v);
9599 }
9600 else
9601 {
9602 Text("%s: %.3f", prefix, v);
9603 }
9604 }
9605
9606 // FIXME: May want to remove those helpers?
ValueColor(const char * prefix,const ImVec4 & v)9607 void ImGui::ValueColor(const char* prefix, const ImVec4& v)
9608 {
9609 Text("%s: (%.2f,%.2f,%.2f,%.2f)", prefix, v.x, v.y, v.z, v.w);
9610 SameLine();
9611 ColorButton(v, true);
9612 }
9613
ValueColor(const char * prefix,ImU32 v)9614 void ImGui::ValueColor(const char* prefix, ImU32 v)
9615 {
9616 Text("%s: %08X", prefix, v);
9617 SameLine();
9618 ColorButton(ColorConvertU32ToFloat4(v), true);
9619 }
9620
9621 //-----------------------------------------------------------------------------
9622 // PLATFORM DEPENDENT HELPERS
9623 //-----------------------------------------------------------------------------
9624
9625 #if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS))
9626 #undef WIN32_LEAN_AND_MEAN
9627 #define WIN32_LEAN_AND_MEAN
9628 #include <windows.h>
9629 #endif
9630
9631 // Win32 API clipboard implementation
9632 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS)
9633
9634 #ifdef _MSC_VER
9635 #pragma comment(lib, "user32")
9636 #endif
9637
GetClipboardTextFn_DefaultImpl(void *)9638 static const char* GetClipboardTextFn_DefaultImpl(void*)
9639 {
9640 static ImVector<char> buf_local;
9641 buf_local.clear();
9642 if (!OpenClipboard(NULL))
9643 return NULL;
9644 HANDLE wbuf_handle = GetClipboardData(CF_UNICODETEXT);
9645 if (wbuf_handle == NULL)
9646 return NULL;
9647 if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle))
9648 {
9649 int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
9650 buf_local.resize(buf_len);
9651 ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
9652 }
9653 GlobalUnlock(wbuf_handle);
9654 CloseClipboard();
9655 return buf_local.Data;
9656 }
9657
SetClipboardTextFn_DefaultImpl(void *,const char * text)9658 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9659 {
9660 if (!OpenClipboard(NULL))
9661 return;
9662 const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
9663 HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
9664 if (wbuf_handle == NULL)
9665 return;
9666 ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle);
9667 ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
9668 GlobalUnlock(wbuf_handle);
9669 EmptyClipboard();
9670 SetClipboardData(CF_UNICODETEXT, wbuf_handle);
9671 CloseClipboard();
9672 }
9673
9674 #else
9675
9676 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
GetClipboardTextFn_DefaultImpl(void *)9677 static const char* GetClipboardTextFn_DefaultImpl(void*)
9678 {
9679 return GImGui->PrivateClipboard;
9680 }
9681
9682 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
SetClipboardTextFn_DefaultImpl(void *,const char * text)9683 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9684 {
9685 ImGuiContext& g = *GImGui;
9686 if (g.PrivateClipboard)
9687 {
9688 ImGui::MemFree(g.PrivateClipboard);
9689 g.PrivateClipboard = NULL;
9690 }
9691 const char* text_end = text + strlen(text);
9692 g.PrivateClipboard = (char*)ImGui::MemAlloc((size_t)(text_end - text) + 1);
9693 memcpy(g.PrivateClipboard, text, (size_t)(text_end - text));
9694 g.PrivateClipboard[(int)(text_end - text)] = 0;
9695 }
9696
9697 #endif
9698
9699 // Win32 API IME support (for Asian languages, etc.)
9700 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS)
9701
9702 #include <imm.h>
9703 #ifdef _MSC_VER
9704 #pragma comment(lib, "imm32")
9705 #endif
9706
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)9707 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
9708 {
9709 // Notify OS Input Method Editor of text input position
9710 if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle)
9711 if (HIMC himc = ImmGetContext(hwnd))
9712 {
9713 COMPOSITIONFORM cf;
9714 cf.ptCurrentPos.x = x;
9715 cf.ptCurrentPos.y = y;
9716 cf.dwStyle = CFS_FORCE_POSITION;
9717 ImmSetCompositionWindow(himc, &cf);
9718 }
9719 }
9720
9721 #else
9722
ImeSetInputScreenPosFn_DefaultImpl(int,int)9723 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
9724
9725 #endif
9726
9727 //-----------------------------------------------------------------------------
9728 // HELP
9729 //-----------------------------------------------------------------------------
9730
ShowMetricsWindow(bool * p_open)9731 void ImGui::ShowMetricsWindow(bool* p_open)
9732 {
9733 if (ImGui::Begin("ImGui Metrics", p_open))
9734 {
9735 ImGui::Text("ImGui %s", ImGui::GetVersion());
9736 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
9737 ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3);
9738 ImGui::Text("%d allocations", ImGui::GetIO().MetricsAllocs);
9739 static bool show_clip_rects = true;
9740 ImGui::Checkbox("Show clipping rectangles when hovering a ImDrawCmd", &show_clip_rects);
9741 ImGui::Separator();
9742
9743 struct Funcs
9744 {
9745 static void NodeDrawList(ImDrawList* draw_list, const char* label)
9746 {
9747 bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size);
9748 if (draw_list == ImGui::GetWindowDrawList())
9749 {
9750 ImGui::SameLine();
9751 ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
9752 if (node_open) ImGui::TreePop();
9753 return;
9754 }
9755 if (!node_open)
9756 return;
9757
9758 ImDrawList* overlay_draw_list = &GImGui->OverlayDrawList; // Render additional visuals into the top-most draw list
9759 overlay_draw_list->PushClipRectFullScreen();
9760 int elem_offset = 0;
9761 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
9762 {
9763 if (pcmd->UserCallback)
9764 {
9765 ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
9766 continue;
9767 }
9768 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
9769 bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %-4d %s vtx, tex = %p, clip_rect = (%.0f,%.0f)..(%.0f,%.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
9770 if (show_clip_rects && ImGui::IsItemHovered())
9771 {
9772 ImRect clip_rect = pcmd->ClipRect;
9773 ImRect vtxs_rect;
9774 for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
9775 vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
9776 clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255));
9777 vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255));
9778 }
9779 if (!pcmd_node_open)
9780 continue;
9781 ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
9782 while (clipper.Step())
9783 for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
9784 {
9785 char buf[300], *buf_p = buf;
9786 ImVec2 triangles_pos[3];
9787 for (int n = 0; n < 3; n++, vtx_i++)
9788 {
9789 ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i];
9790 triangles_pos[n] = v.pos;
9791 buf_p += sprintf(buf_p, "%s %04d { pos = (%8.2f,%8.2f), uv = (%.6f,%.6f), col = %08X }\n", (n == 0) ? "vtx" : " ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
9792 }
9793 ImGui::Selectable(buf, false);
9794 if (ImGui::IsItemHovered())
9795 overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f, false); // Add triangle without AA, more readable for large-thin triangle
9796 }
9797 ImGui::TreePop();
9798 }
9799 overlay_draw_list->PopClipRect();
9800 ImGui::TreePop();
9801 }
9802
9803 static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
9804 {
9805 if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
9806 return;
9807 for (int i = 0; i < windows.Size; i++)
9808 Funcs::NodeWindow(windows[i], "Window");
9809 ImGui::TreePop();
9810 }
9811
9812 static void NodeWindow(ImGuiWindow* window, const char* label)
9813 {
9814 if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
9815 return;
9816 NodeDrawList(window->DrawList, "DrawList");
9817 ImGui::BulletText("Pos: (%.1f,%.1f)", window->Pos.x, window->Pos.y);
9818 ImGui::BulletText("Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y);
9819 ImGui::BulletText("Scroll: (%.2f,%.2f)", window->Scroll.x, window->Scroll.y);
9820 if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
9821 if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
9822 ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
9823 ImGui::TreePop();
9824 }
9825 };
9826
9827 ImGuiContext& g = *GImGui; // Access private state
9828 Funcs::NodeWindows(g.Windows, "Windows");
9829 if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.RenderDrawLists[0].Size))
9830 {
9831 for (int i = 0; i < g.RenderDrawLists[0].Size; i++)
9832 Funcs::NodeDrawList(g.RenderDrawLists[0][i], "DrawList");
9833 ImGui::TreePop();
9834 }
9835 if (ImGui::TreeNode("Popups", "Open Popups Stack (%d)", g.OpenPopupStack.Size))
9836 {
9837 for (int i = 0; i < g.OpenPopupStack.Size; i++)
9838 {
9839 ImGuiWindow* window = g.OpenPopupStack[i].Window;
9840 ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : "");
9841 }
9842 ImGui::TreePop();
9843 }
9844 if (ImGui::TreeNode("Basic state"))
9845 {
9846 ImGui::Text("FocusedWindow: '%s'", g.FocusedWindow ? g.FocusedWindow->Name : "NULL");
9847 ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
9848 ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
9849 ImGui::Text("HoveredID: 0x%08X/0x%08X", g.HoveredId, g.HoveredIdPreviousFrame); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
9850 ImGui::Text("ActiveID: 0x%08X/0x%08X", g.ActiveId, g.ActiveIdPreviousFrame);
9851 ImGui::TreePop();
9852 }
9853 }
9854 ImGui::End();
9855 }
9856
9857 //-----------------------------------------------------------------------------
9858
9859 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
9860 // Prefer just including imgui_internal.h from your code rather than using this define. If a declaration is missing from imgui_internal.h add it or request it on the github.
9861 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
9862 #include "imgui_user.inl"
9863 #endif
9864
9865 //-----------------------------------------------------------------------------
9866