1 // dear imgui, v1.49 WIP
2 // (main code and documentation)
3
4 // See ImGui::ShowTestWindow() in imgui_demo.cpp for demo code.
5 // Newcomers, read 'Programmer guide' below for notes on how to setup ImGui in your codebase.
6 // Get latest version at https://github.com/ocornut/imgui
7 // Releases change-log at https://github.com/ocornut/imgui/releases
8 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
9 // This library is free but I need your support to sustain development and maintenance.
10 // If you work for a company, please consider financial support, e.g: https://www.patreon.com/imgui
11
12 /*
13
14 Index
15 - MISSION STATEMENT
16 - END-USER GUIDE
17 - PROGRAMMER GUIDE (read me!)
18 - API BREAKING CHANGES (read me when you update!)
19 - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
20 - How can I help?
21 - How do I update to a newer version of ImGui?
22 - What is ImTextureID and how do I display an image?
23 - Can I have multiple widgets with the same label? Can I have widget without a label? (Yes) / A primer on the use of labels/IDs in ImGui.
24 - I integrated ImGui in my engine and the text or lines are blurry..
25 - I integrated ImGui in my engine and some elements are clipping or disappearing when I move windows around..
26 - How can I load a different font than the default?
27 - How can I load multiple fonts?
28 - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
29 - ISSUES & TODO-LIST
30 - CODE
31
32
33 MISSION STATEMENT
34 =================
35
36 - easy to use to create code-driven and data-driven tools
37 - easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools
38 - easy to hack and improve
39 - minimize screen real-estate usage
40 - minimize setup and maintenance
41 - minimize state storage on user side
42 - portable, minimize dependencies, run on target (consoles, phones, etc.)
43 - efficient runtime (NB- we do allocate when "growing" content - creating a window / opening a tree node for the first time, etc. - but a typical frame won't allocate anything)
44 - read about immediate-mode gui principles @ http://mollyrocket.com/861, http://mollyrocket.com/forums/index.html
45
46 Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
47 - doesn't look fancy, doesn't animate
48 - limited layout features, intricate layouts are typically crafted in code
49 - occasionally uses statically sized buffers for string manipulations - won't crash, but some very long pieces of text may be clipped. functions like ImGui::TextUnformatted() don't have such restriction.
50
51
52 END-USER GUIDE
53 ==============
54
55 - double-click title bar to collapse window
56 - click upper right corner to close a window, available when 'bool* p_opened' is passed to ImGui::Begin()
57 - click and drag on lower right corner to resize window
58 - click and drag on any empty space to move window
59 - double-click/double-tap on lower right corner grip to auto-fit to content
60 - TAB/SHIFT+TAB to cycle through keyboard editable fields
61 - use mouse wheel to scroll
62 - use CTRL+mouse wheel to zoom window contents (if IO.FontAllowScaling is true)
63 - CTRL+Click on a slider or drag box to input value as text
64 - text editor:
65 - Hold SHIFT or use mouse to select text.
66 - CTRL+Left/Right to word jump
67 - CTRL+Shift+Left/Right to select words
68 - CTRL+A our Double-Click to select all
69 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard
70 - CTRL+Z,CTRL+Y to undo/redo
71 - ESCAPE to revert text to its original value
72 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
73
74
75 PROGRAMMER GUIDE
76 ================
77
78 - read the FAQ below this section!
79 - your code creates the UI, if your code doesn't run the UI is gone! == very dynamic UI, no construction/destructions steps, less data retention on your side, no state duplication, less sync, less bugs.
80 - call and read ImGui::ShowTestWindow() for demo code demonstrating most features.
81 - see examples/ folder for standalone sample applications. Prefer reading examples/opengl_example/ first at it is the simplest.
82 you may be able to grab and copy a ready made imgui_impl_*** file from the examples/.
83 - customization: PushStyleColor()/PushStyleVar() or the style editor to tweak the look of the interface (e.g. if you want a more compact UI or a different color scheme).
84
85 - getting started:
86 - init: call ImGui::GetIO() to retrieve the ImGuiIO structure and fill the fields marked 'Settings'.
87 - init: call io.Fonts->GetTexDataAsRGBA32(...) and load the font texture pixels into graphics memory.
88 - every frame:
89 1/ in your mainloop or right after you got your keyboard/mouse info, call ImGui::GetIO() and fill the fields marked 'Input'
90 2/ call ImGui::NewFrame() as early as you can!
91 3/ use any ImGui function you want between NewFrame() and Render()
92 4/ call ImGui::Render() as late as you can to end the frame and finalize render data. it will call your RenderDrawListFn handler that you set in the IO structure.
93 (if you don't need to render, you still need to call Render() and ignore the callback, or call EndFrame() instead. if you call neither some aspects of windows focusing/moving will appear broken.)
94 - all rendering information are stored into command-lists until ImGui::Render() is called.
95 - ImGui never touches or know about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide.
96 - effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases of your own application.
97 - refer to the examples applications in the examples/ folder for instruction on how to setup your code.
98 - a typical application skeleton may be:
99
100 // Application init
101 ImGuiIO& io = ImGui::GetIO();
102 io.DisplaySize.x = 1920.0f;
103 io.DisplaySize.y = 1280.0f;
104 io.IniFilename = "imgui.ini";
105 io.RenderDrawListsFn = my_render_function; // Setup a render function, or set to NULL and call GetDrawData() after Render() to access the render data.
106 // TODO: Fill others settings of the io structure
107
108 // Load texture atlas
109 // There is a default font so you don't need to care about choosing a font yet
110 unsigned char* pixels;
111 int width, height;
112 io.Fonts->GetTexDataAsRGBA32(pixels, &width, &height);
113 // TODO: At this points you've got a texture pointed to by 'pixels' and you need to upload that your your graphic system
114 // TODO: Store your texture pointer/identifier (whatever your engine uses) in 'io.Fonts->TexID'
115
116 // Application main loop
117 while (true)
118 {
119 // 1) get low-level inputs (e.g. on Win32, GetKeyboardState(), or poll your events, etc.)
120 // TODO: fill all fields of IO structure and call NewFrame
121 ImGuiIO& io = ImGui::GetIO();
122 io.DeltaTime = 1.0f/60.0f;
123 io.MousePos = mouse_pos;
124 io.MouseDown[0] = mouse_button_0;
125 io.MouseDown[1] = mouse_button_1;
126 io.KeysDown[i] = ...
127
128 // 2) call NewFrame(), after this point you can use ImGui::* functions anytime
129 ImGui::NewFrame();
130
131 // 3) most of your application code here
132 ImGui::Begin("My window");
133 ImGui::Text("Hello, world.");
134 ImGui::End();
135 MyGameUpdate(); // may use ImGui functions
136 MyGameRender(); // may use ImGui functions
137
138 // 4) render & swap video buffers
139 ImGui::Render();
140 SwapBuffers();
141 }
142
143 - after calling ImGui::NewFrame() you can read back flags from the IO structure to tell how ImGui intends to use your inputs.
144 When 'io.WantCaptureMouse' or 'io.WantCaptureKeyboard' flags are set you may want to discard/hide the inputs from the rest of your application.
145 When 'io.WantInputsCharacters' is set to may want to notify your OS to popup an on-screen keyboard, if available.
146
147
148 API BREAKING CHANGES
149 ====================
150
151 Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix.
152 Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code.
153 Also read releases logs https://github.com/ocornut/imgui/releases for more details.
154
155 - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref github issue #337).
156 - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337)
157 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
158 - 2016/03/02 (1.48) - InputText() completion/history/always callbacks: if you modify the text buffer manually (without using DeleteChars()/InsertChars() helper) you need to maintain the BufTextLen field. added an assert.
159 - 2016/01/23 (1.48) - fixed not honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. if you had manual pixel-perfect alignment in place it might affect you.
160 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
161 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
162 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
163 GetCursorPos()/SetCursorPos() functions now include the scrolled amount. It shouldn't affect the majority of users, but take note that SetCursorPosX(100.0f) puts you at +100 from the starting x position which may include scrolling, not at +100 from the window left side.
164 GetContentRegionMax()/GetWindowContentRegionMin()/GetWindowContentRegionMax() functions allow include the scrolled amount. Typically those were used in cases where no scrolling would happen so it may not be a problem, but watch out!
165 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
166 - 2015/08/05 (1.44) - split imgui.cpp into extra files: imgui_demo.cpp imgui_draw.cpp imgui_internal.h that you need to add to your project.
167 - 2015/07/18 (1.44) - fixed angles in ImDrawList::PathArcTo(), PathArcToFast() (introduced in 1.43) being off by an extra PI for no justifiable reason
168 - 2015/07/14 (1.43) - add new ImFontAtlas::AddFont() API. For the old AddFont***, moved the 'font_no' parameter of ImFontAtlas::AddFont** functions to the ImFontConfig structure.
169 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
170 - 2015/07/08 (1.43) - switched rendering data to use indexed rendering. this is saving a fair amount of CPU/GPU and enables us to get anti-aliasing for a marginal cost.
171 this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
172 - if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
173 - the signature of the io.RenderDrawListsFn handler has changed!
174 ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
175 became:
176 ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
177 argument 'cmd_lists' -> 'draw_data->CmdLists'
178 argument 'cmd_lists_count' -> 'draw_data->CmdListsCount'
179 ImDrawList 'commands' -> 'CmdBuffer'
180 ImDrawList 'vtx_buffer' -> 'VtxBuffer'
181 ImDrawList n/a -> 'IdxBuffer' (new)
182 ImDrawCmd 'vtx_count' -> 'ElemCount'
183 ImDrawCmd 'clip_rect' -> 'ClipRect'
184 ImDrawCmd 'user_callback' -> 'UserCallback'
185 ImDrawCmd 'texture_id' -> 'TextureId'
186 - each ImDrawList now contains both a vertex buffer and an index buffer. For each command, render ElemCount/3 triangles using indices from the index buffer.
187 - if you REALLY cannot render indexed primitives, you can call the draw_data->DeIndexAllBuffers() method to de-index the buffers. This is slow and a waste of CPU/GPU. Prefer using indexed rendering!
188 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
189 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
190 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
191 - 2015/07/02 (1.42) - renamed GetScrollPosY() to GetScrollY(). Necessary to reduce confusion along with other scrolling functions, because positions (e.g. cursor position) are not equivalent to scrolling amount.
192 - 2015/06/14 (1.41) - changed ImageButton() default bg_col parameter from (0,0,0,1) (black) to (0,0,0,0) (transparent) - makes a difference when texture have transparence
193 - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry!
194 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
195 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
196 - 2015/05/27 (1.40) - removed the third 'repeat_if_held' parameter from Button() - sorry! it was rarely used and inconsistent. Use PushButtonRepeat(true) / PopButtonRepeat() to enable repeat on desired buttons.
197 - 2015/05/11 (1.40) - changed BeginPopup() API, takes a string identifier instead of a bool. ImGui needs to manage the open/closed state of popups. Call OpenPopup() to actually set the "opened" state of a popup. BeginPopup() returns true if the popup is opened.
198 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
199 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function (will obsolete).
200 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
201 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
202 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
203 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function (will obsolete).
204 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
205 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function (will obsolete).
206 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
207 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function (will obsolete).
208 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
209 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
210 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
211 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
212 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
213 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
214 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
215 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
216 this sequence:
217 const void* png_data;
218 unsigned int png_size;
219 ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size);
220 // <Copy to GPU>
221 became:
222 unsigned char* pixels;
223 int width, height;
224 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
225 // <Copy to GPU>
226 io.Fonts->TexID = (your_texture_identifier);
227 you now have much more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
228 it is now recommended that you sample the font texture with bilinear interpolation.
229 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
230 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
231 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
232 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
233 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
234 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
235 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
236 - 2014/10/02 (1.14) - renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL and imgui_user.cpp to imgui_user.inl (more IDE friendly)
237 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
238 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
239 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
240 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
241 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
242 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
243
244
245 FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
246 ======================================
247
248 Q: How can I help?
249 A: - If you are experienced enough with ImGui and with C/C++, look at the todo list and see how you want/can help!
250 - Become a Patron/donate. Convince your company to become a Patron or provide serious funding for development time.
251
252 Q: How do I update to a newer version of ImGui?
253 A: Overwrite the following files:
254 imgui.cpp
255 imgui.h
256 imgui_demo.cpp
257 imgui_draw.cpp
258 imgui_internal.h
259 stb_rect_pack.h
260 stb_textedit.h
261 stb_truetype.h
262 Don't overwrite imconfig.h if you have made modification to your copy.
263 Check the "API BREAKING CHANGES" sections for a list of occasional API breaking changes. If you have a problem with a function, search for its name
264 in the code, there will likely be a comment about it. Please report any issue to the GitHub page!
265
266
267 Q: What is ImTextureID and how do I display an image?
268 A: ImTextureID is a void* used to pass renderer-agnostic texture references around until it hits your render function.
269 ImGui knows nothing about what those bits represent, it just passes them around. It is up to you to decide what you want the void* to carry!
270 It could be an identifier to your OpenGL texture (cast GLuint to void*), a pointer to your custom engine material (cast MyMaterial* to void*), etc.
271 At the end of the chain, your renderer takes this void* to cast it back into whatever it needs to select a current texture to render.
272 Refer to examples applications, where each renderer (in a imgui_impl_xxxx.cpp file) is treating ImTextureID as a different thing.
273 (c++ tip: OpenGL uses integers to identify textures. You can safely store an integer into a void*, just cast it to void*, don't take it's address!)
274 To display a custom image/texture within an ImGui window, you may use ImGui::Image(), ImGui::ImageButton(), ImDrawList::AddImage() functions.
275 ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use.
276 It is your responsibility to get textures uploaded to your GPU.
277
278 Q: Can I have multiple widgets with the same label? Can I have widget without a label? (Yes)
279 A: Yes. A primer on the use of labels/IDs in ImGui..
280
281 - Elements that are not clickable, such as Text() items don't need an ID.
282
283 - Interactive widgets require state to be carried over multiple frames (most typically ImGui often needs to remember what is the "active" widget).
284 to do so they need an unique ID. unique ID are typically derived from a string label, an integer index or a pointer.
285
286 Button("OK"); // Label = "OK", ID = hash of "OK"
287 Button("Cancel"); // Label = "Cancel", ID = hash of "Cancel"
288
289 - ID are uniquely scoped within windows, tree nodes, etc. so no conflict can happen if you have two buttons called "OK" in two different windows
290 or in two different locations of a tree.
291
292 - If you have a same ID twice in the same location, you'll have a conflict:
293
294 Button("OK");
295 Button("OK"); // ID collision! Both buttons will be treated as the same.
296
297 Fear not! this is easy to solve and there are many ways to solve it!
298
299 - When passing a label you can optionally specify extra unique ID information within string itself. This helps solving the simpler collision cases.
300 use "##" to pass a complement to the ID that won't be visible to the end-user:
301
302 Button("Play"); // Label = "Play", ID = hash of "Play"
303 Button("Play##foo1"); // Label = "Play", ID = hash of "Play##foo1" (different from above)
304 Button("Play##foo2"); // Label = "Play", ID = hash of "Play##foo2" (different from above)
305
306 - If you want to completely hide the label, but still need an ID:
307
308 Checkbox("##On", &b); // Label = "", ID = hash of "##On" (no label!)
309
310 - Occasionally/rarely you might want change a label while preserving a constant ID. This allows you to animate labels.
311 For example you may want to include varying information in a window title bar (and windows are uniquely identified by their ID.. obviously)
312 Use "###" to pass a label that isn't part of ID:
313
314 Button("Hello###ID"; // Label = "Hello", ID = hash of "ID"
315 Button("World###ID"; // Label = "World", ID = hash of "ID" (same as above)
316
317 sprintf(buf, "My game (%f FPS)###MyGame");
318 Begin(buf); // Variable label, ID = hash of "MyGame"
319
320 - Use PushID() / PopID() to create scopes and avoid ID conflicts within the same Window.
321 This is the most convenient way of distinguishing ID if you are iterating and creating many UI elements.
322 You can push a pointer, a string or an integer value. Remember that ID are formed from the concatenation of everything in the ID stack!
323
324 for (int i = 0; i < 100; i++)
325 {
326 PushID(i);
327 Button("Click"); // Label = "Click", ID = hash of integer + "label" (unique)
328 PopID();
329 }
330
331 for (int i = 0; i < 100; i++)
332 {
333 MyObject* obj = Objects[i];
334 PushID(obj);
335 Button("Click"); // Label = "Click", ID = hash of pointer + "label" (unique)
336 PopID();
337 }
338
339 for (int i = 0; i < 100; i++)
340 {
341 MyObject* obj = Objects[i];
342 PushID(obj->Name);
343 Button("Click"); // Label = "Click", ID = hash of string + "label" (unique)
344 PopID();
345 }
346
347 - More example showing that you can stack multiple prefixes into the ID stack:
348
349 Button("Click"); // Label = "Click", ID = hash of "Click"
350 PushID("node");
351 Button("Click"); // Label = "Click", ID = hash of "node" + "Click"
352 PushID(my_ptr);
353 Button("Click"); // Label = "Click", ID = hash of "node" + ptr + "Click"
354 PopID();
355 PopID();
356
357 - Tree nodes implicitly creates a scope for you by calling PushID().
358
359 Button("Click"); // Label = "Click", ID = hash of "Click"
360 if (TreeNode("node"))
361 {
362 Button("Click"); // Label = "Click", ID = hash of "node" + "Click"
363 TreePop();
364 }
365
366 - When working with trees, ID are used to preserve the opened/closed state of each tree node.
367 Depending on your use cases you may want to use strings, indices or pointers as ID.
368 e.g. when displaying a single object that may change over time (1-1 relationship), using a static string as ID will preserve your node open/closed state when the targeted object change.
369 e.g. when displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state differently. experiment and see what makes more sense!
370
371 Q: I integrated ImGui in my engine and the text or lines are blurry..
372 A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
373 Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
374
375 Q: I integrated ImGui in my engine and some elements are clipping or disappearing when I move windows around..
376 A: Most likely you are mishandling the clipping rectangles in your render function. Rectangles provided by ImGui are defined as (x1,y1,x2,y2) and NOT as (x1,y1,width,height).
377
378 Q: How can I load a different font than the default? (default is an embedded version of ProggyClean.ttf, rendered at size 13)
379 A: Use the font atlas to load the TTF file you want:
380
381 ImGuiIO& io = ImGui::GetIO();
382 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
383 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
384
385 Q: How can I load multiple fonts?
386 A: Use the font atlas to pack them into a single texture:
387 (Read extra_fonts/README.txt and the code in ImFontAtlas for more details.)
388
389 ImGuiIO& io = ImGui::GetIO();
390 ImFont* font0 = io.Fonts->AddFontDefault();
391 ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
392 ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
393 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
394 // the first loaded font gets used by default
395 // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
396
397 // Options
398 ImFontConfig config;
399 config.OversampleH = 3;
400 config.OversampleV = 1;
401 config.GlyphExtraSpacing.x = 1.0f;
402 io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config);
403
404 // Combine multiple fonts into one
405 ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
406 ImFontConfig config;
407 config.MergeMode = true;
408 io.Fonts->AddFontDefault();
409 io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges);
410 io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese());
411
412 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
413 A: When loading a font, pass custom Unicode ranges to specify the glyphs to load. ImGui will support UTF-8 encoding across the board.
414 Character input depends on you passing the right character code to io.AddInputCharacter(). The example applications do that.
415
416 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese()); // Load Japanese characters
417 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
418 io.ImeWindowHandle = MY_HWND; // To input using Microsoft IME, give ImGui the hwnd of your application
419
420 - tip: the construct 'IMGUI_ONCE_UPON_A_FRAME { ... }' will run the block of code only once a frame. You can use it to quickly add custom UI in the middle of a deep nested inner loop in your code.
421 - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug"
422 - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window. this is also useful to set yourself in the context of another window (to get/set other settings)
423 - tip: you can call Render() multiple times (e.g for VR renders).
424 - tip: call and read the ShowTestWindow() code in imgui_demo.cpp for more example of how to use ImGui!
425
426
427 ISSUES & TODO-LIST
428 ==================
429 Issue numbers (#) refer to github issues listed at https://github.com/ocornut/imgui/issues
430 The list below consist mostly of notes of things to do before they are requested/discussed by users (at that point it usually happens on the github)
431
432 - doc: add a proper documentation+regression testing system (#435)
433 - window: maximum window size settings (per-axis). for large popups in particular user may not want the popup to fill all space.
434 - window: add a way for very transient windows (non-saved, temporary overlay over hundreds of objects) to "clean" up from the global window list. perhaps a lightweight explicit cleanup pass.
435 - window: calling SetNextWindowSize() every frame with <= 0 doesn't do anything, may be useful to allow (particularly when used for a single axis).
436 - window: auto-fit feedback loop when user relies on any dynamic layout (window width multiplier, column) appears weird to end-user. clarify.
437 - window: allow resizing of child windows (possibly given min/max for each axis?)
438 - window: background options for child windows, border option (disable rounding)
439 - window: add a way to clear an existing window instead of appending (e.g. for tooltip override using a consistent api rather than the deferred tooltip)
440 - window: resizing from any sides? + mouse cursor directives for app.
441 !- window: begin with *p_opened == false should return false.
442 - window: get size/pos helpers given names (see discussion in #249)
443 - window: a collapsed window can be stuck behind the main menu bar?
444 - window: when window is small, prioritize resize button over close button.
445 - window: detect extra End() call that pop the "Debug" window out and assert at call site instead of later.
446 - window/tooltip: allow to set the width of a tooltip to allow TextWrapped() etc. while keeping the height automatic.
447 - window: increase minimum size of a window with menus or fix the menu rendering so that it doesn't look odd.
448 - draw-list: maintaining bounding box per command would allow to merge draw command when clipping isn't relied on (typical non-scrolling window or non-overflowing column would merge with previous command).
449 !- scrolling: allow immediately effective change of scroll if we haven't appended items yet
450 - splitter/separator: formalize the splitter idiom into an official api (we want to handle n-way split) (#319)
451 - widgets: display mode: widget-label, label-widget (aligned on column or using fixed size), label-newline-tab-widget etc.
452 - widgets: clean up widgets internal toward exposing everything.
453 - widgets: add disabled and read-only modes (#211)
454 - main: considering adding an Init() function? some constructs are awkward in the implementation because of the lack of them.
455 !- main: make it so that a frame with no window registered won't refocus every window on subsequent frames (~bump LastFrameActive of all windows).
456 - main: IsItemHovered() make it more consistent for various type of widgets, widgets with multiple components, etc. also effectively IsHovered() region sometimes differs from hot region, e.g tree nodes
457 - main: IsItemHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode?
458 - input text: clean up the mess caused by converting UTF-8 <> wchar. the code is rather inefficient right now.
459 - input text: reorganize event handling, allow CharFilter to modify buffers, allow multiple events? (#541)
460 - input text: flag to disable live update of the user buffer (also applies to float/int text input)
461 - input text: resize behavior - field could stretch when being edited? hover tooltip shows more text?
462 - input text: add ImGuiInputTextFlags_EnterToApply? (off #218)
463 - input text multi-line: don't directly call AddText() which does an unnecessary vertex reserve for character count prior to clipping. and/or more line-based clipping to AddText(). and/or reorganize TextUnformatted/RenderText for more efficiency for large text (e.g TextUnformatted could clip and log separately, etc).
464 - input text multi-line: way to dynamically grow the buffer without forcing the user to initially allocate for worse case (follow up on #200)
465 - input text multi-line: line numbers? status bar? (follow up on #200)
466 - input text: allow centering/positioning text so that ctrl+clicking Drag or Slider keeps the textual value at the same pixel position.
467 - input number: optional range min/max for Input*() functions
468 - input number: holding [-]/[+] buttons could increase the step speed non-linearly (or user-controlled)
469 - input number: use mouse wheel to step up/down
470 - input number: applying arithmetics ops (+,-,*,/) messes up with text edit undo stack.
471 - button: provide a button that looks framed.
472 - text: proper alignment options
473 - image/image button: misalignment on padded/bordered button?
474 - image/image button: parameters are confusing, image() has tint_col,border_col whereas imagebutton() has bg_col/tint_col. Even thou they are different parameters ordering could be more consistent. can we fix that?
475 - layout: horizontal layout helper (#97)
476 - layout: horizontal flow until no space left (#404)
477 - layout: more generic alignment state (left/right/centered) for single items?
478 - layout: clean up the InputFloatN/SliderFloatN/ColorEdit4 layout code. item width should include frame padding.
479 - layout: BeginGroup() needs a border option.
480 - columns: declare column set (each column: fixed size, %, fill, distribute default size among fills) (#513, #125)
481 - columns: add a conditional parameter to SetColumnOffset() (#513, #125)
482 - columns: separator function or parameter that works within the column (currently Separator() bypass all columns) (#125)
483 - columns: columns header to act as button (~sort op) and allow resize/reorder (#513, #125)
484 - columns: user specify columns size (#513, #125)
485 - columns: flag to add horizontal separator above/below?
486 - columns/layout: setup minimum line height (equivalent of automatically calling AlignFirstTextHeightToWidgets)
487 - combo: sparse combo boxes (via function call?) / iterators
488 - combo: contents should extends to fit label if combo widget is small
489 - combo/listbox: keyboard control. need InputText-like non-active focus + key handling. considering keyboard for custom listbox (pr #203)
490 - listbox: multiple selection
491 - listbox: user may want to initial scroll to focus on the one selected value?
492 - listbox: keyboard navigation.
493 - listbox: scrolling should track modified selection.
494 !- popups/menus: clarify usage of popups id, how MenuItem/Selectable closing parent popups affects the ID, etc. this is quite fishy needs improvement! (#331, #402)
495 - popups: add variant using global identifier similar to Begin/End (#402)
496 - popups: border options. richer api like BeginChild() perhaps? (#197)
497 - tooltip: tooltip that doesn't fit in entire screen seems to lose their "last prefered button" and may teleport when moving mouse
498 - menus: local shortcuts, global shortcuts (#456, #126)
499 - menus: icons
500 - menus: menubars: some sort of priority / effect of main menu-bar on desktop size?
501 - menus: calling BeginMenu() twice with a same name doesn't seem to append nicely
502 - statusbar: add a per-window status bar helper similar to what menubar does.
503 - tabs (#261, #351)
504 - separator: separator on the initial position of a window is not visible (cursorpos.y <= clippos.y)
505 !- color: the color helpers/typing is a mess and needs sorting out.
506 - color: add a better color picker (#346)
507 - node/graph editor (#306)
508 - pie menus patterns (#434)
509 - drag'n drop, dragging helpers (carry dragging info, visualize drag source before clicking, drop target, etc.) (#143, #479)
510 - plot: PlotLines() should use the polygon-stroke facilities (currently issues with averaging normals)
511 - plot: make it easier for user to draw extra stuff into the graph (e.g: draw basis, highlight certain points, 2d plots, multiple plots)
512 - plot: "smooth" automatic scale over time, user give an input 0.0(full user scale) 1.0(full derived from value)
513 - plot: add a helper e.g. Plot(char* label, float value, float time_span=2.0f) that stores values and Plot them for you - probably another function name. and/or automatically allow to plot ANY displayed value (more reliance on stable ID)
514 - slider: allow using the [-]/[+] buttons used by InputFloat()/InputInt()
515 - slider: initial absolute click is imprecise. change to relative movement slider (same as scrollbar).
516 - slider: add dragging-based widgets to edit values with mouse (on 2 axises), saving screen real-estate.
517 - slider: tint background based on value (e.g. v_min -> v_max, or use 0.0f either side of the sign)
518 - slider & drag: int data passing through a float
519 - drag float: up/down axis
520 - drag float: added leeway on edge (e.g. a few invisible steps past the clamp limits)
521 - tree node / optimization: avoid formatting when clipped.
522 - tree node: clarify spacing, perhaps provide API to query exact spacing. provide API to draw the primitive. same with Bullet().
523 - tree node: tree-node/header right-most side doesn't take account of horizontal scrolling.
524 - tree node: add treenode/treepush int variants? because (void*) cast from int warns on some platforms/settings
525 - tree node: try to apply scrolling at time of TreePop() if node was just opened and end of node is past scrolling limits?
526 - tree node / selectable render mismatch which is visible if you use them both next to each other (e.g. cf. property viewer)
527 - textwrapped: figure out better way to use TextWrapped() in an always auto-resize context (tooltip, etc.) (git issue #249)
528 - settings: write more decent code to allow saving/loading new fields
529 - settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file
530 - style: add window shadows.
531 - style/optimization: store rounded corners in texture to use 1 quad per corner (filled and wireframe) to lower the cost of rounding.
532 - style: color-box not always square?
533 - style: a concept of "compact style" that the end-user can easily rely on (e.g. PushStyleCompact()?) that maps to other settings? avoid implementing duplicate helpers such as SmallCheckbox(), etc.
534 - style: try to make PushStyleVar() more robust to incorrect parameters (to be more friendly to edit & continues situation).
535 - style/opt: PopStyleVar could be optimized by having GetStyleVar returns the type, using a table mapping stylevar enum to data type.
536 - style: global scale setting.
537 - style: WindowPadding needs to be EVEN needs the 0.5 multiplier probably have a subtle effect on clip rectangle
538 - text: simple markup language for color change?
539 - font: dynamic font atlas to avoid baking huge ranges into bitmap and make scaling easier.
540 - font: helper to add glyph redirect/replacements (e.g. redirect alternate apostrophe unicode code points to ascii one, etc.)
541 - log: LogButtons() options for specifying depth and/or hiding depth slider
542 - log: have more control over the log scope (e.g. stop logging when leaving current tree node scope)
543 - log: be able to log anything (e.g. right-click on a window/tree-node, shows context menu? log into tty/file/clipboard)
544 - log: let user copy any window content to clipboard easily (CTRL+C on windows? while moving it? context menu?). code is commented because it fails with multiple Begin/End pairs.
545 - filters: set a current filter that tree node can automatically query to hide themselves
546 - filters: handle wildcards (with implicit leading/trailing *), regexps
547 - shortcuts: add a shortcut api, e.g. parse "&Save" and/or "Save (CTRL+S)", pass in to widgets or provide simple ways to use (button=activate, input=focus)
548 !- keyboard: tooltip & combo boxes are messing up / not honoring keyboard tabbing
549 - keyboard: full keyboard navigation and focus. (#323)
550 - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame)
551 - input: rework IO system to be able to pass actual ordered/timestamped events.
552 - input: allow to decide and pass explicit double-clicks (e.g. for windows by the CS_DBLCLKS style).
553 - input: support track pad style scrolling & slider edit.
554 - misc: provide a way to compile out the entire implementation while providing a dummy API (e.g. #define IMGUI_DUMMY_IMPL)
555 - misc: double-clicking on title bar to minimize isn't consistent, perhaps move to single-click on left-most collapse icon?
556 - misc: provide HoveredTime and ActivatedTime to ease the creation of animations.
557 - style editor: have a more global HSV setter (e.g. alter hue on all elements). consider replacing active/hovered by offset in HSV space? (#438)
558 - style editor: color child window height expressed in multiple of line height.
559 - remote: make a system like RemoteImGui first-class citizen/project (#75)
560 !- demo: custom render demo pushes a clipping rectangle past parent window bounds. expose ImGui::PushClipRect() from imgui_internal.h?
561 - drawlist: end-user probably can't call Clear() directly because we expect a texture to be pushed in the stack.
562 - examples: directx9: save/restore device state more thoroughly.
563 - examples: window minimize, maximize (#583)
564 - optimization: use another hash function than crc32, e.g. FNV1a
565 - optimization/render: merge command-lists with same clip-rect into one even if they aren't sequential? (as long as in-between clip rectangle don't overlap)?
566 - optimization: turn some the various stack vectors into statically-sized arrays
567 - optimization: better clipping for multi-component widgets
568 */
569
570 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
571 #define _CRT_SECURE_NO_WARNINGS
572 #endif
573
574 #include "imgui.h"
575 #define IMGUI_DEFINE_MATH_OPERATORS
576 #define IMGUI_DEFINE_PLACEMENT_NEW
577 #include "imgui_internal.h"
578
579 #include <ctype.h> // toupper, isprint
580 #include <math.h> // sqrtf, fabsf, fmodf, powf, cosf, sinf, floorf, ceilf
581 #include <stdlib.h> // NULL, malloc, free, qsort, atoi
582 #include <stdio.h> // vsnprintf, sscanf, printf
583 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
584 #include <stddef.h> // intptr_t
585 #else
586 #include <stdint.h> // intptr_t
587 #endif
588
589 #ifdef _MSC_VER
590 #pragma warning (disable: 4127) // condition expression is constant
591 #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
592 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
593 #define snprintf _snprintf
594 #endif
595
596 // Clang warnings with -Weverything
597 #ifdef __clang__
598 #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
599 #pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants ok.
600 #pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
601 #pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.
602 #pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it.
603 #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
604 #pragma clang diagnostic ignored "-Wmissing-noreturn" // warning : function xx could be declared with attribute 'noreturn' warning // GetDefaultFontData() asserts which some implementation makes it never return.
605 #pragma clang diagnostic ignored "-Wdeprecated-declarations"// warning : 'xx' is deprecated: The POSIX name for this item.. // for strdup used in demo code (so user can copy & paste the code)
606 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int'
607 #endif
608 #ifdef __GNUC__
609 #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
610 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
611 #endif
612
613 //-------------------------------------------------------------------------
614 // Forward Declarations
615 //-------------------------------------------------------------------------
616
617 static void LogRenderedText(const ImVec2& ref_pos, const char* text, const char* text_end = NULL);
618
619 static void PushMultiItemsWidths(int components, float w_full = 0.0f);
620 static float GetDraggedColumnOffset(int column_index);
621
622 static bool IsKeyPressedMap(ImGuiKey key, bool repeat = true);
623
624 static void SetCurrentFont(ImFont* font);
625 static void SetCurrentWindow(ImGuiWindow* window);
626 static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y);
627 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond);
628 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond);
629 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiSetCond cond);
630 static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs);
631 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
632 static inline bool IsWindowContentHoverable(ImGuiWindow* window);
633 static void ClearSetNextWindowData();
634 static void CheckStacksSize(ImGuiWindow* window, bool write);
635 static void Scrollbar(ImGuiWindow* window, bool horizontal);
636 static bool CloseWindowButton(bool* p_opened);
637
638 static void AddDrawListToRenderList(ImVector<ImDrawList*>& out_render_list, ImDrawList* draw_list);
639 static void AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiWindow* window);
640 static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>& out_sorted_windows, ImGuiWindow* window);
641
642 static ImGuiIniData* FindWindowSettings(const char* name);
643 static ImGuiIniData* AddWindowSettings(const char* name);
644 static void LoadSettings();
645 static void SaveSettings();
646 static void MarkSettingsDirty();
647
648 static void PushColumnClipRect(int column_index = -1);
649 static ImRect GetVisibleRect();
650
651 static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags);
652 static void CloseInactivePopups();
653 static void ClosePopupToLevel(int remaining);
654 static void ClosePopup(ImGuiID id);
655 static bool IsPopupOpen(ImGuiID id);
656 static ImGuiWindow* GetFrontMostModalRootWindow();
657 static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& rect_to_avoid);
658
659 static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data);
660 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
661 static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false);
662
663 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size);
664 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size);
665 static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2);
666 static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format);
667
668 //-----------------------------------------------------------------------------
669 // Platform dependent default implementations
670 //-----------------------------------------------------------------------------
671
672 static const char* GetClipboardTextFn_DefaultImpl();
673 static void SetClipboardTextFn_DefaultImpl(const char* text);
674 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
675
676 //-----------------------------------------------------------------------------
677 // Context
678 //-----------------------------------------------------------------------------
679
680 // We access everything through this pointer (always assumed to be != NULL)
681 // You can swap the pointer to a different context by calling ImGui::SetInternalState()
682 static ImGuiState GImDefaultState;
683 ImGuiState* GImGui = &GImDefaultState;
684
685 // Statically allocated default font atlas. This is merely a maneuver to keep ImFontAtlas definition at the bottom of the .h file (otherwise it'd be inside ImGuiIO)
686 // Also we wouldn't be able to new() one at this point, before users may define IO.MemAllocFn.
687 static ImFontAtlas GImDefaultFontAtlas;
688
689 //-----------------------------------------------------------------------------
690 // User facing structures
691 //-----------------------------------------------------------------------------
692
ImGuiStyle()693 ImGuiStyle::ImGuiStyle()
694 {
695 Alpha = 1.0f; // Global alpha applies to everything in ImGui
696 WindowPadding = ImVec2(8,8); // Padding within a window
697 WindowMinSize = ImVec2(32,32); // Minimum window size
698 WindowRounding = 9.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows
699 WindowTitleAlign = ImGuiAlign_Left; // Alignment for title bar text
700 ChildWindowRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
701 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
702 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
703 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
704 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
705 TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!
706 IndentSpacing = 22.0f; // Horizontal spacing when e.g. entering a tree node
707 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns
708 ScrollbarSize = 16.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
709 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
710 GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
711 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
712 DisplayWindowPadding = ImVec2(22,22); // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows.
713 DisplaySafeAreaPadding = ImVec2(4,4); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
714 AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
715 AntiAliasedShapes = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
716 CurveTessellationTol = 1.25f; // Tessellation tolerance. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
717
718 Colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
719 Colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
720 Colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.70f);
721 Colors[ImGuiCol_ChildWindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
722 Colors[ImGuiCol_PopupBg] = ImVec4(0.05f, 0.05f, 0.10f, 0.90f);
723 Colors[ImGuiCol_Border] = ImVec4(0.70f, 0.70f, 0.70f, 0.65f);
724 Colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
725 Colors[ImGuiCol_FrameBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.30f); // Background of checkbox, radio button, plot, slider, text input
726 Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.90f, 0.80f, 0.80f, 0.40f);
727 Colors[ImGuiCol_FrameBgActive] = ImVec4(0.90f, 0.65f, 0.65f, 0.45f);
728 Colors[ImGuiCol_TitleBg] = ImVec4(0.50f, 0.50f, 1.00f, 0.45f);
729 Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.40f, 0.40f, 0.80f, 0.20f);
730 Colors[ImGuiCol_TitleBgActive] = ImVec4(0.50f, 0.50f, 1.00f, 0.55f);
731 Colors[ImGuiCol_MenuBarBg] = ImVec4(0.40f, 0.40f, 0.55f, 0.80f);
732 Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.20f, 0.25f, 0.30f, 0.60f);
733 Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.40f, 0.40f, 0.80f, 0.30f);
734 Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.80f, 0.40f);
735 Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 0.40f);
736 Colors[ImGuiCol_ComboBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.99f);
737 Colors[ImGuiCol_CheckMark] = ImVec4(0.90f, 0.90f, 0.90f, 0.50f);
738 Colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
739 Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
740 Colors[ImGuiCol_Button] = ImVec4(0.67f, 0.40f, 0.40f, 0.60f);
741 Colors[ImGuiCol_ButtonHovered] = ImVec4(0.67f, 0.40f, 0.40f, 1.00f);
742 Colors[ImGuiCol_ButtonActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
743 Colors[ImGuiCol_Header] = ImVec4(0.40f, 0.40f, 0.90f, 0.45f);
744 Colors[ImGuiCol_HeaderHovered] = ImVec4(0.45f, 0.45f, 0.90f, 0.80f);
745 Colors[ImGuiCol_HeaderActive] = ImVec4(0.53f, 0.53f, 0.87f, 0.80f);
746 Colors[ImGuiCol_Column] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
747 Colors[ImGuiCol_ColumnHovered] = ImVec4(0.70f, 0.60f, 0.60f, 1.00f);
748 Colors[ImGuiCol_ColumnActive] = ImVec4(0.90f, 0.70f, 0.70f, 1.00f);
749 Colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
750 Colors[ImGuiCol_ResizeGripHovered] = ImVec4(1.00f, 1.00f, 1.00f, 0.60f);
751 Colors[ImGuiCol_ResizeGripActive] = ImVec4(1.00f, 1.00f, 1.00f, 0.90f);
752 Colors[ImGuiCol_CloseButton] = ImVec4(0.50f, 0.50f, 0.90f, 0.50f);
753 Colors[ImGuiCol_CloseButtonHovered] = ImVec4(0.70f, 0.70f, 0.90f, 0.60f);
754 Colors[ImGuiCol_CloseButtonActive] = ImVec4(0.70f, 0.70f, 0.70f, 1.00f);
755 Colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
756 Colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
757 Colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
758 Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
759 Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
760 Colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
761 }
762
ImGuiIO()763 ImGuiIO::ImGuiIO()
764 {
765 // Most fields are initialized with zero
766 memset(this, 0, sizeof(*this));
767
768 DisplaySize = ImVec2(-1.0f, -1.0f);
769 DeltaTime = 1.0f/60.0f;
770 IniSavingRate = 5.0f;
771 IniFilename = "imgui.ini";
772 LogFilename = "imgui_log.txt";
773 Fonts = &GImDefaultFontAtlas;
774 FontGlobalScale = 1.0f;
775 DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
776 MousePos = ImVec2(-1,-1);
777 MousePosPrev = ImVec2(-1,-1);
778 MouseDoubleClickTime = 0.30f;
779 MouseDoubleClickMaxDist = 6.0f;
780 MouseDragThreshold = 6.0f;
781 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++)
782 MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
783 for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++)
784 KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
785 for (int i = 0; i < ImGuiKey_COUNT; i++)
786 KeyMap[i] = -1;
787 KeyRepeatDelay = 0.250f;
788 KeyRepeatRate = 0.050f;
789 UserData = NULL;
790
791 // User functions
792 RenderDrawListsFn = NULL;
793 MemAllocFn = malloc;
794 MemFreeFn = free;
795 GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
796 SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
797 ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
798
799 // Set OS X style defaults based on __APPLE__ compile time flag
800 #ifdef __APPLE__
801 WordMovementUsesAltKey = true; // OS X style: Text editing cursor movement using Alt instead of Ctrl
802 ShortcutsUseSuperKey = true; // OS X style: Shortcuts using Cmd/Super instead of Ctrl
803 DoubleClickSelectsWord = true; // OS X style: Double click selects by word instead of selecting whole text
804 MultiSelectUsesSuperKey = true; // OS X style: Multi-selection in lists uses Cmd/Super instead of Ctrl
805 #endif
806 }
807
808 // Pass in translated ASCII characters for text input.
809 // - with glfw you can get those from the callback set in glfwSetCharCallback()
810 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(ImWchar c)811 void ImGuiIO::AddInputCharacter(ImWchar c)
812 {
813 const int n = ImStrlenW(InputCharacters);
814 if (n + 1 < IM_ARRAYSIZE(InputCharacters))
815 {
816 InputCharacters[n] = c;
817 InputCharacters[n+1] = '\0';
818 }
819 }
820
AddInputCharactersUTF8(const char * utf8_chars)821 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
822 {
823 // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more
824 const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar);
825 ImWchar wchars[wchars_buf_len];
826 ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL);
827 for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++)
828 AddInputCharacter(wchars[i]);
829 }
830
831 //-----------------------------------------------------------------------------
832 // HELPERS
833 //-----------------------------------------------------------------------------
834
835 #define IM_F32_TO_INT8(_VAL) ((int)((_VAL) * 255.0f + 0.5f))
836
837 #define IM_INT_MIN (-2147483647-1)
838 #define IM_INT_MAX (2147483647)
839
840 // Play it nice with Windows users. Notepad in 2015 still doesn't display text data with Unix-style \n.
841 #ifdef _WIN32
842 #define IM_NEWLINE "\r\n"
843 #else
844 #define IM_NEWLINE "\n"
845 #endif
846
ImIsPointInTriangle(const ImVec2 & p,const ImVec2 & a,const ImVec2 & b,const ImVec2 & c)847 bool ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, const ImVec2& c)
848 {
849 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
850 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
851 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
852 return ((b1 == b2) && (b2 == b3));
853 }
854
ImStricmp(const char * str1,const char * str2)855 int ImStricmp(const char* str1, const char* str2)
856 {
857 int d;
858 while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
859 return d;
860 }
861
ImStrnicmp(const char * str1,const char * str2,int count)862 int ImStrnicmp(const char* str1, const char* str2, int count)
863 {
864 int d = 0;
865 while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
866 return d;
867 }
868
ImStrdup(const char * str)869 char* ImStrdup(const char *str)
870 {
871 size_t len = strlen(str) + 1;
872 void* buff = ImGui::MemAlloc(len);
873 return (char*)memcpy(buff, (const void*)str, len);
874 }
875
ImStrlenW(const ImWchar * str)876 int ImStrlenW(const ImWchar* str)
877 {
878 int n = 0;
879 while (*str++) n++;
880 return n;
881 }
882
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)883 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
884 {
885 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
886 buf_mid_line--;
887 return buf_mid_line;
888 }
889
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)890 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
891 {
892 if (!needle_end)
893 needle_end = needle + strlen(needle);
894
895 const char un0 = (char)toupper(*needle);
896 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
897 {
898 if (toupper(*haystack) == un0)
899 {
900 const char* b = needle + 1;
901 for (const char* a = haystack + 1; b < needle_end; a++, b++)
902 if (toupper(*a) != toupper(*b))
903 break;
904 if (b == needle_end)
905 return haystack;
906 }
907 haystack++;
908 }
909 return NULL;
910 }
911
ImFormatString(char * buf,int buf_size,const char * fmt,...)912 int ImFormatString(char* buf, int buf_size, const char* fmt, ...)
913 {
914 va_list args;
915 va_start(args, fmt);
916 int w = vsnprintf(buf, buf_size, fmt, args);
917 va_end(args);
918 buf[buf_size-1] = 0;
919 return (w == -1) ? buf_size : w;
920 }
921
ImFormatStringV(char * buf,int buf_size,const char * fmt,va_list args)922 int ImFormatStringV(char* buf, int buf_size, const char* fmt, va_list args)
923 {
924 int w = vsnprintf(buf, buf_size, fmt, args);
925 buf[buf_size-1] = 0;
926 return (w == -1) ? buf_size : w;
927 }
928
929 // Pass data_size==0 for zero-terminated strings
930 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImHash(const void * data,int data_size,ImU32 seed)931 ImU32 ImHash(const void* data, int data_size, ImU32 seed)
932 {
933 static ImU32 crc32_lut[256] = { 0 };
934 if (!crc32_lut[1])
935 {
936 const ImU32 polynomial = 0xEDB88320;
937 for (ImU32 i = 0; i < 256; i++)
938 {
939 ImU32 crc = i;
940 for (ImU32 j = 0; j < 8; j++)
941 crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial);
942 crc32_lut[i] = crc;
943 }
944 }
945
946 seed = ~seed;
947 ImU32 crc = seed;
948 const unsigned char* current = (const unsigned char*)data;
949
950 if (data_size > 0)
951 {
952 // Known size
953 while (data_size--)
954 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++];
955 }
956 else
957 {
958 // Zero-terminated string
959 while (unsigned char c = *current++)
960 {
961 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
962 // Because this syntax is rarely used we are optimizing for the common case.
963 // - If we reach ### in the string we discard the hash so far and reset to the seed.
964 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller.
965 if (c == '#' && current[0] == '#' && current[1] == '#')
966 crc = seed;
967
968 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
969 }
970 }
971 return ~crc;
972 }
973
974 //-----------------------------------------------------------------------------
975 // ImText* helpers
976 //-----------------------------------------------------------------------------
977
978 // Convert UTF-8 to 32-bits character, process single character input.
979 // Based on stb_from_utf8() from github.com/nothings/stb/
980 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)981 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
982 {
983 unsigned int c = (unsigned int)-1;
984 const unsigned char* str = (const unsigned char*)in_text;
985 if (!(*str & 0x80))
986 {
987 c = (unsigned int)(*str++);
988 *out_char = c;
989 return 1;
990 }
991 if ((*str & 0xe0) == 0xc0)
992 {
993 *out_char = 0xFFFD; // will be invalid but not end of string
994 if (in_text_end && in_text_end - (const char*)str < 2) return 1;
995 if (*str < 0xc2) return 2;
996 c = (unsigned int)((*str++ & 0x1f) << 6);
997 if ((*str & 0xc0) != 0x80) return 2;
998 c += (*str++ & 0x3f);
999 *out_char = c;
1000 return 2;
1001 }
1002 if ((*str & 0xf0) == 0xe0)
1003 {
1004 *out_char = 0xFFFD; // will be invalid but not end of string
1005 if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1006 if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1007 if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1008 c = (unsigned int)((*str++ & 0x0f) << 12);
1009 if ((*str & 0xc0) != 0x80) return 3;
1010 c += (unsigned int)((*str++ & 0x3f) << 6);
1011 if ((*str & 0xc0) != 0x80) return 3;
1012 c += (*str++ & 0x3f);
1013 *out_char = c;
1014 return 3;
1015 }
1016 if ((*str & 0xf8) == 0xf0)
1017 {
1018 *out_char = 0xFFFD; // will be invalid but not end of string
1019 if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1020 if (*str > 0xf4) return 4;
1021 if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1022 if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1023 c = (unsigned int)((*str++ & 0x07) << 18);
1024 if ((*str & 0xc0) != 0x80) return 4;
1025 c += (unsigned int)((*str++ & 0x3f) << 12);
1026 if ((*str & 0xc0) != 0x80) return 4;
1027 c += (unsigned int)((*str++ & 0x3f) << 6);
1028 if ((*str & 0xc0) != 0x80) return 4;
1029 c += (*str++ & 0x3f);
1030 // utf-8 encodings of values used in surrogate pairs are invalid
1031 if ((c & 0xFFFFF800) == 0xD800) return 4;
1032 *out_char = c;
1033 return 4;
1034 }
1035 *out_char = 0;
1036 return 0;
1037 }
1038
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1039 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1040 {
1041 ImWchar* buf_out = buf;
1042 ImWchar* buf_end = buf + buf_size;
1043 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1044 {
1045 unsigned int c;
1046 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1047 if (c == 0)
1048 break;
1049 if (c < 0x10000) // FIXME: Losing characters that don't fit in 2 bytes
1050 *buf_out++ = (ImWchar)c;
1051 }
1052 *buf_out = 0;
1053 if (in_text_remaining)
1054 *in_text_remaining = in_text;
1055 return (int)(buf_out - buf);
1056 }
1057
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1058 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1059 {
1060 int char_count = 0;
1061 while ((!in_text_end || in_text < in_text_end) && *in_text)
1062 {
1063 unsigned int c;
1064 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1065 if (c == 0)
1066 break;
1067 if (c < 0x10000)
1068 char_count++;
1069 }
1070 return char_count;
1071 }
1072
1073 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1074 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1075 {
1076 if (c < 0x80)
1077 {
1078 buf[0] = (char)c;
1079 return 1;
1080 }
1081 if (c < 0x800)
1082 {
1083 if (buf_size < 2) return 0;
1084 buf[0] = (char)(0xc0 + (c >> 6));
1085 buf[1] = (char)(0x80 + (c & 0x3f));
1086 return 2;
1087 }
1088 if (c >= 0xdc00 && c < 0xe000)
1089 {
1090 return 0;
1091 }
1092 if (c >= 0xd800 && c < 0xdc00)
1093 {
1094 if (buf_size < 4) return 0;
1095 buf[0] = (char)(0xf0 + (c >> 18));
1096 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1097 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1098 buf[3] = (char)(0x80 + ((c ) & 0x3f));
1099 return 4;
1100 }
1101 //else if (c < 0x10000)
1102 {
1103 if (buf_size < 3) return 0;
1104 buf[0] = (char)(0xe0 + (c >> 12));
1105 buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1106 buf[2] = (char)(0x80 + ((c ) & 0x3f));
1107 return 3;
1108 }
1109 }
1110
ImTextCountUtf8BytesFromChar(unsigned int c)1111 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1112 {
1113 if (c < 0x80) return 1;
1114 if (c < 0x800) return 2;
1115 if (c >= 0xdc00 && c < 0xe000) return 0;
1116 if (c >= 0xd800 && c < 0xdc00) return 4;
1117 return 3;
1118 }
1119
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1120 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1121 {
1122 char* buf_out = buf;
1123 const char* buf_end = buf + buf_size;
1124 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1125 {
1126 unsigned int c = (unsigned int)(*in_text++);
1127 if (c < 0x80)
1128 *buf_out++ = (char)c;
1129 else
1130 buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1131 }
1132 *buf_out = 0;
1133 return (int)(buf_out - buf);
1134 }
1135
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1136 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1137 {
1138 int bytes_count = 0;
1139 while ((!in_text_end || in_text < in_text_end) && *in_text)
1140 {
1141 unsigned int c = (unsigned int)(*in_text++);
1142 if (c < 0x80)
1143 bytes_count++;
1144 else
1145 bytes_count += ImTextCountUtf8BytesFromChar(c);
1146 }
1147 return bytes_count;
1148 }
1149
ColorConvertU32ToFloat4(ImU32 in)1150 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1151 {
1152 float s = 1.0f/255.0f;
1153 return ImVec4((in & 0xFF) * s, ((in >> 8) & 0xFF) * s, ((in >> 16) & 0xFF) * s, (in >> 24) * s);
1154 }
1155
ColorConvertFloat4ToU32(const ImVec4 & in)1156 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1157 {
1158 ImU32 out;
1159 out = ((ImU32)IM_F32_TO_INT8(ImSaturate(in.x)));
1160 out |= ((ImU32)IM_F32_TO_INT8(ImSaturate(in.y))) << 8;
1161 out |= ((ImU32)IM_F32_TO_INT8(ImSaturate(in.z))) << 16;
1162 out |= ((ImU32)IM_F32_TO_INT8(ImSaturate(in.w))) << 24;
1163 return out;
1164 }
1165
1166 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1167 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
ColorConvertRGBtoHSV(float r,float g,float b,float & out_h,float & out_s,float & out_v)1168 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1169 {
1170 float K = 0.f;
1171 if (g < b)
1172 {
1173 const float tmp = g; g = b; b = tmp;
1174 K = -1.f;
1175 }
1176 if (r < g)
1177 {
1178 const float tmp = r; r = g; g = tmp;
1179 K = -2.f / 6.f - K;
1180 }
1181
1182 const float chroma = r - (g < b ? g : b);
1183 out_h = fabsf(K + (g - b) / (6.f * chroma + 1e-20f));
1184 out_s = chroma / (r + 1e-20f);
1185 out_v = r;
1186 }
1187
1188 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1189 // also http://en.wikipedia.org/wiki/HSL_and_HSV
ColorConvertHSVtoRGB(float h,float s,float v,float & out_r,float & out_g,float & out_b)1190 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1191 {
1192 if (s == 0.0f)
1193 {
1194 // gray
1195 out_r = out_g = out_b = v;
1196 return;
1197 }
1198
1199 h = fmodf(h, 1.0f) / (60.0f/360.0f);
1200 int i = (int)h;
1201 float f = h - (float)i;
1202 float p = v * (1.0f - s);
1203 float q = v * (1.0f - s * f);
1204 float t = v * (1.0f - s * (1.0f - f));
1205
1206 switch (i)
1207 {
1208 case 0: out_r = v; out_g = t; out_b = p; break;
1209 case 1: out_r = q; out_g = v; out_b = p; break;
1210 case 2: out_r = p; out_g = v; out_b = t; break;
1211 case 3: out_r = p; out_g = q; out_b = v; break;
1212 case 4: out_r = t; out_g = p; out_b = v; break;
1213 case 5: default: out_r = v; out_g = p; out_b = q; break;
1214 }
1215 }
1216
1217 // Load file content into memory
1218 // Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree()
ImLoadFileToMemory(const char * filename,const char * file_open_mode,int * out_file_size,int padding_bytes)1219 void* ImLoadFileToMemory(const char* filename, const char* file_open_mode, int* out_file_size, int padding_bytes)
1220 {
1221 IM_ASSERT(filename && file_open_mode);
1222 if (out_file_size)
1223 *out_file_size = 0;
1224
1225 FILE* f;
1226 if ((f = fopen(filename, file_open_mode)) == NULL)
1227 return NULL;
1228
1229 long file_size_signed;
1230 if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
1231 {
1232 fclose(f);
1233 return NULL;
1234 }
1235
1236 int file_size = (int)file_size_signed;
1237 void* file_data = ImGui::MemAlloc(file_size + padding_bytes);
1238 if (file_data == NULL)
1239 {
1240 fclose(f);
1241 return NULL;
1242 }
1243 if (fread(file_data, 1, (size_t)file_size, f) != (size_t)file_size)
1244 {
1245 fclose(f);
1246 ImGui::MemFree(file_data);
1247 return NULL;
1248 }
1249 if (padding_bytes > 0)
1250 memset((void *)(((char*)file_data) + file_size), 0, padding_bytes);
1251
1252 fclose(f);
1253 if (out_file_size)
1254 *out_file_size = file_size;
1255
1256 return file_data;
1257 }
1258
1259 //-----------------------------------------------------------------------------
1260 // ImGuiStorage
1261 //-----------------------------------------------------------------------------
1262
1263 // Helper: Key->value storage
Clear()1264 void ImGuiStorage::Clear()
1265 {
1266 Data.clear();
1267 }
1268
1269 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::Pair> & data,ImU32 key)1270 static ImVector<ImGuiStorage::Pair>::iterator LowerBound(ImVector<ImGuiStorage::Pair>& data, ImU32 key)
1271 {
1272 ImVector<ImGuiStorage::Pair>::iterator first = data.begin();
1273 ImVector<ImGuiStorage::Pair>::iterator last = data.end();
1274 int count = (int)(last - first);
1275 while (count > 0)
1276 {
1277 int count2 = count / 2;
1278 ImVector<ImGuiStorage::Pair>::iterator mid = first + count2;
1279 if (mid->key < key)
1280 {
1281 first = ++mid;
1282 count -= count2 + 1;
1283 }
1284 else
1285 {
1286 count = count2;
1287 }
1288 }
1289 return first;
1290 }
1291
GetInt(ImU32 key,int default_val) const1292 int ImGuiStorage::GetInt(ImU32 key, int default_val) const
1293 {
1294 ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1295 if (it == Data.end() || it->key != key)
1296 return default_val;
1297 return it->val_i;
1298 }
1299
GetFloat(ImU32 key,float default_val) const1300 float ImGuiStorage::GetFloat(ImU32 key, float default_val) const
1301 {
1302 ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1303 if (it == Data.end() || it->key != key)
1304 return default_val;
1305 return it->val_f;
1306 }
1307
GetVoidPtr(ImGuiID key) const1308 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1309 {
1310 ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1311 if (it == Data.end() || it->key != key)
1312 return NULL;
1313 return it->val_p;
1314 }
1315
1316 // References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer.
GetIntRef(ImGuiID key,int default_val)1317 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1318 {
1319 ImVector<Pair>::iterator it = LowerBound(Data, key);
1320 if (it == Data.end() || it->key != key)
1321 it = Data.insert(it, Pair(key, default_val));
1322 return &it->val_i;
1323 }
1324
GetFloatRef(ImGuiID key,float default_val)1325 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1326 {
1327 ImVector<Pair>::iterator it = LowerBound(Data, key);
1328 if (it == Data.end() || it->key != key)
1329 it = Data.insert(it, Pair(key, default_val));
1330 return &it->val_f;
1331 }
1332
GetVoidPtrRef(ImGuiID key,void * default_val)1333 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1334 {
1335 ImVector<Pair>::iterator it = LowerBound(Data, key);
1336 if (it == Data.end() || it->key != key)
1337 it = Data.insert(it, Pair(key, default_val));
1338 return &it->val_p;
1339 }
1340
1341 // FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame)
SetInt(ImU32 key,int val)1342 void ImGuiStorage::SetInt(ImU32 key, int val)
1343 {
1344 ImVector<Pair>::iterator it = LowerBound(Data, key);
1345 if (it == Data.end() || it->key != key)
1346 {
1347 Data.insert(it, Pair(key, val));
1348 return;
1349 }
1350 it->val_i = val;
1351 }
1352
SetFloat(ImU32 key,float val)1353 void ImGuiStorage::SetFloat(ImU32 key, float val)
1354 {
1355 ImVector<Pair>::iterator it = LowerBound(Data, key);
1356 if (it == Data.end() || it->key != key)
1357 {
1358 Data.insert(it, Pair(key, val));
1359 return;
1360 }
1361 it->val_f = val;
1362 }
1363
SetVoidPtr(ImU32 key,void * val)1364 void ImGuiStorage::SetVoidPtr(ImU32 key, void* val)
1365 {
1366 ImVector<Pair>::iterator it = LowerBound(Data, key);
1367 if (it == Data.end() || it->key != key)
1368 {
1369 Data.insert(it, Pair(key, val));
1370 return;
1371 }
1372 it->val_p = val;
1373 }
1374
SetAllInt(int v)1375 void ImGuiStorage::SetAllInt(int v)
1376 {
1377 for (int i = 0; i < Data.Size; i++)
1378 Data[i].val_i = v;
1379 }
1380
1381 //-----------------------------------------------------------------------------
1382 // ImGuiTextFilter
1383 //-----------------------------------------------------------------------------
1384
1385 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1386 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1387 {
1388 if (default_filter)
1389 {
1390 ImFormatString(InputBuf, IM_ARRAYSIZE(InputBuf), "%s", default_filter);
1391 Build();
1392 }
1393 else
1394 {
1395 InputBuf[0] = 0;
1396 CountGrep = 0;
1397 }
1398 }
1399
Draw(const char * label,float width)1400 bool ImGuiTextFilter::Draw(const char* label, float width)
1401 {
1402 if (width != 0.0f)
1403 ImGui::PushItemWidth(width);
1404 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1405 if (width != 0.0f)
1406 ImGui::PopItemWidth();
1407 if (value_changed)
1408 Build();
1409 return value_changed;
1410 }
1411
split(char separator,ImVector<TextRange> & out)1412 void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>& out)
1413 {
1414 out.resize(0);
1415 const char* wb = b;
1416 const char* we = wb;
1417 while (we < e)
1418 {
1419 if (*we == separator)
1420 {
1421 out.push_back(TextRange(wb, we));
1422 wb = we + 1;
1423 }
1424 we++;
1425 }
1426 if (wb != we)
1427 out.push_back(TextRange(wb, we));
1428 }
1429
Build()1430 void ImGuiTextFilter::Build()
1431 {
1432 Filters.resize(0);
1433 TextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
1434 input_range.split(',', Filters);
1435
1436 CountGrep = 0;
1437 for (int i = 0; i != Filters.Size; i++)
1438 {
1439 Filters[i].trim_blanks();
1440 if (Filters[i].empty())
1441 continue;
1442 if (Filters[i].front() != '-')
1443 CountGrep += 1;
1444 }
1445 }
1446
PassFilter(const char * text,const char * text_end) const1447 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
1448 {
1449 if (Filters.empty())
1450 return true;
1451
1452 if (text == NULL)
1453 text = "";
1454
1455 for (int i = 0; i != Filters.Size; i++)
1456 {
1457 const TextRange& f = Filters[i];
1458 if (f.empty())
1459 continue;
1460 if (f.front() == '-')
1461 {
1462 // Subtract
1463 if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL)
1464 return false;
1465 }
1466 else
1467 {
1468 // Grep
1469 if (ImStristr(text, text_end, f.begin(), f.end()) != NULL)
1470 return true;
1471 }
1472 }
1473
1474 // Implicit * grep
1475 if (CountGrep == 0)
1476 return true;
1477
1478 return false;
1479 }
1480
1481 //-----------------------------------------------------------------------------
1482 // ImGuiTextBuffer
1483 //-----------------------------------------------------------------------------
1484
1485 // On some platform vsnprintf() takes va_list by reference and modifies it.
1486 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1487 #ifndef va_copy
1488 #define va_copy(dest, src) (dest = src)
1489 #endif
1490
1491 // Helper: Text buffer for logging/accumulating text
appendv(const char * fmt,va_list args)1492 void ImGuiTextBuffer::appendv(const char* fmt, va_list args)
1493 {
1494 va_list args_copy;
1495 va_copy(args_copy, args);
1496
1497 int len = vsnprintf(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
1498 if (len <= 0)
1499 return;
1500
1501 const int write_off = Buf.Size;
1502 const int needed_sz = write_off + len;
1503 if (write_off + len >= Buf.Capacity)
1504 {
1505 int double_capacity = Buf.Capacity * 2;
1506 Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity);
1507 }
1508
1509 Buf.resize(needed_sz);
1510 ImFormatStringV(&Buf[write_off] - 1, len+1, fmt, args_copy);
1511 }
1512
append(const char * fmt,...)1513 void ImGuiTextBuffer::append(const char* fmt, ...)
1514 {
1515 va_list args;
1516 va_start(args, fmt);
1517 appendv(fmt, args);
1518 va_end(args);
1519 }
1520
1521 //-----------------------------------------------------------------------------
1522 // ImGuiSimpleColumns
1523 //-----------------------------------------------------------------------------
1524
ImGuiSimpleColumns()1525 ImGuiSimpleColumns::ImGuiSimpleColumns()
1526 {
1527 Count = 0;
1528 Spacing = Width = NextWidth = 0.0f;
1529 memset(Pos, 0, sizeof(Pos));
1530 memset(NextWidths, 0, sizeof(NextWidths));
1531 }
1532
Update(int count,float spacing,bool clear)1533 void ImGuiSimpleColumns::Update(int count, float spacing, bool clear)
1534 {
1535 IM_ASSERT(Count <= IM_ARRAYSIZE(Pos));
1536 Count = count;
1537 Width = NextWidth = 0.0f;
1538 Spacing = spacing;
1539 if (clear) memset(NextWidths, 0, sizeof(NextWidths));
1540 for (int i = 0; i < Count; i++)
1541 {
1542 if (i > 0 && NextWidths[i] > 0.0f)
1543 Width += Spacing;
1544 Pos[i] = (float)(int)Width;
1545 Width += NextWidths[i];
1546 NextWidths[i] = 0.0f;
1547 }
1548 }
1549
DeclColumns(float w0,float w1,float w2)1550 float ImGuiSimpleColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double
1551 {
1552 NextWidth = 0.0f;
1553 NextWidths[0] = ImMax(NextWidths[0], w0);
1554 NextWidths[1] = ImMax(NextWidths[1], w1);
1555 NextWidths[2] = ImMax(NextWidths[2], w2);
1556 for (int i = 0; i < 3; i++)
1557 NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f);
1558 return ImMax(Width, NextWidth);
1559 }
1560
CalcExtraSpace(float avail_w)1561 float ImGuiSimpleColumns::CalcExtraSpace(float avail_w)
1562 {
1563 return ImMax(0.0f, avail_w - Width);
1564 }
1565
1566 //-----------------------------------------------------------------------------
1567 // ImGuiWindow
1568 //-----------------------------------------------------------------------------
1569
ImGuiWindow(const char * name)1570 ImGuiWindow::ImGuiWindow(const char* name)
1571 {
1572 Name = ImStrdup(name);
1573 ID = ImHash(name, 0);
1574 IDStack.push_back(ID);
1575 MoveID = GetID("#MOVE");
1576
1577 Flags = 0;
1578 PosFloat = Pos = ImVec2(0.0f, 0.0f);
1579 Size = SizeFull = ImVec2(0.0f, 0.0f);
1580 SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
1581 WindowPadding = ImVec2(0.0f, 0.0f);
1582 Scroll = ImVec2(0.0f, 0.0f);
1583 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
1584 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
1585 ScrollbarX = ScrollbarY = false;
1586 ScrollbarSizes = ImVec2(0.0f, 0.0f);
1587 BorderSize = 0.0f;
1588 Active = WasActive = false;
1589 Accessed = false;
1590 Collapsed = false;
1591 SkipItems = false;
1592 BeginCount = 0;
1593 PopupID = 0;
1594 AutoFitFramesX = AutoFitFramesY = -1;
1595 AutoFitOnlyGrows = false;
1596 AutoPosLastDirection = -1;
1597 HiddenFrames = 0;
1598 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiSetCond_Always | ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing;
1599 SetWindowPosCenterWanted = false;
1600
1601 LastFrameActive = -1;
1602 ItemWidthDefault = 0.0f;
1603 FontWindowScale = 1.0f;
1604
1605 DrawList = (ImDrawList*)ImGui::MemAlloc(sizeof(ImDrawList));
1606 IM_PLACEMENT_NEW(DrawList) ImDrawList();
1607 DrawList->_OwnerName = Name;
1608 RootWindow = NULL;
1609 RootNonPopupWindow = NULL;
1610
1611 FocusIdxAllCounter = FocusIdxTabCounter = -1;
1612 FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = IM_INT_MAX;
1613 FocusIdxAllRequestNext = FocusIdxTabRequestNext = IM_INT_MAX;
1614 }
1615
~ImGuiWindow()1616 ImGuiWindow::~ImGuiWindow()
1617 {
1618 DrawList->~ImDrawList();
1619 ImGui::MemFree(DrawList);
1620 DrawList = NULL;
1621 ImGui::MemFree(Name);
1622 Name = NULL;
1623 }
1624
GetID(const char * str,const char * str_end)1625 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
1626 {
1627 ImGuiID seed = IDStack.back();
1628 ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
1629 ImGui::KeepAliveID(id);
1630 return id;
1631 }
1632
GetID(const void * ptr)1633 ImGuiID ImGuiWindow::GetID(const void* ptr)
1634 {
1635 ImGuiID seed = IDStack.back();
1636 ImGuiID id = ImHash(&ptr, sizeof(void*), seed);
1637 ImGui::KeepAliveID(id);
1638 return id;
1639 }
1640
1641 //-----------------------------------------------------------------------------
1642 // Internal API exposed in imgui_internal.h
1643 //-----------------------------------------------------------------------------
1644
SetCurrentWindow(ImGuiWindow * window)1645 static void SetCurrentWindow(ImGuiWindow* window)
1646 {
1647 ImGuiState& g = *GImGui;
1648 g.CurrentWindow = window;
1649 if (window)
1650 g.FontSize = window->CalcFontSize();
1651 }
1652
GetParentWindow()1653 ImGuiWindow* ImGui::GetParentWindow()
1654 {
1655 ImGuiState& g = *GImGui;
1656 IM_ASSERT(g.CurrentWindowStack.Size >= 2);
1657 return g.CurrentWindowStack[(unsigned int)g.CurrentWindowStack.Size - 2];
1658 }
1659
SetActiveID(ImGuiID id,ImGuiWindow * window=NULL)1660 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window = NULL)
1661 {
1662 ImGuiState& g = *GImGui;
1663 g.ActiveId = id;
1664 g.ActiveIdAllowOverlap = false;
1665 g.ActiveIdIsJustActivated = true;
1666 g.ActiveIdWindow = window;
1667 }
1668
SetHoveredID(ImGuiID id)1669 void ImGui::SetHoveredID(ImGuiID id)
1670 {
1671 ImGuiState& g = *GImGui;
1672 g.HoveredId = id;
1673 g.HoveredIdAllowOverlap = false;
1674 }
1675
KeepAliveID(ImGuiID id)1676 void ImGui::KeepAliveID(ImGuiID id)
1677 {
1678 ImGuiState& g = *GImGui;
1679 if (g.ActiveId == id)
1680 g.ActiveIdIsAlive = true;
1681 }
1682
1683 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_offset_y)1684 void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
1685 {
1686 ImGuiWindow* window = GetCurrentWindow();
1687 if (window->SkipItems)
1688 return;
1689
1690 // Always align ourselves on pixel boundaries
1691 ImGuiState& g = *GImGui;
1692 const float line_height = ImMax(window->DC.CurrentLineHeight, size.y);
1693 const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y);
1694 window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
1695 window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y));
1696 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
1697 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
1698
1699 //window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, 0xFF0000FF, 4); // Debug
1700
1701 window->DC.PrevLineHeight = line_height;
1702 window->DC.PrevLineTextBaseOffset = text_base_offset;
1703 window->DC.CurrentLineHeight = window->DC.CurrentLineTextBaseOffset = 0.0f;
1704 }
1705
ItemSize(const ImRect & bb,float text_offset_y)1706 void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
1707 {
1708 ItemSize(bb.GetSize(), text_offset_y);
1709 }
1710
1711 // Declare item bounding box for clipping and interaction.
1712 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
1713 // declares their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd().
ItemAdd(const ImRect & bb,const ImGuiID * id)1714 bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id)
1715 {
1716 ImGuiWindow* window = GetCurrentWindow();
1717 window->DC.LastItemID = id ? *id : 0;
1718 window->DC.LastItemRect = bb;
1719 if (IsClippedEx(bb, id, false))
1720 {
1721 window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = false;
1722 return false;
1723 }
1724
1725 // This is a sensible default, but widgets are free to override it after calling ItemAdd()
1726 ImGuiState& g = *GImGui;
1727 if (IsMouseHoveringRect(bb.Min, bb.Max))
1728 {
1729 // Matching the behavior of IsHovered() but ignore if ActiveId==window->MoveID (we clicked on the window background)
1730 // So that clicking on items with no active id such as Text() still returns true with IsItemHovered()
1731 window->DC.LastItemHoveredRect = true;
1732 window->DC.LastItemHoveredAndUsable = false;
1733 if (g.HoveredRootWindow == window->RootWindow)
1734 if (g.ActiveId == 0 || (id && g.ActiveId == *id) || g.ActiveIdAllowOverlap || (g.ActiveId == window->MoveID))
1735 if (IsWindowContentHoverable(window))
1736 window->DC.LastItemHoveredAndUsable = true;
1737 }
1738 else
1739 {
1740 window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = false;
1741 }
1742
1743 return true;
1744 }
1745
IsClippedEx(const ImRect & bb,const ImGuiID * id,bool clip_even_when_logged)1746 bool ImGui::IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged)
1747 {
1748 ImGuiState& g = *GImGui;
1749 ImGuiWindow* window = GetCurrentWindowRead();
1750
1751 if (!bb.Overlaps(window->ClipRect))
1752 {
1753 if (!id || *id != GImGui->ActiveId)
1754 if (clip_even_when_logged || !g.LogEnabled)
1755 return true;
1756 }
1757 return false;
1758 }
1759
IsHovered(const ImRect & bb,ImGuiID id,bool flatten_childs)1760 bool ImGui::IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs)
1761 {
1762 ImGuiState& g = *GImGui;
1763 if (g.HoveredId == 0 || g.HoveredId == id || g.HoveredIdAllowOverlap)
1764 {
1765 ImGuiWindow* window = GetCurrentWindowRead();
1766 if (g.HoveredWindow == window || (flatten_childs && g.HoveredRootWindow == window->RootWindow))
1767 if ((g.ActiveId == 0 || g.ActiveId == id || g.ActiveIdAllowOverlap) && ImGui::IsMouseHoveringRect(bb.Min, bb.Max))
1768 if (IsWindowContentHoverable(g.HoveredRootWindow))
1769 return true;
1770 }
1771 return false;
1772 }
1773
FocusableItemRegister(ImGuiWindow * window,bool is_active,bool tab_stop)1774 bool ImGui::FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_stop)
1775 {
1776 ImGuiState& g = *GImGui;
1777
1778 const bool allow_keyboard_focus = window->DC.AllowKeyboardFocus;
1779 window->FocusIdxAllCounter++;
1780 if (allow_keyboard_focus)
1781 window->FocusIdxTabCounter++;
1782
1783 // Process keyboard input at this point: TAB, Shift-TAB switch focus
1784 // We can always TAB out of a widget that doesn't allow tabbing in.
1785 if (tab_stop && window->FocusIdxAllRequestNext == IM_INT_MAX && window->FocusIdxTabRequestNext == IM_INT_MAX && is_active && IsKeyPressedMap(ImGuiKey_Tab))
1786 {
1787 // Modulo on index will be applied at the end of frame once we've got the total counter of items.
1788 window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1);
1789 }
1790
1791 if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
1792 return true;
1793
1794 if (allow_keyboard_focus)
1795 if (window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent)
1796 return true;
1797
1798 return false;
1799 }
1800
FocusableItemUnregister(ImGuiWindow * window)1801 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
1802 {
1803 window->FocusIdxAllCounter--;
1804 window->FocusIdxTabCounter--;
1805 }
1806
CalcItemSize(ImVec2 size,float default_x,float default_y)1807 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y)
1808 {
1809 ImGuiState& g = *GImGui;
1810 ImVec2 content_max;
1811 if (size.x < 0.0f || size.y < 0.0f)
1812 content_max = g.CurrentWindow->Pos + ImGui::GetContentRegionMax();
1813 if (size.x <= 0.0f)
1814 size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x;
1815 if (size.y <= 0.0f)
1816 size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y;
1817 return size;
1818 }
1819
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)1820 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
1821 {
1822 if (wrap_pos_x < 0.0f)
1823 return 0.0f;
1824
1825 ImGuiWindow* window = GetCurrentWindowRead();
1826 if (wrap_pos_x == 0.0f)
1827 wrap_pos_x = ImGui::GetContentRegionMax().x + window->Pos.x;
1828 else if (wrap_pos_x > 0.0f)
1829 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
1830
1831 const float wrap_width = wrap_pos_x > 0.0f ? ImMax(wrap_pos_x - pos.x, 0.00001f) : 0.0f;
1832 return wrap_width;
1833 }
1834
1835 //-----------------------------------------------------------------------------
1836
MemAlloc(size_t sz)1837 void* ImGui::MemAlloc(size_t sz)
1838 {
1839 GImGui->IO.MetricsAllocs++;
1840 return GImGui->IO.MemAllocFn(sz);
1841 }
1842
MemFree(void * ptr)1843 void ImGui::MemFree(void* ptr)
1844 {
1845 if (ptr) GImGui->IO.MetricsAllocs--;
1846 return GImGui->IO.MemFreeFn(ptr);
1847 }
1848
GetClipboardText()1849 const char* ImGui::GetClipboardText()
1850 {
1851 return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn() : "";
1852 }
1853
SetClipboardText(const char * text)1854 void ImGui::SetClipboardText(const char* text)
1855 {
1856 if (GImGui->IO.SetClipboardTextFn)
1857 GImGui->IO.SetClipboardTextFn(text);
1858 }
1859
GetVersion()1860 const char* ImGui::GetVersion()
1861 {
1862 return IMGUI_VERSION;
1863 }
1864
1865 // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
1866 // Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module
GetInternalState()1867 void* ImGui::GetInternalState()
1868 {
1869 return GImGui;
1870 }
1871
GetInternalStateSize()1872 size_t ImGui::GetInternalStateSize()
1873 {
1874 return sizeof(ImGuiState);
1875 }
1876
SetInternalState(void * state,bool construct)1877 void ImGui::SetInternalState(void* state, bool construct)
1878 {
1879 if (construct)
1880 IM_PLACEMENT_NEW(state) ImGuiState();
1881 GImGui = (ImGuiState*)state;
1882 }
1883
GetIO()1884 ImGuiIO& ImGui::GetIO()
1885 {
1886 return GImGui->IO;
1887 }
1888
GetStyle()1889 ImGuiStyle& ImGui::GetStyle()
1890 {
1891 return GImGui->Style;
1892 }
1893
1894 // Same value as passed to your RenderDrawListsFn() function. valid after Render() and until the next call to NewFrame()
GetDrawData()1895 ImDrawData* ImGui::GetDrawData()
1896 {
1897 return GImGui->RenderDrawData.Valid ? &GImGui->RenderDrawData : NULL;
1898 }
1899
GetTime()1900 float ImGui::GetTime()
1901 {
1902 return GImGui->Time;
1903 }
1904
GetFrameCount()1905 int ImGui::GetFrameCount()
1906 {
1907 return GImGui->FrameCount;
1908 }
1909
NewFrame()1910 void ImGui::NewFrame()
1911 {
1912 ImGuiState& g = *GImGui;
1913
1914 // Check user data
1915 IM_ASSERT(g.IO.DeltaTime >= 0.0f); // Need a positive DeltaTime (zero is tolerated but will cause some timing issues)
1916 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f);
1917 IM_ASSERT(g.IO.Fonts->Fonts.Size > 0); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
1918 IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
1919 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f); // Invalid style setting
1920
1921 if (!g.Initialized)
1922 {
1923 // Initialize on first frame
1924 g.LogClipboard = (ImGuiTextBuffer*)ImGui::MemAlloc(sizeof(ImGuiTextBuffer));
1925 IM_PLACEMENT_NEW(g.LogClipboard) ImGuiTextBuffer();
1926
1927 IM_ASSERT(g.Settings.empty());
1928 LoadSettings();
1929 g.Initialized = true;
1930 }
1931
1932 SetCurrentFont(g.IO.Fonts->Fonts[0]);
1933
1934 g.Time += g.IO.DeltaTime;
1935 g.FrameCount += 1;
1936 g.Tooltip[0] = '\0';
1937 g.OverlayDrawList.Clear();
1938 g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID);
1939 g.OverlayDrawList.PushClipRectFullScreen();
1940 g.OverlayDrawList.AddDrawCmd();
1941
1942 // Mark rendering data as invalid to prevent user who may have a handle on it to use it
1943 g.RenderDrawData.Valid = false;
1944 g.RenderDrawData.CmdLists = NULL;
1945 g.RenderDrawData.CmdListsCount = g.RenderDrawData.TotalVtxCount = g.RenderDrawData.TotalIdxCount = 0;
1946
1947 // Update inputs state
1948 if (g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0)
1949 g.IO.MousePos = ImVec2(-9999.0f, -9999.0f);
1950 if ((g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0) || (g.IO.MousePosPrev.x < 0 && g.IO.MousePosPrev.y < 0)) // if mouse just appeared or disappeared (negative coordinate) we cancel out movement in MouseDelta
1951 g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
1952 else
1953 g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
1954 g.IO.MousePosPrev = g.IO.MousePos;
1955 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
1956 {
1957 g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
1958 g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
1959 g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
1960 g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f;
1961 g.IO.MouseDoubleClicked[i] = false;
1962 if (g.IO.MouseClicked[i])
1963 {
1964 if (g.Time - g.IO.MouseClickedTime[i] < g.IO.MouseDoubleClickTime)
1965 {
1966 if (ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
1967 g.IO.MouseDoubleClicked[i] = true;
1968 g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click
1969 }
1970 else
1971 {
1972 g.IO.MouseClickedTime[i] = g.Time;
1973 }
1974 g.IO.MouseClickedPos[i] = g.IO.MousePos;
1975 g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
1976 }
1977 else if (g.IO.MouseDown[i])
1978 {
1979 g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]));
1980 }
1981 }
1982 memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
1983 for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
1984 g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f;
1985
1986 // Calculate frame-rate for the user, as a purely luxurious feature
1987 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
1988 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
1989 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
1990 g.IO.Framerate = 1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame));
1991
1992 // Clear reference to active widget if the widget isn't alive anymore
1993 g.HoveredIdPreviousFrame = g.HoveredId;
1994 g.HoveredId = 0;
1995 g.HoveredIdAllowOverlap = false;
1996 if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
1997 SetActiveID(0);
1998 g.ActiveIdPreviousFrame = g.ActiveId;
1999 g.ActiveIdIsAlive = false;
2000 g.ActiveIdIsJustActivated = false;
2001 if (!g.ActiveId)
2002 g.MovedWindow = NULL;
2003
2004 // Delay saving settings so we don't spam disk too much
2005 if (g.SettingsDirtyTimer > 0.0f)
2006 {
2007 g.SettingsDirtyTimer -= g.IO.DeltaTime;
2008 if (g.SettingsDirtyTimer <= 0.0f)
2009 SaveSettings();
2010 }
2011
2012 // Find the window we are hovering. Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow
2013 g.HoveredWindow = FindHoveredWindow(g.IO.MousePos, false);
2014 if (g.HoveredWindow && (g.HoveredWindow->Flags & ImGuiWindowFlags_ChildWindow))
2015 g.HoveredRootWindow = g.HoveredWindow->RootWindow;
2016 else
2017 g.HoveredRootWindow = FindHoveredWindow(g.IO.MousePos, true);
2018
2019 if (ImGuiWindow* modal_window = GetFrontMostModalRootWindow())
2020 {
2021 g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f);
2022 if (g.HoveredRootWindow != modal_window)
2023 g.HoveredRootWindow = g.HoveredWindow = NULL;
2024 }
2025 else
2026 {
2027 g.ModalWindowDarkeningRatio = 0.0f;
2028 }
2029
2030 // Are we using inputs? Tell user so they can capture/discard the inputs away from the rest of their application.
2031 // When clicking outside of a window we assume the click is owned by the application and won't request capture. We need to track click ownership.
2032 int mouse_earliest_button_down = -1;
2033 bool mouse_any_down = false;
2034 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
2035 {
2036 if (g.IO.MouseClicked[i])
2037 g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenedPopupStack.empty());
2038 mouse_any_down |= g.IO.MouseDown[i];
2039 if (g.IO.MouseDown[i])
2040 if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[mouse_earliest_button_down] > g.IO.MouseClickedTime[i])
2041 mouse_earliest_button_down = i;
2042 }
2043 bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
2044 if (g.CaptureMouseNextFrame != -1)
2045 g.IO.WantCaptureMouse = (g.CaptureMouseNextFrame != 0);
2046 else
2047 g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.ActiveId != 0) || (!g.OpenedPopupStack.empty());
2048 g.IO.WantCaptureKeyboard = (g.CaptureKeyboardNextFrame != -1) ? (g.CaptureKeyboardNextFrame != 0) : (g.ActiveId != 0);
2049 g.IO.WantTextInput = (g.ActiveId != 0 && g.InputTextState.Id == g.ActiveId);
2050 g.MouseCursor = ImGuiMouseCursor_Arrow;
2051 g.CaptureMouseNextFrame = g.CaptureKeyboardNextFrame = -1;
2052 g.OsImePosRequest = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
2053
2054 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
2055 if (!mouse_avail_to_imgui)
2056 g.HoveredWindow = g.HoveredRootWindow = NULL;
2057
2058 // Scale & Scrolling
2059 if (g.HoveredWindow && g.IO.MouseWheel != 0.0f && !g.HoveredWindow->Collapsed)
2060 {
2061 ImGuiWindow* window = g.HoveredWindow;
2062 if (g.IO.KeyCtrl)
2063 {
2064 if (g.IO.FontAllowUserScaling)
2065 {
2066 // Zoom / Scale window
2067 float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
2068 float scale = new_font_scale / window->FontWindowScale;
2069 window->FontWindowScale = new_font_scale;
2070
2071 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
2072 window->Pos += offset;
2073 window->PosFloat += offset;
2074 window->Size *= scale;
2075 window->SizeFull *= scale;
2076 }
2077 }
2078 else
2079 {
2080 // Scroll
2081 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse))
2082 {
2083 const int scroll_lines = (window->Flags & ImGuiWindowFlags_ComboBox) ? 3 : 5;
2084 SetWindowScrollY(window, window->Scroll.y - g.IO.MouseWheel * window->CalcFontSize() * scroll_lines);
2085 }
2086 }
2087 }
2088
2089 // Pressing TAB activate widget focus
2090 // NB: Don't discard FocusedWindow if it isn't active, so that a window that go on/off programatically won't lose its keyboard focus.
2091 if (g.ActiveId == 0 && g.FocusedWindow != NULL && g.FocusedWindow->Active && IsKeyPressedMap(ImGuiKey_Tab, false))
2092 g.FocusedWindow->FocusIdxTabRequestNext = 0;
2093
2094 // Mark all windows as not visible
2095 for (int i = 0; i != g.Windows.Size; i++)
2096 {
2097 ImGuiWindow* window = g.Windows[i];
2098 window->WasActive = window->Active;
2099 window->Active = false;
2100 window->Accessed = false;
2101 }
2102
2103 // No window should be open at the beginning of the frame.
2104 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
2105 g.CurrentWindowStack.resize(0);
2106 g.CurrentPopupStack.resize(0);
2107 CloseInactivePopups();
2108
2109 // Create implicit window - we will only render it if the user has added something to it.
2110 ImGui::SetNextWindowSize(ImVec2(400,400), ImGuiSetCond_FirstUseEver);
2111 ImGui::Begin("Debug");
2112 }
2113
2114 // NB: behavior of ImGui after Shutdown() is not tested/guaranteed at the moment. This function is merely here to free heap allocations.
Shutdown()2115 void ImGui::Shutdown()
2116 {
2117 ImGuiState& g = *GImGui;
2118
2119 // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame)
2120 if (g.IO.Fonts) // Testing for NULL to allow user to NULLify in case of running Shutdown() on multiple contexts. Bit hacky.
2121 g.IO.Fonts->Clear();
2122
2123 // Cleanup of other data are conditional on actually having used ImGui.
2124 if (!g.Initialized)
2125 return;
2126
2127 SaveSettings();
2128
2129 for (int i = 0; i < g.Windows.Size; i++)
2130 {
2131 g.Windows[i]->~ImGuiWindow();
2132 ImGui::MemFree(g.Windows[i]);
2133 }
2134 g.Windows.clear();
2135 g.WindowsSortBuffer.clear();
2136 g.CurrentWindowStack.clear();
2137 g.FocusedWindow = NULL;
2138 g.HoveredWindow = NULL;
2139 g.HoveredRootWindow = NULL;
2140 for (int i = 0; i < g.Settings.Size; i++)
2141 ImGui::MemFree(g.Settings[i].Name);
2142 g.Settings.clear();
2143 g.ColorModifiers.clear();
2144 g.StyleModifiers.clear();
2145 g.FontStack.clear();
2146 g.OpenedPopupStack.clear();
2147 g.CurrentPopupStack.clear();
2148 for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2149 g.RenderDrawLists[i].clear();
2150 g.OverlayDrawList.ClearFreeMemory();
2151 g.ColorEditModeStorage.Clear();
2152 if (g.PrivateClipboard)
2153 {
2154 ImGui::MemFree(g.PrivateClipboard);
2155 g.PrivateClipboard = NULL;
2156 }
2157 g.InputTextState.Text.clear();
2158 g.InputTextState.InitialText.clear();
2159 g.InputTextState.TempTextBuffer.clear();
2160
2161 if (g.LogFile && g.LogFile != stdout)
2162 {
2163 fclose(g.LogFile);
2164 g.LogFile = NULL;
2165 }
2166 if (g.LogClipboard)
2167 {
2168 g.LogClipboard->~ImGuiTextBuffer();
2169 ImGui::MemFree(g.LogClipboard);
2170 }
2171
2172 g.Initialized = false;
2173 }
2174
FindWindowSettings(const char * name)2175 static ImGuiIniData* FindWindowSettings(const char* name)
2176 {
2177 ImGuiState& g = *GImGui;
2178 ImGuiID id = ImHash(name, 0);
2179 for (int i = 0; i != g.Settings.Size; i++)
2180 {
2181 ImGuiIniData* ini = &g.Settings[i];
2182 if (ini->ID == id)
2183 return ini;
2184 }
2185 return NULL;
2186 }
2187
AddWindowSettings(const char * name)2188 static ImGuiIniData* AddWindowSettings(const char* name)
2189 {
2190 GImGui->Settings.resize(GImGui->Settings.Size + 1);
2191 ImGuiIniData* ini = &GImGui->Settings.back();
2192 ini->Name = ImStrdup(name);
2193 ini->ID = ImHash(name, 0);
2194 ini->Collapsed = false;
2195 ini->Pos = ImVec2(FLT_MAX,FLT_MAX);
2196 ini->Size = ImVec2(0,0);
2197 return ini;
2198 }
2199
2200 // Zero-tolerance, poor-man .ini parsing
2201 // FIXME: Write something less rubbish
LoadSettings()2202 static void LoadSettings()
2203 {
2204 ImGuiState& g = *GImGui;
2205 const char* filename = g.IO.IniFilename;
2206 if (!filename)
2207 return;
2208
2209 int file_size;
2210 char* file_data = (char*)ImLoadFileToMemory(filename, "rb", &file_size, 1);
2211 if (!file_data)
2212 return;
2213
2214 ImGuiIniData* settings = NULL;
2215 const char* buf_end = file_data + file_size;
2216 for (const char* line_start = file_data; line_start < buf_end; )
2217 {
2218 const char* line_end = line_start;
2219 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
2220 line_end++;
2221
2222 if (line_start[0] == '[' && line_end > line_start && line_end[-1] == ']')
2223 {
2224 char name[64];
2225 ImFormatString(name, IM_ARRAYSIZE(name), "%.*s", (int)(line_end-line_start-2), line_start+1);
2226 settings = FindWindowSettings(name);
2227 if (!settings)
2228 settings = AddWindowSettings(name);
2229 }
2230 else if (settings)
2231 {
2232 float x, y;
2233 int i;
2234 if (sscanf(line_start, "Pos=%f,%f", &x, &y) == 2)
2235 settings->Pos = ImVec2(x, y);
2236 else if (sscanf(line_start, "Size=%f,%f", &x, &y) == 2)
2237 settings->Size = ImMax(ImVec2(x, y), g.Style.WindowMinSize);
2238 else if (sscanf(line_start, "Collapsed=%d", &i) == 1)
2239 settings->Collapsed = (i != 0);
2240 }
2241
2242 line_start = line_end+1;
2243 }
2244
2245 ImGui::MemFree(file_data);
2246 }
2247
SaveSettings()2248 static void SaveSettings()
2249 {
2250 ImGuiState& g = *GImGui;
2251 const char* filename = g.IO.IniFilename;
2252 if (!filename)
2253 return;
2254
2255 // Gather data from windows that were active during this session
2256 for (int i = 0; i != g.Windows.Size; i++)
2257 {
2258 ImGuiWindow* window = g.Windows[i];
2259 if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
2260 continue;
2261 ImGuiIniData* settings = FindWindowSettings(window->Name);
2262 settings->Pos = window->Pos;
2263 settings->Size = window->SizeFull;
2264 settings->Collapsed = window->Collapsed;
2265 }
2266
2267 // Write .ini file
2268 // If a window wasn't opened in this session we preserve its settings
2269 FILE* f = fopen(filename, "wt");
2270 if (!f)
2271 return;
2272 for (int i = 0; i != g.Settings.Size; i++)
2273 {
2274 const ImGuiIniData* settings = &g.Settings[i];
2275 if (settings->Pos.x == FLT_MAX)
2276 continue;
2277 const char* name = settings->Name;
2278 if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
2279 name = p;
2280 fprintf(f, "[%s]\n", name);
2281 fprintf(f, "Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
2282 fprintf(f, "Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
2283 fprintf(f, "Collapsed=%d\n", settings->Collapsed);
2284 fprintf(f, "\n");
2285 }
2286
2287 fclose(f);
2288 }
2289
MarkSettingsDirty()2290 static void MarkSettingsDirty()
2291 {
2292 ImGuiState& g = *GImGui;
2293 if (g.SettingsDirtyTimer <= 0.0f)
2294 g.SettingsDirtyTimer = g.IO.IniSavingRate;
2295 }
2296
2297 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)2298 static int ChildWindowComparer(const void* lhs, const void* rhs)
2299 {
2300 const ImGuiWindow* a = *(const ImGuiWindow**)lhs;
2301 const ImGuiWindow* b = *(const ImGuiWindow**)rhs;
2302 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
2303 return d;
2304 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
2305 return d;
2306 if (int d = (a->Flags & ImGuiWindowFlags_ComboBox) - (b->Flags & ImGuiWindowFlags_ComboBox))
2307 return d;
2308 return 0;
2309 }
2310
AddWindowToSortedBuffer(ImVector<ImGuiWindow * > & out_sorted_windows,ImGuiWindow * window)2311 static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>& out_sorted_windows, ImGuiWindow* window)
2312 {
2313 out_sorted_windows.push_back(window);
2314 if (window->Active)
2315 {
2316 int count = window->DC.ChildWindows.Size;
2317 if (count > 1)
2318 qsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
2319 for (int i = 0; i < count; i++)
2320 {
2321 ImGuiWindow* child = window->DC.ChildWindows[i];
2322 if (child->Active)
2323 AddWindowToSortedBuffer(out_sorted_windows, child);
2324 }
2325 }
2326 }
2327
AddDrawListToRenderList(ImVector<ImDrawList * > & out_render_list,ImDrawList * draw_list)2328 static void AddDrawListToRenderList(ImVector<ImDrawList*>& out_render_list, ImDrawList* draw_list)
2329 {
2330 if (draw_list->CmdBuffer.empty())
2331 return;
2332
2333 // Remove trailing command if unused
2334 ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
2335 if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
2336 {
2337 draw_list->CmdBuffer.pop_back();
2338 if (draw_list->CmdBuffer.empty())
2339 return;
2340 }
2341
2342 // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = 2 bytes = 64K vertices)
2343 // If this assert triggers because you are drawing lots of stuff manually, A) workaround by calling BeginChild()/EndChild() to put your draw commands in multiple draw lists, B) #define ImDrawIdx to a 'unsigned int' in imconfig.h and render accordingly.
2344 IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); // Sanity check. Bug or mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
2345 IM_ASSERT((int64_t)draw_list->_VtxCurrentIdx <= ((int64_t)1L << (sizeof(ImDrawIdx)*8))); // Too many vertices in same ImDrawList. See comment above.
2346
2347 out_render_list.push_back(draw_list);
2348 GImGui->IO.MetricsRenderVertices += draw_list->VtxBuffer.Size;
2349 GImGui->IO.MetricsRenderIndices += draw_list->IdxBuffer.Size;
2350 }
2351
AddWindowToRenderList(ImVector<ImDrawList * > & out_render_list,ImGuiWindow * window)2352 static void AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiWindow* window)
2353 {
2354 AddDrawListToRenderList(out_render_list, window->DrawList);
2355 for (int i = 0; i < window->DC.ChildWindows.Size; i++)
2356 {
2357 ImGuiWindow* child = window->DC.ChildWindows[i];
2358 if (!child->Active) // clipped children may have been marked not active
2359 continue;
2360 if ((child->Flags & ImGuiWindowFlags_Popup) && child->HiddenFrames > 0)
2361 continue;
2362 AddWindowToRenderList(out_render_list, child);
2363 }
2364 }
2365
PushClipRect(const ImVec2 & clip_rect_min,const ImVec2 & clip_rect_max,bool intersect_with_existing_clip_rect)2366 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_existing_clip_rect)
2367 {
2368 ImGuiWindow* window = GetCurrentWindow();
2369
2370 ImRect cr(clip_rect_min, clip_rect_max);
2371 if (intersect_with_existing_clip_rect)
2372 {
2373 // Clip our argument with the current clip rect
2374 cr.Clip(window->ClipRect);
2375 }
2376 cr.Max.x = ImMax(cr.Min.x, cr.Max.x);
2377 cr.Max.y = ImMax(cr.Min.y, cr.Max.y);
2378
2379 IM_ASSERT(cr.Min.x <= cr.Max.x && cr.Min.y <= cr.Max.y);
2380 window->ClipRect = cr;
2381 window->DrawList->PushClipRect(ImVec4(cr.Min.x, cr.Min.y, cr.Max.x, cr.Max.y));
2382 }
2383
PopClipRect()2384 void ImGui::PopClipRect()
2385 {
2386 ImGuiWindow* window = GetCurrentWindow();
2387 window->DrawList->PopClipRect();
2388 window->ClipRect = window->DrawList->_ClipRectStack.back();
2389 }
2390
2391 // This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal.
EndFrame()2392 void ImGui::EndFrame()
2393 {
2394 ImGuiState& g = *GImGui;
2395 IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame()
2396 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // ImGui::EndFrame() called multiple times, or forgot to call ImGui::NewFrame() again
2397
2398 // Render tooltip
2399 if (g.Tooltip[0])
2400 {
2401 ImGui::BeginTooltip();
2402 ImGui::TextUnformatted(g.Tooltip);
2403 ImGui::EndTooltip();
2404 }
2405
2406 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
2407 if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.OsImePosRequest - g.OsImePosSet) > 0.0001f)
2408 {
2409 g.IO.ImeSetInputScreenPosFn((int)g.OsImePosRequest.x, (int)g.OsImePosRequest.y);
2410 g.OsImePosSet = g.OsImePosRequest;
2411 }
2412
2413 // Hide implicit "Debug" window if it hasn't been used
2414 IM_ASSERT(g.CurrentWindowStack.Size == 1); // Mismatched Begin()/End() calls
2415 if (g.CurrentWindow && !g.CurrentWindow->Accessed)
2416 g.CurrentWindow->Active = false;
2417 ImGui::End();
2418
2419 // Click to focus window and start moving (after we're done with all our widgets)
2420 if (!g.ActiveId)
2421 g.MovedWindow = NULL;
2422 if (g.ActiveId == 0 && g.HoveredId == 0 && g.IO.MouseClicked[0])
2423 {
2424 if (!(g.FocusedWindow && !g.FocusedWindow->WasActive && g.FocusedWindow->Active)) // Unless we just made a popup appear
2425 {
2426 if (g.HoveredRootWindow != NULL)
2427 {
2428 FocusWindow(g.HoveredWindow);
2429 if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove))
2430 {
2431 g.MovedWindow = g.HoveredWindow;
2432 SetActiveID(g.HoveredRootWindow->MoveID, g.HoveredRootWindow);
2433 }
2434 }
2435 else if (g.FocusedWindow != NULL && GetFrontMostModalRootWindow() == NULL)
2436 {
2437 // Clicking on void disable focus
2438 FocusWindow(NULL);
2439 }
2440 }
2441 }
2442
2443 // Sort the window list so that all child windows are after their parent
2444 // We cannot do that on FocusWindow() because childs may not exist yet
2445 g.WindowsSortBuffer.resize(0);
2446 g.WindowsSortBuffer.reserve(g.Windows.Size);
2447 for (int i = 0; i != g.Windows.Size; i++)
2448 {
2449 ImGuiWindow* window = g.Windows[i];
2450 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
2451 continue;
2452 AddWindowToSortedBuffer(g.WindowsSortBuffer, window);
2453 }
2454 IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong
2455 g.Windows.swap(g.WindowsSortBuffer);
2456
2457 // Clear Input data for next frame
2458 g.IO.MouseWheel = 0.0f;
2459 memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
2460
2461 g.FrameCountEnded = g.FrameCount;
2462 }
2463
Render()2464 void ImGui::Render()
2465 {
2466 ImGuiState& g = *GImGui;
2467 IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame()
2468
2469 if (g.FrameCountEnded != g.FrameCount)
2470 ImGui::EndFrame();
2471 g.FrameCountRendered = g.FrameCount;
2472
2473 // Skip render altogether if alpha is 0.0
2474 // Note that vertex buffers have been created and are wasted, so it is best practice that you don't create windows in the first place, or consistently respond to Begin() returning false.
2475 if (g.Style.Alpha > 0.0f)
2476 {
2477 // Gather windows to render
2478 g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0;
2479 for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2480 g.RenderDrawLists[i].resize(0);
2481 for (int i = 0; i != g.Windows.Size; i++)
2482 {
2483 ImGuiWindow* window = g.Windows[i];
2484 if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0)
2485 {
2486 // FIXME: Generalize this with a proper layering system so e.g. user can draw in specific layers, below text, ..
2487 g.IO.MetricsActiveWindows++;
2488 if (window->Flags & ImGuiWindowFlags_Popup)
2489 AddWindowToRenderList(g.RenderDrawLists[1], window);
2490 else if (window->Flags & ImGuiWindowFlags_Tooltip)
2491 AddWindowToRenderList(g.RenderDrawLists[2], window);
2492 else
2493 AddWindowToRenderList(g.RenderDrawLists[0], window);
2494 }
2495 }
2496
2497 // Flatten layers
2498 int n = g.RenderDrawLists[0].Size;
2499 int flattened_size = n;
2500 for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2501 flattened_size += g.RenderDrawLists[i].Size;
2502 g.RenderDrawLists[0].resize(flattened_size);
2503 for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++)
2504 {
2505 ImVector<ImDrawList*>& layer = g.RenderDrawLists[i];
2506 if (layer.empty())
2507 continue;
2508 memcpy(&g.RenderDrawLists[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
2509 n += layer.Size;
2510 }
2511
2512 // Draw software mouse cursor if requested
2513 if (g.IO.MouseDrawCursor)
2514 {
2515 const ImGuiMouseCursorData& cursor_data = g.MouseCursorData[g.MouseCursor];
2516 const ImVec2 pos = g.IO.MousePos - cursor_data.HotOffset;
2517 const ImVec2 size = cursor_data.Size;
2518 const ImTextureID tex_id = g.IO.Fonts->TexID;
2519 g.OverlayDrawList.PushTextureID(tex_id);
2520 g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(1,0), pos+ImVec2(1,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], 0x30000000); // Shadow
2521 g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(2,0), pos+ImVec2(2,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], 0x30000000); // Shadow
2522 g.OverlayDrawList.AddImage(tex_id, pos, pos + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], 0xFF000000); // Black border
2523 g.OverlayDrawList.AddImage(tex_id, pos, pos + size, cursor_data.TexUvMin[0], cursor_data.TexUvMax[0], 0xFFFFFFFF); // White fill
2524 g.OverlayDrawList.PopTextureID();
2525 }
2526 if (!g.OverlayDrawList.VtxBuffer.empty())
2527 AddDrawListToRenderList(g.RenderDrawLists[0], &g.OverlayDrawList);
2528
2529 // Setup draw data
2530 g.RenderDrawData.Valid = true;
2531 g.RenderDrawData.CmdLists = (g.RenderDrawLists[0].Size > 0) ? &g.RenderDrawLists[0][0] : NULL;
2532 g.RenderDrawData.CmdListsCount = g.RenderDrawLists[0].Size;
2533 g.RenderDrawData.TotalVtxCount = g.IO.MetricsRenderVertices;
2534 g.RenderDrawData.TotalIdxCount = g.IO.MetricsRenderIndices;
2535
2536 // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData()
2537 if (g.RenderDrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
2538 g.IO.RenderDrawListsFn(&g.RenderDrawData);
2539 }
2540 }
2541
FindRenderedTextEnd(const char * text,const char * text_end)2542 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2543 {
2544 const char* text_display_end = text;
2545 if (!text_end)
2546 text_end = (const char*)-1;
2547
2548 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2549 text_display_end++;
2550 return text_display_end;
2551 }
2552
2553 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)2554 void ImGui::LogText(const char* fmt, ...)
2555 {
2556 ImGuiState& g = *GImGui;
2557 if (!g.LogEnabled)
2558 return;
2559
2560 va_list args;
2561 va_start(args, fmt);
2562 if (g.LogFile)
2563 {
2564 vfprintf(g.LogFile, fmt, args);
2565 }
2566 else
2567 {
2568 g.LogClipboard->appendv(fmt, args);
2569 }
2570 va_end(args);
2571 }
2572
2573 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
2574 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 & ref_pos,const char * text,const char * text_end)2575 static void LogRenderedText(const ImVec2& ref_pos, const char* text, const char* text_end)
2576 {
2577 ImGuiState& g = *GImGui;
2578 ImGuiWindow* window = ImGui::GetCurrentWindowRead();
2579
2580 if (!text_end)
2581 text_end = ImGui::FindRenderedTextEnd(text, text_end);
2582
2583 const bool log_new_line = ref_pos.y > window->DC.LogLinePosY+1;
2584 window->DC.LogLinePosY = ref_pos.y;
2585
2586 const char* text_remaining = text;
2587 if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth
2588 g.LogStartDepth = window->DC.TreeDepth;
2589 const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth);
2590 for (;;)
2591 {
2592 // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
2593 const char* line_end = text_remaining;
2594 while (line_end < text_end)
2595 if (*line_end == '\n')
2596 break;
2597 else
2598 line_end++;
2599 if (line_end >= text_end)
2600 line_end = NULL;
2601
2602 const bool is_first_line = (text == text_remaining);
2603 bool is_last_line = false;
2604 if (line_end == NULL)
2605 {
2606 is_last_line = true;
2607 line_end = text_end;
2608 }
2609 if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0))
2610 {
2611 const int char_count = (int)(line_end - text_remaining);
2612 if (log_new_line || !is_first_line)
2613 ImGui::LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining);
2614 else
2615 ImGui::LogText(" %.*s", char_count, text_remaining);
2616 }
2617
2618 if (is_last_line)
2619 break;
2620 text_remaining = line_end + 1;
2621 }
2622 }
2623
2624 // Internal ImGui functions to render text
2625 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2626 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2627 {
2628 ImGuiState& g = *GImGui;
2629 ImGuiWindow* window = GetCurrentWindow();
2630
2631 // Hide anything after a '##' string
2632 const char* text_display_end;
2633 if (hide_text_after_hash)
2634 {
2635 text_display_end = FindRenderedTextEnd(text, text_end);
2636 }
2637 else
2638 {
2639 if (!text_end)
2640 text_end = text + strlen(text); // FIXME-OPT
2641 text_display_end = text_end;
2642 }
2643
2644 const int text_len = (int)(text_display_end - text);
2645 if (text_len > 0)
2646 {
2647 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2648 if (g.LogEnabled)
2649 LogRenderedText(pos, text, text_display_end);
2650 }
2651 }
2652
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2653 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2654 {
2655 ImGuiState& g = *GImGui;
2656 ImGuiWindow* window = GetCurrentWindow();
2657
2658 if (!text_end)
2659 text_end = text + strlen(text); // FIXME-OPT
2660
2661 const int text_len = (int)(text_end - text);
2662 if (text_len > 0)
2663 {
2664 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2665 if (g.LogEnabled)
2666 LogRenderedText(pos, text, text_end);
2667 }
2668 }
2669
2670 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
RenderTextClipped(const ImVec2 & pos_min,const ImVec2 & pos_max,const char * text,const char * text_end,const ImVec2 * text_size_if_known,ImGuiAlign align,const ImVec2 * clip_min,const ImVec2 * clip_max)2671 void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, ImGuiAlign align, const ImVec2* clip_min, const ImVec2* clip_max)
2672 {
2673 // Hide anything after a '##' string
2674 const char* text_display_end = FindRenderedTextEnd(text, text_end);
2675 const int text_len = (int)(text_display_end - text);
2676 if (text_len == 0)
2677 return;
2678
2679 ImGuiState& g = *GImGui;
2680 ImGuiWindow* window = GetCurrentWindow();
2681
2682 // Perform CPU side clipping for single clipped element to avoid using scissor state
2683 ImVec2 pos = pos_min;
2684 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : ImGui::CalcTextSize(text, text_display_end, false, 0.0f);
2685
2686 if (!clip_max) clip_max = &pos_max;
2687 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2688 if (!clip_min) clip_min = &pos_min; else need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2689
2690 // Align
2691 if (align & ImGuiAlign_Center) pos.x = ImMax(pos.x, (pos.x + pos_max.x - text_size.x) * 0.5f);
2692 else if (align & ImGuiAlign_Right) pos.x = ImMax(pos.x, pos_max.x - text_size.x);
2693 if (align & ImGuiAlign_VCenter) pos.y = ImMax(pos.y, (pos.y + pos_max.y - text_size.y) * 0.5f);
2694
2695 // Render
2696 if (need_clipping)
2697 {
2698 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2699 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2700 }
2701 else
2702 {
2703 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2704 }
2705 if (g.LogEnabled)
2706 LogRenderedText(pos, text, text_display_end);
2707 }
2708
2709 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2710 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2711 {
2712 ImGuiWindow* window = GetCurrentWindow();
2713
2714 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2715 if (border && (window->Flags & ImGuiWindowFlags_ShowBorders))
2716 {
2717 window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding);
2718 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding);
2719 }
2720 }
2721
2722 // Render a triangle to denote expanded/collapsed state
RenderCollapseTriangle(ImVec2 p_min,bool opened,float scale,bool shadow)2723 void ImGui::RenderCollapseTriangle(ImVec2 p_min, bool opened, float scale, bool shadow)
2724 {
2725 ImGuiState& g = *GImGui;
2726 ImGuiWindow* window = GetCurrentWindow();
2727
2728 const float h = g.FontSize * 1.00f;
2729 const float r = h * 0.40f * scale;
2730 ImVec2 center = p_min + ImVec2(h*0.50f, h*0.50f*scale);
2731
2732 ImVec2 a, b, c;
2733 if (opened)
2734 {
2735 center.y -= r*0.25f;
2736 a = center + ImVec2(0,1)*r;
2737 b = center + ImVec2(-0.866f,-0.5f)*r;
2738 c = center + ImVec2(0.866f,-0.5f)*r;
2739 }
2740 else
2741 {
2742 a = center + ImVec2(1,0)*r;
2743 b = center + ImVec2(-0.500f,0.866f)*r;
2744 c = center + ImVec2(-0.500f,-0.866f)*r;
2745 }
2746
2747 if (shadow && (window->Flags & ImGuiWindowFlags_ShowBorders) != 0)
2748 window->DrawList->AddTriangleFilled(a+ImVec2(2,2), b+ImVec2(2,2), c+ImVec2(2,2), GetColorU32(ImGuiCol_BorderShadow));
2749 window->DrawList->AddTriangleFilled(a, b, c, GetColorU32(ImGuiCol_Text));
2750 }
2751
RenderCheckMark(ImVec2 pos,ImU32 col)2752 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col)
2753 {
2754 ImGuiState& g = *GImGui;
2755 ImGuiWindow* window = GetCurrentWindow();
2756
2757 ImVec2 a, b, c;
2758 float start_x = (float)(int)(g.FontSize * 0.307f + 0.5f);
2759 float rem_third = (float)(int)((g.FontSize - start_x) / 3.0f);
2760 a.x = pos.x + 0.5f + start_x;
2761 b.x = a.x + rem_third;
2762 c.x = a.x + rem_third * 3.0f;
2763 b.y = pos.y - 1.0f + (float)(int)(g.Font->Ascent * (g.FontSize / g.Font->FontSize) + 0.5f) + (float)(int)(g.Font->DisplayOffset.y);
2764 a.y = b.y - rem_third;
2765 c.y = b.y - rem_third * 2.0f;
2766
2767 window->DrawList->PathLineTo(a);
2768 window->DrawList->PathLineTo(b);
2769 window->DrawList->PathLineTo(c);
2770 window->DrawList->PathStroke(col, false);
2771 }
2772
2773 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
2774 // CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize)
CalcTextSize(const char * text,const char * text_end,bool hide_text_after_double_hash,float wrap_width)2775 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
2776 {
2777 ImGuiState& g = *GImGui;
2778
2779 const char* text_display_end;
2780 if (hide_text_after_double_hash)
2781 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
2782 else
2783 text_display_end = text_end;
2784
2785 ImFont* font = g.Font;
2786 const float font_size = g.FontSize;
2787 if (text == text_display_end)
2788 return ImVec2(0.0f, font_size);
2789 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
2790
2791 // Cancel out character spacing for the last character of a line (it is baked into glyph->XAdvance field)
2792 const float font_scale = font_size / font->FontSize;
2793 const float character_spacing_x = 1.0f * font_scale;
2794 if (text_size.x > 0.0f)
2795 text_size.x -= character_spacing_x;
2796 text_size.x = (float)(int)(text_size.x + 0.95f);
2797
2798 return text_size;
2799 }
2800
2801 // Helper to calculate coarse clipping of large list of evenly sized items.
2802 // NB: Prefer using the ImGuiListClipper higher-level helper if you can!
2803 // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX
2804 // If you are displaying thousands of items and you have a random access to the list, you can perform clipping yourself to save on CPU.
2805 // {
2806 // float item_height = ImGui::GetTextLineHeightWithSpacing();
2807 // int display_start, display_end;
2808 // ImGui::CalcListClipping(count, item_height, &display_start, &display_end); // calculate how many to clip/display
2809 // ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (display_start) * item_height); // advance cursor
2810 // for (int i = display_start; i < display_end; i++) // display only visible items
2811 // // TODO: display visible item
2812 // ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (count - display_end) * item_height); // advance cursor
2813 // }
CalcListClipping(int items_count,float items_height,int * out_items_display_start,int * out_items_display_end)2814 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
2815 {
2816 ImGuiState& g = *GImGui;
2817 ImGuiWindow* window = GetCurrentWindowRead();
2818 if (g.LogEnabled)
2819 {
2820 // If logging is active, do not perform any clipping
2821 *out_items_display_start = 0;
2822 *out_items_display_end = items_count;
2823 return;
2824 }
2825
2826 const ImVec2 pos = window->DC.CursorPos;
2827 int start = (int)((window->ClipRect.Min.y - pos.y) / items_height);
2828 int end = (int)((window->ClipRect.Max.y - pos.y) / items_height);
2829 start = ImClamp(start, 0, items_count);
2830 end = ImClamp(end + 1, start, items_count);
2831 *out_items_display_start = start;
2832 *out_items_display_end = end;
2833 }
2834
2835 // Find window given position, search front-to-back
FindHoveredWindow(ImVec2 pos,bool excluding_childs)2836 static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs)
2837 {
2838 ImGuiState& g = *GImGui;
2839 for (int i = g.Windows.Size-1; i >= 0; i--)
2840 {
2841 ImGuiWindow* window = g.Windows[i];
2842 if (!window->Active)
2843 continue;
2844 if (window->Flags & ImGuiWindowFlags_NoInputs)
2845 continue;
2846 if (excluding_childs && (window->Flags & ImGuiWindowFlags_ChildWindow) != 0)
2847 continue;
2848
2849 // Using the clipped AABB so a child window will typically be clipped by its parent.
2850 ImRect bb(window->ClippedWindowRect.Min - g.Style.TouchExtraPadding, window->ClippedWindowRect.Max + g.Style.TouchExtraPadding);
2851 if (bb.Contains(pos))
2852 return window;
2853 }
2854 return NULL;
2855 }
2856
2857 // Test if mouse cursor is hovering given rectangle
2858 // NB- Rectangle is clipped by our current clip setting
2859 // NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
IsMouseHoveringRect(const ImVec2 & r_min,const ImVec2 & r_max,bool clip)2860 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
2861 {
2862 ImGuiState& g = *GImGui;
2863 ImGuiWindow* window = GetCurrentWindowRead();
2864
2865 // Clip
2866 ImRect rect_clipped(r_min, r_max);
2867 if (clip)
2868 rect_clipped.Clip(window->ClipRect);
2869
2870 // Expand for touch input
2871 const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
2872 return rect_for_touch.Contains(g.IO.MousePos);
2873 }
2874
IsMouseHoveringWindow()2875 bool ImGui::IsMouseHoveringWindow()
2876 {
2877 ImGuiState& g = *GImGui;
2878 return g.HoveredWindow == g.CurrentWindow;
2879 }
2880
IsMouseHoveringAnyWindow()2881 bool ImGui::IsMouseHoveringAnyWindow()
2882 {
2883 ImGuiState& g = *GImGui;
2884 return g.HoveredWindow != NULL;
2885 }
2886
IsPosHoveringAnyWindow(const ImVec2 & pos)2887 bool ImGui::IsPosHoveringAnyWindow(const ImVec2& pos)
2888 {
2889 return FindHoveredWindow(pos, false) != NULL;
2890 }
2891
IsKeyPressedMap(ImGuiKey key,bool repeat)2892 static bool IsKeyPressedMap(ImGuiKey key, bool repeat)
2893 {
2894 const int key_index = GImGui->IO.KeyMap[key];
2895 return ImGui::IsKeyPressed(key_index, repeat);
2896 }
2897
GetKeyIndex(ImGuiKey key)2898 int ImGui::GetKeyIndex(ImGuiKey key)
2899 {
2900 IM_ASSERT(key >= 0 && key < ImGuiKey_COUNT);
2901 return GImGui->IO.KeyMap[key];
2902 }
2903
IsKeyDown(int key_index)2904 bool ImGui::IsKeyDown(int key_index)
2905 {
2906 if (key_index < 0) return false;
2907 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown));
2908 return GImGui->IO.KeysDown[key_index];
2909 }
2910
IsKeyPressed(int key_index,bool repeat)2911 bool ImGui::IsKeyPressed(int key_index, bool repeat)
2912 {
2913 ImGuiState& g = *GImGui;
2914 if (key_index < 0) return false;
2915 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
2916 const float t = g.IO.KeysDownDuration[key_index];
2917 if (t == 0.0f)
2918 return true;
2919
2920 if (repeat && t > g.IO.KeyRepeatDelay)
2921 {
2922 float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
2923 if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
2924 return true;
2925 }
2926 return false;
2927 }
2928
IsKeyReleased(int key_index)2929 bool ImGui::IsKeyReleased(int key_index)
2930 {
2931 ImGuiState& g = *GImGui;
2932 if (key_index < 0) return false;
2933 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
2934 if (g.IO.KeysDownDurationPrev[key_index] >= 0.0f && !g.IO.KeysDown[key_index])
2935 return true;
2936 return false;
2937 }
2938
IsMouseDown(int button)2939 bool ImGui::IsMouseDown(int button)
2940 {
2941 ImGuiState& g = *GImGui;
2942 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
2943 return g.IO.MouseDown[button];
2944 }
2945
IsMouseClicked(int button,bool repeat)2946 bool ImGui::IsMouseClicked(int button, bool repeat)
2947 {
2948 ImGuiState& g = *GImGui;
2949 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
2950 const float t = g.IO.MouseDownDuration[button];
2951 if (t == 0.0f)
2952 return true;
2953
2954 if (repeat && t > g.IO.KeyRepeatDelay)
2955 {
2956 float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
2957 if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
2958 return true;
2959 }
2960
2961 return false;
2962 }
2963
IsMouseReleased(int button)2964 bool ImGui::IsMouseReleased(int button)
2965 {
2966 ImGuiState& g = *GImGui;
2967 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
2968 return g.IO.MouseReleased[button];
2969 }
2970
IsMouseDoubleClicked(int button)2971 bool ImGui::IsMouseDoubleClicked(int button)
2972 {
2973 ImGuiState& g = *GImGui;
2974 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
2975 return g.IO.MouseDoubleClicked[button];
2976 }
2977
IsMouseDragging(int button,float lock_threshold)2978 bool ImGui::IsMouseDragging(int button, float lock_threshold)
2979 {
2980 ImGuiState& g = *GImGui;
2981 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
2982 if (!g.IO.MouseDown[button])
2983 return false;
2984 if (lock_threshold < 0.0f)
2985 lock_threshold = g.IO.MouseDragThreshold;
2986 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
2987 }
2988
GetMousePos()2989 ImVec2 ImGui::GetMousePos()
2990 {
2991 return GImGui->IO.MousePos;
2992 }
2993
2994 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()2995 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
2996 {
2997 ImGuiState& g = *GImGui;
2998 if (g.CurrentPopupStack.Size > 0)
2999 return g.OpenedPopupStack[g.CurrentPopupStack.Size-1].MousePosOnOpen;
3000 return g.IO.MousePos;
3001 }
3002
GetMouseDragDelta(int button,float lock_threshold)3003 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
3004 {
3005 ImGuiState& g = *GImGui;
3006 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3007 if (lock_threshold < 0.0f)
3008 lock_threshold = g.IO.MouseDragThreshold;
3009 if (g.IO.MouseDown[button])
3010 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
3011 return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment).
3012 return ImVec2(0.0f, 0.0f);
3013 }
3014
ResetMouseDragDelta(int button)3015 void ImGui::ResetMouseDragDelta(int button)
3016 {
3017 ImGuiState& g = *GImGui;
3018 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3019 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
3020 g.IO.MouseClickedPos[button] = g.IO.MousePos;
3021 }
3022
GetMouseCursor()3023 ImGuiMouseCursor ImGui::GetMouseCursor()
3024 {
3025 return GImGui->MouseCursor;
3026 }
3027
SetMouseCursor(ImGuiMouseCursor cursor_type)3028 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
3029 {
3030 GImGui->MouseCursor = cursor_type;
3031 }
3032
CaptureKeyboardFromApp(bool capture)3033 void ImGui::CaptureKeyboardFromApp(bool capture)
3034 {
3035 GImGui->CaptureKeyboardNextFrame = capture ? 1 : 0;
3036 }
3037
CaptureMouseFromApp(bool capture)3038 void ImGui::CaptureMouseFromApp(bool capture)
3039 {
3040 GImGui->CaptureMouseNextFrame = capture ? 1 : 0;
3041 }
3042
IsItemHovered()3043 bool ImGui::IsItemHovered()
3044 {
3045 ImGuiWindow* window = GetCurrentWindowRead();
3046 return window->DC.LastItemHoveredAndUsable;
3047 }
3048
IsItemHoveredRect()3049 bool ImGui::IsItemHoveredRect()
3050 {
3051 ImGuiWindow* window = GetCurrentWindowRead();
3052 return window->DC.LastItemHoveredRect;
3053 }
3054
IsItemActive()3055 bool ImGui::IsItemActive()
3056 {
3057 ImGuiState& g = *GImGui;
3058 if (g.ActiveId)
3059 {
3060 ImGuiWindow* window = GetCurrentWindowRead();
3061 return g.ActiveId == window->DC.LastItemID;
3062 }
3063 return false;
3064 }
3065
IsAnyItemHovered()3066 bool ImGui::IsAnyItemHovered()
3067 {
3068 return GImGui->HoveredId != 0 || GImGui->HoveredIdPreviousFrame != 0;
3069 }
3070
IsAnyItemActive()3071 bool ImGui::IsAnyItemActive()
3072 {
3073 return GImGui->ActiveId != 0;
3074 }
3075
IsItemVisible()3076 bool ImGui::IsItemVisible()
3077 {
3078 ImGuiWindow* window = GetCurrentWindowRead();
3079 ImRect r(window->ClipRect);
3080 return r.Overlaps(window->DC.LastItemRect);
3081 }
3082
3083 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
SetItemAllowOverlap()3084 void ImGui::SetItemAllowOverlap()
3085 {
3086 ImGuiState& g = *GImGui;
3087 if (g.HoveredId == g.CurrentWindow->DC.LastItemID)
3088 g.HoveredIdAllowOverlap = true;
3089 if (g.ActiveId == g.CurrentWindow->DC.LastItemID)
3090 g.ActiveIdAllowOverlap = true;
3091 }
3092
GetItemRectMin()3093 ImVec2 ImGui::GetItemRectMin()
3094 {
3095 ImGuiWindow* window = GetCurrentWindowRead();
3096 return window->DC.LastItemRect.Min;
3097 }
3098
GetItemRectMax()3099 ImVec2 ImGui::GetItemRectMax()
3100 {
3101 ImGuiWindow* window = GetCurrentWindowRead();
3102 return window->DC.LastItemRect.Max;
3103 }
3104
GetItemRectSize()3105 ImVec2 ImGui::GetItemRectSize()
3106 {
3107 ImGuiWindow* window = GetCurrentWindowRead();
3108 return window->DC.LastItemRect.GetSize();
3109 }
3110
CalcItemRectClosestPoint(const ImVec2 & pos,bool on_edge,float outward)3111 ImVec2 ImGui::CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge, float outward)
3112 {
3113 ImGuiWindow* window = GetCurrentWindowRead();
3114 ImRect rect = window->DC.LastItemRect;
3115 rect.Expand(outward);
3116 return rect.GetClosestPoint(pos, on_edge);
3117 }
3118
3119 // Tooltip is stored and turned into a BeginTooltip()/EndTooltip() sequence at the end of the frame. Each call override previous value.
SetTooltipV(const char * fmt,va_list args)3120 void ImGui::SetTooltipV(const char* fmt, va_list args)
3121 {
3122 ImGuiState& g = *GImGui;
3123 ImFormatStringV(g.Tooltip, IM_ARRAYSIZE(g.Tooltip), fmt, args);
3124 }
3125
SetTooltip(const char * fmt,...)3126 void ImGui::SetTooltip(const char* fmt, ...)
3127 {
3128 va_list args;
3129 va_start(args, fmt);
3130 SetTooltipV(fmt, args);
3131 va_end(args);
3132 }
3133
GetVisibleRect()3134 static ImRect GetVisibleRect()
3135 {
3136 ImGuiState& g = *GImGui;
3137 if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y)
3138 return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax);
3139 return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3140 }
3141
BeginTooltip()3142 void ImGui::BeginTooltip()
3143 {
3144 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
3145 ImGui::Begin("##Tooltip", NULL, flags);
3146 }
3147
EndTooltip()3148 void ImGui::EndTooltip()
3149 {
3150 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
3151 ImGui::End();
3152 }
3153
IsPopupOpen(ImGuiID id)3154 static bool IsPopupOpen(ImGuiID id)
3155 {
3156 ImGuiState& g = *GImGui;
3157 const bool opened = g.OpenedPopupStack.Size > g.CurrentPopupStack.Size && g.OpenedPopupStack[g.CurrentPopupStack.Size].PopupID == id;
3158 return opened;
3159 }
3160
3161 // Mark popup as open (toggle toward open state).
3162 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
3163 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
3164 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(const char * str_id,bool reopen_existing)3165 void ImGui::OpenPopupEx(const char* str_id, bool reopen_existing)
3166 {
3167 ImGuiState& g = *GImGui;
3168 ImGuiWindow* window = g.CurrentWindow;
3169 ImGuiID id = window->GetID(str_id);
3170 int current_stack_size = g.CurrentPopupStack.Size;
3171 ImGuiPopupRef popup_ref = ImGuiPopupRef(id, window, window->GetID("##menus"), g.IO.MousePos); // Tagged as new ref because constructor sets Window to NULL (we are passing the ParentWindow info here)
3172 if (g.OpenedPopupStack.Size < current_stack_size + 1)
3173 g.OpenedPopupStack.push_back(popup_ref);
3174 else if (reopen_existing || g.OpenedPopupStack[current_stack_size].PopupID != id)
3175 {
3176 g.OpenedPopupStack.resize(current_stack_size+1);
3177 g.OpenedPopupStack[current_stack_size] = popup_ref;
3178 }
3179 }
3180
OpenPopup(const char * str_id)3181 void ImGui::OpenPopup(const char* str_id)
3182 {
3183 ImGui::OpenPopupEx(str_id, false);
3184 }
3185
CloseInactivePopups()3186 static void CloseInactivePopups()
3187 {
3188 ImGuiState& g = *GImGui;
3189 if (g.OpenedPopupStack.empty())
3190 return;
3191
3192 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
3193 // Don't close our own child popup windows
3194 int n = 0;
3195 if (g.FocusedWindow)
3196 {
3197 for (n = 0; n < g.OpenedPopupStack.Size; n++)
3198 {
3199 ImGuiPopupRef& popup = g.OpenedPopupStack[n];
3200 if (!popup.Window)
3201 continue;
3202 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
3203 if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
3204 continue;
3205
3206 bool has_focus = false;
3207 for (int m = n; m < g.OpenedPopupStack.Size && !has_focus; m++)
3208 has_focus = (g.OpenedPopupStack[m].Window && g.OpenedPopupStack[m].Window->RootWindow == g.FocusedWindow->RootWindow);
3209 if (!has_focus)
3210 break;
3211 }
3212 }
3213 if (n < g.OpenedPopupStack.Size) // This test is not required but it allows to set a useful breakpoint on the line below
3214 g.OpenedPopupStack.resize(n);
3215 }
3216
GetFrontMostModalRootWindow()3217 static ImGuiWindow* GetFrontMostModalRootWindow()
3218 {
3219 ImGuiState& g = *GImGui;
3220 if (!g.OpenedPopupStack.empty())
3221 if (ImGuiWindow* front_most_popup = g.OpenedPopupStack.back().Window)
3222 if (front_most_popup->Flags & ImGuiWindowFlags_Modal)
3223 return front_most_popup;
3224 return NULL;
3225 }
3226
ClosePopupToLevel(int remaining)3227 static void ClosePopupToLevel(int remaining)
3228 {
3229 ImGuiState& g = *GImGui;
3230 if (remaining > 0)
3231 ImGui::FocusWindow(g.OpenedPopupStack[remaining-1].Window);
3232 else
3233 ImGui::FocusWindow(g.OpenedPopupStack[0].ParentWindow);
3234 g.OpenedPopupStack.resize(remaining);
3235 }
3236
ClosePopup(ImGuiID id)3237 static void ClosePopup(ImGuiID id)
3238 {
3239 if (!IsPopupOpen(id))
3240 return;
3241 ImGuiState& g = *GImGui;
3242 ClosePopupToLevel(g.OpenedPopupStack.Size - 1);
3243 }
3244
3245 // Close the popup we have begin-ed into.
CloseCurrentPopup()3246 void ImGui::CloseCurrentPopup()
3247 {
3248 ImGuiState& g = *GImGui;
3249 int popup_idx = g.CurrentPopupStack.Size - 1;
3250 if (popup_idx < 0 || popup_idx > g.OpenedPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupID != g.OpenedPopupStack[popup_idx].PopupID)
3251 return;
3252 while (popup_idx > 0 && g.OpenedPopupStack[popup_idx].Window && (g.OpenedPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu))
3253 popup_idx--;
3254 ClosePopupToLevel(popup_idx);
3255 }
3256
ClearSetNextWindowData()3257 static inline void ClearSetNextWindowData()
3258 {
3259 ImGuiState& g = *GImGui;
3260 g.SetNextWindowPosCond = g.SetNextWindowSizeCond = g.SetNextWindowContentSizeCond = g.SetNextWindowCollapsedCond = 0;
3261 g.SetNextWindowFocus = false;
3262 }
3263
BeginPopupEx(const char * str_id,ImGuiWindowFlags extra_flags)3264 static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags)
3265 {
3266 ImGuiState& g = *GImGui;
3267 ImGuiWindow* window = g.CurrentWindow;
3268 const ImGuiID id = window->GetID(str_id);
3269 if (!IsPopupOpen(id))
3270 {
3271 ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3272 return false;
3273 }
3274
3275 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
3276 ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
3277
3278 char name[32];
3279 if (flags & ImGuiWindowFlags_ChildMenu)
3280 ImFormatString(name, 20, "##menu_%d", g.CurrentPopupStack.Size); // Recycle windows based on depth
3281 else
3282 ImFormatString(name, 20, "##popup_%08x", id); // Not recycling, so we can close/open during the same frame
3283
3284 bool opened = ImGui::Begin(name, NULL, flags);
3285 if (!(window->Flags & ImGuiWindowFlags_ShowBorders))
3286 g.CurrentWindow->Flags &= ~ImGuiWindowFlags_ShowBorders;
3287 if (!opened) // opened can be 'false' when the popup is completely clipped (e.g. zero size display)
3288 ImGui::EndPopup();
3289
3290 return opened;
3291 }
3292
BeginPopup(const char * str_id)3293 bool ImGui::BeginPopup(const char* str_id)
3294 {
3295 if (GImGui->OpenedPopupStack.Size <= GImGui->CurrentPopupStack.Size) // Early out for performance
3296 {
3297 ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3298 return false;
3299 }
3300 return BeginPopupEx(str_id, ImGuiWindowFlags_ShowBorders);
3301 }
3302
BeginPopupModal(const char * name,bool * p_opened,ImGuiWindowFlags extra_flags)3303 bool ImGui::BeginPopupModal(const char* name, bool* p_opened, ImGuiWindowFlags extra_flags)
3304 {
3305 ImGuiState& g = *GImGui;
3306 ImGuiWindow* window = g.CurrentWindow;
3307 const ImGuiID id = window->GetID(name);
3308 if (!IsPopupOpen(id))
3309 {
3310 ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
3311 return false;
3312 }
3313
3314 ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoSavedSettings;
3315 bool opened = ImGui::Begin(name, p_opened, flags);
3316 if (!opened || (p_opened && !*p_opened)) // Opened can be 'false' when the popup is completely clipped (e.g. zero size display)
3317 {
3318 ImGui::EndPopup();
3319 if (opened)
3320 ClosePopup(id);
3321 return false;
3322 }
3323
3324 return opened;
3325 }
3326
EndPopup()3327 void ImGui::EndPopup()
3328 {
3329 ImGuiWindow* window = GetCurrentWindow();
3330 IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
3331 IM_ASSERT(GImGui->CurrentPopupStack.Size > 0);
3332 ImGui::End();
3333 if (!(window->Flags & ImGuiWindowFlags_Modal))
3334 ImGui::PopStyleVar();
3335 }
3336
3337 // This is a helper to handle the most simple case of associating one named popup to one given widget.
3338 // 1. If you have many possible popups (for different "instances" of a same widget, or for wholly different widgets), you may be better off handling
3339 // this yourself so you can store data relative to the widget that opened the popup instead of choosing different popup identifiers.
3340 // 2. If you want right-clicking on the same item to reopen the popup at new location, use the same code replacing IsItemHovered() with IsItemHoveredRect()
3341 // and passing true to the OpenPopupEx().
3342 // Because: hovering an item in a window below the popup won't normally trigger is hovering behavior/coloring. The pattern of ignoring the fact that
3343 // the item isn't interactable (because it is blocked by the active popup) may useful in some situation when e.g. large canvas as one item, content of menu
3344 // driven by click position.
BeginPopupContextItem(const char * str_id,int mouse_button)3345 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
3346 {
3347 if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(mouse_button))
3348 ImGui::OpenPopupEx(str_id, false);
3349 return ImGui::BeginPopup(str_id);
3350 }
3351
BeginPopupContextWindow(bool also_over_items,const char * str_id,int mouse_button)3352 bool ImGui::BeginPopupContextWindow(bool also_over_items, const char* str_id, int mouse_button)
3353 {
3354 if (!str_id) str_id = "window_context_menu";
3355 if (ImGui::IsMouseHoveringWindow() && ImGui::IsMouseClicked(mouse_button))
3356 if (also_over_items || !ImGui::IsAnyItemHovered())
3357 ImGui::OpenPopupEx(str_id, true);
3358 return ImGui::BeginPopup(str_id);
3359 }
3360
BeginPopupContextVoid(const char * str_id,int mouse_button)3361 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
3362 {
3363 if (!str_id) str_id = "void_context_menu";
3364 if (!ImGui::IsMouseHoveringAnyWindow() && ImGui::IsMouseClicked(mouse_button))
3365 ImGui::OpenPopupEx(str_id, true);
3366 return ImGui::BeginPopup(str_id);
3367 }
3368
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)3369 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
3370 {
3371 ImGuiWindow* window = GetCurrentWindow();
3372 ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
3373
3374 const ImVec2 content_avail = ImGui::GetContentRegionAvail();
3375 ImVec2 size = ImRound(size_arg);
3376 if (size.x <= 0.0f)
3377 {
3378 if (size.x == 0.0f)
3379 flags |= ImGuiWindowFlags_ChildWindowAutoFitX;
3380 size.x = ImMax(content_avail.x, 4.0f) - fabsf(size.x); // Arbitrary minimum zero-ish child size of 4.0f (0.0f causing too much issues)
3381 }
3382 if (size.y <= 0.0f)
3383 {
3384 if (size.y == 0.0f)
3385 flags |= ImGuiWindowFlags_ChildWindowAutoFitY;
3386 size.y = ImMax(content_avail.y, 4.0f) - fabsf(size.y);
3387 }
3388 if (border)
3389 flags |= ImGuiWindowFlags_ShowBorders;
3390 flags |= extra_flags;
3391
3392 char title[256];
3393 ImFormatString(title, IM_ARRAYSIZE(title), "%s.%s", window->Name, str_id);
3394
3395 bool ret = ImGui::Begin(title, NULL, size, -1.0f, flags);
3396
3397 if (!(window->Flags & ImGuiWindowFlags_ShowBorders))
3398 GetCurrentWindow()->Flags &= ~ImGuiWindowFlags_ShowBorders;
3399
3400 return ret;
3401 }
3402
BeginChild(ImGuiID id,const ImVec2 & size,bool border,ImGuiWindowFlags extra_flags)3403 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size, bool border, ImGuiWindowFlags extra_flags)
3404 {
3405 char str_id[32];
3406 ImFormatString(str_id, IM_ARRAYSIZE(str_id), "child_%08x", id);
3407 bool ret = ImGui::BeginChild(str_id, size, border, extra_flags);
3408 return ret;
3409 }
3410
EndChild()3411 void ImGui::EndChild()
3412 {
3413 ImGuiWindow* window = GetCurrentWindow();
3414
3415 IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() callss
3416 if ((window->Flags & ImGuiWindowFlags_ComboBox) || window->BeginCount > 1)
3417 {
3418 ImGui::End();
3419 }
3420 else
3421 {
3422 // When using auto-filling child window, we don't provide full width/height to ItemSize so that it doesn't feed back into automatic size-fitting.
3423 ImVec2 sz = ImGui::GetWindowSize();
3424 if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitX) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
3425 sz.x = ImMax(4.0f, sz.x);
3426 if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitY)
3427 sz.y = ImMax(4.0f, sz.y);
3428
3429 ImGui::End();
3430
3431 window = GetCurrentWindow();
3432 ImRect bb(window->DC.CursorPos, window->DC.CursorPos + sz);
3433 ItemSize(sz);
3434 ItemAdd(bb, NULL);
3435 }
3436 }
3437
3438 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)3439 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
3440 {
3441 ImGuiState& g = *GImGui;
3442 const ImGuiStyle& style = g.Style;
3443 ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, style.Colors[ImGuiCol_FrameBg]);
3444 ImGui::PushStyleVar(ImGuiStyleVar_ChildWindowRounding, style.FrameRounding);
3445 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
3446 return ImGui::BeginChild(id, size, (g.CurrentWindow->Flags & ImGuiWindowFlags_ShowBorders) ? true : false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
3447 }
3448
EndChildFrame()3449 void ImGui::EndChildFrame()
3450 {
3451 ImGui::EndChild();
3452 ImGui::PopStyleVar(2);
3453 ImGui::PopStyleColor();
3454 }
3455
3456 // Save and compare stack sizes on Begin()/End() to detect usage errors
CheckStacksSize(ImGuiWindow * window,bool write)3457 static void CheckStacksSize(ImGuiWindow* window, bool write)
3458 {
3459 // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
3460 ImGuiState& g = *GImGui;
3461 int* p_backup = &window->DC.StackSizesBackup[0];
3462 { int current = window->IDStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; } // User forgot PopID()
3463 { int current = window->DC.GroupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; } // User forgot EndGroup()
3464 { int current = g.CurrentPopupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; } // User forgot EndPopup()/EndMenu()
3465 { int current = g.ColorModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; } // User forgot PopStyleColor()
3466 { int current = g.StyleModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; } // User forgot PopStyleVar()
3467 { int current = g.FontStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current); p_backup++; } // User forgot PopFont()
3468 IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
3469 }
3470
FindBestPopupWindowPos(const ImVec2 & base_pos,const ImVec2 & size,int * last_dir,const ImRect & r_inner)3471 static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& r_inner)
3472 {
3473 const ImGuiStyle& style = GImGui->Style;
3474
3475 // Clamp into visible area while not overlapping the cursor. Safety padding is optional if our popup size won't fit without it.
3476 ImVec2 safe_padding = style.DisplaySafeAreaPadding;
3477 ImRect r_outer(GetVisibleRect());
3478 r_outer.Reduce(ImVec2((size.x - r_outer.GetWidth() > safe_padding.x*2) ? safe_padding.x : 0.0f, (size.y - r_outer.GetHeight() > safe_padding.y*2) ? safe_padding.y : 0.0f));
3479 ImVec2 base_pos_clamped = ImClamp(base_pos, r_outer.Min, r_outer.Max - size);
3480
3481 for (int n = (*last_dir != -1) ? -1 : 0; n < 4; n++) // Last, Right, down, up, left. (Favor last used direction).
3482 {
3483 const int dir = (n == -1) ? *last_dir : n;
3484 ImRect rect(dir == 0 ? r_inner.Max.x : r_outer.Min.x, dir == 1 ? r_inner.Max.y : r_outer.Min.y, dir == 3 ? r_inner.Min.x : r_outer.Max.x, dir == 2 ? r_inner.Min.y : r_outer.Max.y);
3485 if (rect.GetWidth() < size.x || rect.GetHeight() < size.y)
3486 continue;
3487 *last_dir = dir;
3488 return ImVec2(dir == 0 ? r_inner.Max.x : dir == 3 ? r_inner.Min.x - size.x : base_pos_clamped.x, dir == 1 ? r_inner.Max.y : dir == 2 ? r_inner.Min.y - size.y : base_pos_clamped.y);
3489 }
3490
3491 // Fallback, try to keep within display
3492 *last_dir = -1;
3493 ImVec2 pos = base_pos;
3494 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
3495 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
3496 return pos;
3497 }
3498
FindWindowByName(const char * name)3499 ImGuiWindow* ImGui::FindWindowByName(const char* name)
3500 {
3501 // FIXME-OPT: Store sorted hashes -> pointers so we can do a bissection in a contiguous block
3502 ImGuiState& g = *GImGui;
3503 ImGuiID id = ImHash(name, 0);
3504 for (int i = 0; i < g.Windows.Size; i++)
3505 if (g.Windows[i]->ID == id)
3506 return g.Windows[i];
3507 return NULL;
3508 }
3509
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)3510 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
3511 {
3512 ImGuiState& g = *GImGui;
3513
3514 // Create window the first time
3515 ImGuiWindow* window = (ImGuiWindow*)ImGui::MemAlloc(sizeof(ImGuiWindow));
3516 IM_PLACEMENT_NEW(window) ImGuiWindow(name);
3517 window->Flags = flags;
3518
3519 if (flags & ImGuiWindowFlags_NoSavedSettings)
3520 {
3521 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
3522 window->Size = window->SizeFull = size;
3523 }
3524 else
3525 {
3526 // Retrieve settings from .ini file
3527 // Use SetWindowPos() or SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
3528 window->PosFloat = ImVec2(60, 60);
3529 window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
3530
3531 ImGuiIniData* settings = FindWindowSettings(name);
3532 if (!settings)
3533 {
3534 settings = AddWindowSettings(name);
3535 }
3536 else
3537 {
3538 window->SetWindowPosAllowFlags &= ~ImGuiSetCond_FirstUseEver;
3539 window->SetWindowSizeAllowFlags &= ~ImGuiSetCond_FirstUseEver;
3540 window->SetWindowCollapsedAllowFlags &= ~ImGuiSetCond_FirstUseEver;
3541 }
3542
3543 if (settings->Pos.x != FLT_MAX)
3544 {
3545 window->PosFloat = settings->Pos;
3546 window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
3547 window->Collapsed = settings->Collapsed;
3548 }
3549
3550 if (ImLengthSqr(settings->Size) > 0.00001f && !(flags & ImGuiWindowFlags_NoResize))
3551 size = settings->Size;
3552 window->Size = window->SizeFull = size;
3553 }
3554
3555 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
3556 {
3557 window->AutoFitFramesX = window->AutoFitFramesY = 2;
3558 window->AutoFitOnlyGrows = false;
3559 }
3560 else
3561 {
3562 if (window->Size.x <= 0.0f)
3563 window->AutoFitFramesX = 2;
3564 if (window->Size.y <= 0.0f)
3565 window->AutoFitFramesY = 2;
3566 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
3567 }
3568
3569 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
3570 g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once
3571 else
3572 g.Windows.push_back(window);
3573 return window;
3574 }
3575
3576 // Push a new ImGui window to add widgets to.
3577 // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair.
3578 // - Begin/End can be called multiple times during the frame with the same window name to append content.
3579 // - 'size_on_first_use' for a regular window denote the initial size for first-time creation (no saved data) and isn't that useful. Use SetNextWindowSize() prior to calling Begin() for more flexible window manipulation.
3580 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
3581 // You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file.
3582 // - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned.
3583 // - Passing 'bool* p_opened' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed.
3584 // - Passing non-zero 'size' is roughly equivalent to calling SetNextWindowSize(size, ImGuiSetCond_FirstUseEver) prior to calling Begin().
Begin(const char * name,bool * p_opened,ImGuiWindowFlags flags)3585 bool ImGui::Begin(const char* name, bool* p_opened, ImGuiWindowFlags flags)
3586 {
3587 return ImGui::Begin(name, p_opened, ImVec2(0.f, 0.f), -1.0f, flags);
3588 }
3589
Begin(const char * name,bool * p_opened,const ImVec2 & size_on_first_use,float bg_alpha,ImGuiWindowFlags flags)3590 bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_use, float bg_alpha, ImGuiWindowFlags flags)
3591 {
3592 ImGuiState& g = *GImGui;
3593 const ImGuiStyle& style = g.Style;
3594 IM_ASSERT(name != NULL); // Window name required
3595 IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame()
3596 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
3597
3598 if (flags & ImGuiWindowFlags_NoInputs)
3599 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
3600
3601 // Find or create
3602 bool window_is_new = false;
3603 ImGuiWindow* window = FindWindowByName(name);
3604 if (!window)
3605 {
3606 window = CreateNewWindow(name, size_on_first_use, flags);
3607 window_is_new = true;
3608 }
3609
3610 const int current_frame = ImGui::GetFrameCount();
3611 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
3612 if (first_begin_of_the_frame)
3613 window->Flags = (ImGuiWindowFlags)flags;
3614 else
3615 flags = window->Flags;
3616
3617 // Add to stack
3618 ImGuiWindow* parent_window = !g.CurrentWindowStack.empty() ? g.CurrentWindowStack.back() : NULL;
3619 g.CurrentWindowStack.push_back(window);
3620 SetCurrentWindow(window);
3621 CheckStacksSize(window, true);
3622 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
3623
3624 bool window_was_active = (window->LastFrameActive == current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
3625 if (flags & ImGuiWindowFlags_Popup)
3626 {
3627 ImGuiPopupRef& popup_ref = g.OpenedPopupStack[g.CurrentPopupStack.Size];
3628 window_was_active &= (window->PopupID == popup_ref.PopupID);
3629 window_was_active &= (window == popup_ref.Window);
3630 popup_ref.Window = window;
3631 g.CurrentPopupStack.push_back(popup_ref);
3632 window->PopupID = popup_ref.PopupID;
3633 }
3634
3635 const bool window_appearing_after_being_hidden = (window->HiddenFrames == 1);
3636
3637 // Process SetNextWindow***() calls
3638 bool window_pos_set_by_api = false, window_size_set_by_api = false;
3639 if (g.SetNextWindowPosCond)
3640 {
3641 const ImVec2 backup_cursor_pos = window->DC.CursorPos; // FIXME: not sure of the exact reason of this anymore :( need to look into that.
3642 if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowPosAllowFlags |= ImGuiSetCond_Appearing;
3643 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.SetNextWindowPosCond) != 0;
3644 if (window_pos_set_by_api && ImLengthSqr(g.SetNextWindowPosVal - ImVec2(-FLT_MAX,-FLT_MAX)) < 0.001f)
3645 {
3646 window->SetWindowPosCenterWanted = true; // May be processed on the next frame if this is our first frame and we are measuring size
3647 window->SetWindowPosAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
3648 }
3649 else
3650 {
3651 SetWindowPos(window, g.SetNextWindowPosVal, g.SetNextWindowPosCond);
3652 }
3653 window->DC.CursorPos = backup_cursor_pos;
3654 g.SetNextWindowPosCond = 0;
3655 }
3656 if (g.SetNextWindowSizeCond)
3657 {
3658 if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowSizeAllowFlags |= ImGuiSetCond_Appearing;
3659 window_size_set_by_api = (window->SetWindowSizeAllowFlags & g.SetNextWindowSizeCond) != 0;
3660 SetWindowSize(window, g.SetNextWindowSizeVal, g.SetNextWindowSizeCond);
3661 g.SetNextWindowSizeCond = 0;
3662 }
3663 if (g.SetNextWindowContentSizeCond)
3664 {
3665 window->SizeContentsExplicit = g.SetNextWindowContentSizeVal;
3666 g.SetNextWindowContentSizeCond = 0;
3667 }
3668 else if (first_begin_of_the_frame)
3669 {
3670 window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
3671 }
3672 if (g.SetNextWindowCollapsedCond)
3673 {
3674 if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowCollapsedAllowFlags |= ImGuiSetCond_Appearing;
3675 SetWindowCollapsed(window, g.SetNextWindowCollapsedVal, g.SetNextWindowCollapsedCond);
3676 g.SetNextWindowCollapsedCond = 0;
3677 }
3678 if (g.SetNextWindowFocus)
3679 {
3680 ImGui::SetWindowFocus();
3681 g.SetNextWindowFocus = false;
3682 }
3683
3684 // Update known root window (if we are a child window, otherwise window == window->RootWindow)
3685 int root_idx, root_non_popup_idx;
3686 for (root_idx = g.CurrentWindowStack.Size - 1; root_idx > 0; root_idx--)
3687 if (!(g.CurrentWindowStack[root_idx]->Flags & ImGuiWindowFlags_ChildWindow))
3688 break;
3689 for (root_non_popup_idx = root_idx; root_non_popup_idx > 0; root_non_popup_idx--)
3690 if (!(g.CurrentWindowStack[root_non_popup_idx]->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
3691 break;
3692 window->RootWindow = g.CurrentWindowStack[root_idx];
3693 window->RootNonPopupWindow = g.CurrentWindowStack[root_non_popup_idx]; // This is merely for displaying the TitleBgActive color.
3694
3695 // When reusing window again multiple times a frame, just append content (don't need to setup again)
3696 if (first_begin_of_the_frame)
3697 {
3698 window->Active = true;
3699 window->BeginCount = 0;
3700 window->DrawList->Clear();
3701 window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
3702 window->LastFrameActive = current_frame;
3703 window->IDStack.resize(1);
3704
3705 // Setup texture, outer clipping rectangle
3706 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
3707 ImRect fullscreen_rect(GetVisibleRect());
3708 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_ComboBox|ImGuiWindowFlags_Popup)))
3709 PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
3710 else
3711 PushClipRect(fullscreen_rect.Min, fullscreen_rect.Max, true);
3712
3713 // New windows appears in front
3714 if (!window_was_active)
3715 {
3716 window->AutoPosLastDirection = -1;
3717
3718 if (!(flags & ImGuiWindowFlags_NoFocusOnAppearing))
3719 if (!(flags & (ImGuiWindowFlags_ChildWindow|ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
3720 FocusWindow(window);
3721
3722 // Popup first latch mouse position, will position itself when it appears next frame
3723 if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
3724 window->PosFloat = g.IO.MousePos;
3725 }
3726
3727 // Collapse window by double-clicking on title bar
3728 // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing
3729 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
3730 {
3731 ImRect title_bar_rect = window->TitleBarRect();
3732 if (g.HoveredWindow == window && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
3733 {
3734 window->Collapsed = !window->Collapsed;
3735 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
3736 MarkSettingsDirty();
3737 FocusWindow(window);
3738 }
3739 }
3740 else
3741 {
3742 window->Collapsed = false;
3743 }
3744
3745 // SIZE
3746
3747 // Save contents size from last frame for auto-fitting (unless explicitly specified)
3748 window->SizeContents.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.x - window->Pos.x) + window->Scroll.x));
3749 window->SizeContents.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.y - window->Pos.y) + window->Scroll.y));
3750
3751 // Hide popup/tooltip window when first appearing while we measure size (because we recycle them)
3752 if (window->HiddenFrames > 0)
3753 window->HiddenFrames--;
3754 if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && !window_was_active)
3755 {
3756 window->HiddenFrames = 1;
3757 if (flags & ImGuiWindowFlags_AlwaysAutoResize)
3758 {
3759 if (!window_size_set_by_api)
3760 window->Size = window->SizeFull = ImVec2(0.f, 0.f);
3761 window->SizeContents = ImVec2(0.f, 0.f);
3762 }
3763 }
3764
3765 // Lock window padding so that altering the ShowBorders flag for children doesn't have side-effects.
3766 window->WindowPadding = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_ComboBox | ImGuiWindowFlags_Popup))) ? ImVec2(0,0) : style.WindowPadding;
3767
3768 // Calculate auto-fit size
3769 ImVec2 size_auto_fit;
3770 if ((flags & ImGuiWindowFlags_Tooltip) != 0)
3771 {
3772 // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose.
3773 size_auto_fit = window->SizeContents + window->WindowPadding - ImVec2(0.0f, style.ItemSpacing.y);
3774 }
3775 else
3776 {
3777 size_auto_fit = ImClamp(window->SizeContents + window->WindowPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding));
3778
3779 // Handling case of auto fit window not fitting in screen on one axis, we are growing auto fit size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
3780 if (size_auto_fit.x < window->SizeContents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar))
3781 size_auto_fit.y += style.ScrollbarSize;
3782 if (size_auto_fit.y < window->SizeContents.y && !(flags & ImGuiWindowFlags_NoScrollbar))
3783 size_auto_fit.x += style.ScrollbarSize;
3784 size_auto_fit.y = ImMax(size_auto_fit.y - style.ItemSpacing.y, 0.0f);
3785 }
3786
3787 // Handle automatic resize
3788 if (window->Collapsed)
3789 {
3790 // We still process initial auto-fit on collapsed windows to get a window width,
3791 // But otherwise we don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
3792 if (window->AutoFitFramesX > 0)
3793 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
3794 if (window->AutoFitFramesY > 0)
3795 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
3796 window->Size = window->TitleBarRect().GetSize();
3797 }
3798 else
3799 {
3800 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window_size_set_by_api)
3801 {
3802 window->SizeFull = size_auto_fit;
3803 }
3804 else if ((window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) && !window_size_set_by_api)
3805 {
3806 // Auto-fit only grows during the first few frames
3807 if (window->AutoFitFramesX > 0)
3808 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
3809 if (window->AutoFitFramesY > 0)
3810 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
3811 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
3812 MarkSettingsDirty();
3813 }
3814 window->Size = window->SizeFull;
3815 }
3816
3817 // Minimum window size
3818 if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
3819 {
3820 window->SizeFull = ImMax(window->SizeFull, style.WindowMinSize);
3821 if (!window->Collapsed)
3822 window->Size = window->SizeFull;
3823 }
3824
3825 // POSITION
3826
3827 // Position child window
3828 if (flags & ImGuiWindowFlags_ChildWindow)
3829 parent_window->DC.ChildWindows.push_back(window);
3830 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup))
3831 {
3832 window->Pos = window->PosFloat = parent_window->DC.CursorPos;
3833 window->Size = window->SizeFull = size_on_first_use; // NB: argument name 'size_on_first_use' misleading here, it's really just 'size' as provided by user.
3834 }
3835
3836 bool window_pos_center = false;
3837 window_pos_center |= (window->SetWindowPosCenterWanted && window->HiddenFrames == 0);
3838 window_pos_center |= ((flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api && window_appearing_after_being_hidden);
3839 if (window_pos_center)
3840 {
3841 // Center (any sort of window)
3842 SetWindowPos(ImMax(style.DisplaySafeAreaPadding, fullscreen_rect.GetCenter() - window->SizeFull * 0.5f));
3843 }
3844 else if (flags & ImGuiWindowFlags_ChildMenu)
3845 {
3846 IM_ASSERT(window_pos_set_by_api);
3847 ImRect rect_to_avoid;
3848 if (parent_window->DC.MenuBarAppending)
3849 rect_to_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
3850 else
3851 rect_to_avoid = ImRect(parent_window->Pos.x + style.ItemSpacing.x, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - style.ItemSpacing.x - parent_window->ScrollbarSizes.x, FLT_MAX); // We want some overlap to convey the relative depth of each popup (here hard-coded to 4)
3852 window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
3853 }
3854 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_appearing_after_being_hidden)
3855 {
3856 ImRect rect_to_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1);
3857 window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
3858 }
3859
3860 // Position tooltip (always follows mouse)
3861 if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api)
3862 {
3863 ImRect rect_to_avoid(g.IO.MousePos.x - 16, g.IO.MousePos.y - 8, g.IO.MousePos.x + 24, g.IO.MousePos.y + 24); // FIXME: Completely hard-coded. Perhaps center on cursor hit-point instead?
3864 window->PosFloat = FindBestPopupWindowPos(g.IO.MousePos, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
3865 if (window->AutoPosLastDirection == -1)
3866 window->PosFloat = g.IO.MousePos + ImVec2(2,2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
3867 }
3868
3869 // User moving window (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows.
3870 KeepAliveID(window->MoveID);
3871 if (g.ActiveId == window->MoveID)
3872 {
3873 if (g.IO.MouseDown[0])
3874 {
3875 if (!(flags & ImGuiWindowFlags_NoMove))
3876 {
3877 window->PosFloat += g.IO.MouseDelta;
3878 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
3879 MarkSettingsDirty();
3880 }
3881 IM_ASSERT(g.MovedWindow != NULL);
3882 FocusWindow(g.MovedWindow);
3883 }
3884 else
3885 {
3886 SetActiveID(0);
3887 g.MovedWindow = NULL; // Not strictly necessary but doing it for sanity.
3888 }
3889 }
3890
3891 // Clamp position so it stays visible
3892 if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
3893 {
3894 if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
3895 {
3896 ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
3897 window->PosFloat = ImMax(window->PosFloat + window->Size, padding) - window->Size;
3898 window->PosFloat = ImMin(window->PosFloat, g.IO.DisplaySize - padding);
3899 }
3900 }
3901 window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
3902
3903 // Default item width. Make it proportional to window size if window manually resizes
3904 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
3905 window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
3906 else
3907 window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
3908
3909 // Prepare for focus requests
3910 window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == IM_INT_MAX || window->FocusIdxAllCounter == -1) ? IM_INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1);
3911 window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == IM_INT_MAX || window->FocusIdxTabCounter == -1) ? IM_INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1);
3912 window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1;
3913 window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = IM_INT_MAX;
3914
3915 // Apply scrolling
3916 if (window->ScrollTarget.x < FLT_MAX)
3917 {
3918 window->Scroll.x = window->ScrollTarget.x;
3919 window->ScrollTarget.x = FLT_MAX;
3920 }
3921 if (window->ScrollTarget.y < FLT_MAX)
3922 {
3923 float center_ratio = window->ScrollTargetCenterRatio.y;
3924 window->Scroll.y = window->ScrollTarget.y - ((1.0f - center_ratio) * window->TitleBarHeight()) - (center_ratio * window->SizeFull.y);
3925 window->ScrollTarget.y = FLT_MAX;
3926 }
3927 window->Scroll = ImMax(window->Scroll, ImVec2(0.0f, 0.0f));
3928 if (!window->Collapsed && !window->SkipItems)
3929 window->Scroll = ImMin(window->Scroll, ImMax(ImVec2(0.0f, 0.0f), window->SizeContents - window->SizeFull + window->ScrollbarSizes));
3930
3931 // Modal window darkens what is behind them
3932 if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow())
3933 window->DrawList->AddRectFilled(fullscreen_rect.Min, fullscreen_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio));
3934
3935 // Draw window + handle manual resize
3936 ImRect title_bar_rect = window->TitleBarRect();
3937 const float window_rounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding;
3938 if (window->Collapsed)
3939 {
3940 // Draw title bar only
3941 RenderFrame(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32(ImGuiCol_TitleBgCollapsed), true, window_rounding);
3942 }
3943 else
3944 {
3945 ImU32 resize_col = 0;
3946 const float resize_corner_size = ImMax(g.FontSize * 1.35f, window_rounding + 1.0f + g.FontSize * 0.2f);
3947 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && !(flags & ImGuiWindowFlags_NoResize))
3948 {
3949 // Manual resize
3950 const ImVec2 br = window->Rect().GetBR();
3951 const ImRect resize_rect(br - ImVec2(resize_corner_size * 0.75f, resize_corner_size * 0.75f), br);
3952 const ImGuiID resize_id = window->GetID("#RESIZE");
3953 bool hovered, held;
3954 ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds);
3955 resize_col = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
3956
3957 if (hovered || held)
3958 g.MouseCursor = ImGuiMouseCursor_ResizeNWSE;
3959
3960 if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0])
3961 {
3962 // Manual auto-fit when double-clicking
3963 window->SizeFull = size_auto_fit;
3964 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
3965 MarkSettingsDirty();
3966 SetActiveID(0);
3967 }
3968 else if (held)
3969 {
3970 window->SizeFull = ImMax(window->SizeFull + g.IO.MouseDelta, style.WindowMinSize);
3971 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
3972 MarkSettingsDirty();
3973 }
3974
3975 window->Size = window->SizeFull;
3976 title_bar_rect = window->TitleBarRect();
3977 }
3978
3979 // Scrollbars
3980 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > window->Size.y + style.ItemSpacing.y) && !(flags & ImGuiWindowFlags_NoScrollbar));
3981 window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > window->Size.x - (window->ScrollbarY ? style.ScrollbarSize : 0.0f) - window->WindowPadding.x) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
3982 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
3983 window->BorderSize = (flags & ImGuiWindowFlags_ShowBorders) ? 1.0f : 0.0f;
3984
3985 // Window background
3986 // Default alpha
3987 ImGuiCol bg_color_idx = ImGuiCol_WindowBg;
3988 if ((flags & ImGuiWindowFlags_ComboBox) != 0)
3989 bg_color_idx = ImGuiCol_ComboBg;
3990 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 || (flags & ImGuiWindowFlags_Popup) != 0)
3991 bg_color_idx = ImGuiCol_PopupBg;
3992 else if ((flags & ImGuiWindowFlags_ChildWindow) != 0)
3993 bg_color_idx = ImGuiCol_ChildWindowBg;
3994 ImVec4 bg_color = style.Colors[bg_color_idx];
3995 if (bg_alpha >= 0.0f)
3996 bg_color.w = bg_alpha;
3997 bg_color.w *= style.Alpha;
3998 if (bg_color.w > 0.0f)
3999 window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size, ColorConvertFloat4ToU32(bg_color), window_rounding);
4000
4001 // Title bar
4002 if (!(flags & ImGuiWindowFlags_NoTitleBar))
4003 window->DrawList->AddRectFilled(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32((g.FocusedWindow && window->RootNonPopupWindow == g.FocusedWindow->RootNonPopupWindow) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, 1|2);
4004
4005 // Menu bar
4006 if (flags & ImGuiWindowFlags_MenuBar)
4007 {
4008 ImRect menu_bar_rect = window->MenuBarRect();
4009 window->DrawList->AddRectFilled(menu_bar_rect.GetTL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, 1|2);
4010 }
4011
4012 // Scrollbars
4013 if (window->ScrollbarX)
4014 Scrollbar(window, true);
4015 if (window->ScrollbarY)
4016 Scrollbar(window, false);
4017
4018 // Render resize grip
4019 // (after the input handling so we don't have a frame of latency)
4020 if (!(flags & ImGuiWindowFlags_NoResize))
4021 {
4022 const ImVec2 br = window->Rect().GetBR();
4023 window->DrawList->PathLineTo(br + ImVec2(-resize_corner_size, -window->BorderSize));
4024 window->DrawList->PathLineTo(br + ImVec2(-window->BorderSize, -resize_corner_size));
4025 window->DrawList->PathArcToFast(ImVec2(br.x - window_rounding - window->BorderSize, br.y - window_rounding - window->BorderSize), window_rounding, 0, 3);
4026 window->DrawList->PathFill(resize_col);
4027 }
4028
4029 // Borders
4030 if (flags & ImGuiWindowFlags_ShowBorders)
4031 {
4032 window->DrawList->AddRect(window->Pos+ImVec2(1,1), window->Pos+window->Size+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), window_rounding);
4033 window->DrawList->AddRect(window->Pos, window->Pos+window->Size, GetColorU32(ImGuiCol_Border), window_rounding);
4034 if (!(flags & ImGuiWindowFlags_NoTitleBar))
4035 window->DrawList->AddLine(title_bar_rect.GetBL()+ImVec2(1,0), title_bar_rect.GetBR()-ImVec2(1,0), GetColorU32(ImGuiCol_Border));
4036 }
4037 }
4038
4039 // Setup drawing context
4040 window->DC.IndentX = 0.0f + window->WindowPadding.x - window->Scroll.x;
4041 window->DC.ColumnsOffsetX = 0.0f;
4042 window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.IndentX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y);
4043 window->DC.CursorPos = window->DC.CursorStartPos;
4044 window->DC.CursorPosPrevLine = window->DC.CursorPos;
4045 window->DC.CursorMaxPos = window->DC.CursorStartPos;
4046 window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f;
4047 window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
4048 window->DC.MenuBarAppending = false;
4049 window->DC.MenuBarOffsetX = ImMax(window->WindowPadding.x, style.ItemSpacing.x);
4050 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
4051 window->DC.ChildWindows.resize(0);
4052 window->DC.LayoutType = ImGuiLayoutType_Vertical;
4053 window->DC.ItemWidth = window->ItemWidthDefault;
4054 window->DC.TextWrapPos = -1.0f; // disabled
4055 window->DC.AllowKeyboardFocus = true;
4056 window->DC.ButtonRepeat = false;
4057 window->DC.ItemWidthStack.resize(0);
4058 window->DC.TextWrapPosStack.resize(0);
4059 window->DC.AllowKeyboardFocusStack.resize(0);
4060 window->DC.ButtonRepeatStack.resize(0);
4061 window->DC.ColorEditMode = ImGuiColorEditMode_UserSelect;
4062 window->DC.ColumnsCurrent = 0;
4063 window->DC.ColumnsCount = 1;
4064 window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
4065 window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.ColumnsStartPosY;
4066 window->DC.TreeDepth = 0;
4067 window->DC.StateStorage = &window->StateStorage;
4068 window->DC.GroupStack.resize(0);
4069 window->MenuColumns.Update(3, style.ItemSpacing.x, !window_was_active);
4070
4071 if (window->AutoFitFramesX > 0)
4072 window->AutoFitFramesX--;
4073 if (window->AutoFitFramesY > 0)
4074 window->AutoFitFramesY--;
4075
4076 // Title bar
4077 if (!(flags & ImGuiWindowFlags_NoTitleBar))
4078 {
4079 if (p_opened != NULL)
4080 CloseWindowButton(p_opened);
4081
4082 const ImVec2 text_size = CalcTextSize(name, NULL, true);
4083 if (!(flags & ImGuiWindowFlags_NoCollapse))
4084 RenderCollapseTriangle(window->Pos + style.FramePadding, !window->Collapsed, 1.0f, true);
4085
4086 ImVec2 text_min = window->Pos + style.FramePadding;
4087 ImVec2 text_max = window->Pos + ImVec2(window->Size.x - style.FramePadding.x, style.FramePadding.y*2 + text_size.y);
4088 ImVec2 clip_max = ImVec2(window->Pos.x + window->Size.x - (p_opened ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x), text_max.y); // Match the size of CloseWindowButton()
4089 bool pad_left = (flags & ImGuiWindowFlags_NoCollapse) == 0;
4090 bool pad_right = (p_opened != NULL);
4091 if (style.WindowTitleAlign & ImGuiAlign_Center) pad_right = pad_left;
4092 if (pad_left) text_min.x += g.FontSize + style.ItemInnerSpacing.x;
4093 if (pad_right) text_max.x -= g.FontSize + style.ItemInnerSpacing.x;
4094 RenderTextClipped(text_min, text_max, name, NULL, &text_size, style.WindowTitleAlign, NULL, &clip_max);
4095 }
4096
4097 // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
4098 window->ClippedWindowRect = window->Rect();
4099 window->ClippedWindowRect.Clip(window->ClipRect);
4100
4101 // Pressing CTRL+C while holding on a window copy its content to the clipboard
4102 // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope.
4103 // Maybe we can support CTRL+C on every element?
4104 /*
4105 if (g.ActiveId == move_id)
4106 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
4107 ImGui::LogToClipboard();
4108 */
4109 }
4110
4111 // Inner clipping rectangle
4112 // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
4113 // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior.
4114 const ImRect title_bar_rect = window->TitleBarRect();
4115 const float border_size = window->BorderSize;
4116 ImRect clip_rect;
4117 clip_rect.Min.x = title_bar_rect.Min.x + 0.5f + ImMax(border_size, window->WindowPadding.x*0.5f);
4118 clip_rect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + 0.5f + border_size;
4119 clip_rect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - ImMax(border_size, window->WindowPadding.x*0.5f);
4120 clip_rect.Max.y = window->Pos.y + window->Size.y - border_size - window->ScrollbarSizes.y;
4121 PushClipRect(clip_rect.Min, clip_rect.Max, true);
4122
4123 // Clear 'accessed' flag last thing
4124 if (first_begin_of_the_frame)
4125 window->Accessed = false;
4126 window->BeginCount++;
4127
4128 // Child window can be out of sight and have "negative" clip windows.
4129 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar).
4130 if (flags & ImGuiWindowFlags_ChildWindow)
4131 {
4132 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
4133 window->Collapsed = parent_window && parent_window->Collapsed;
4134
4135 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
4136 window->Collapsed |= (window->ClippedWindowRect.Min.x >= window->ClippedWindowRect.Max.x || window->ClippedWindowRect.Min.y >= window->ClippedWindowRect.Max.y);
4137
4138 // We also hide the window from rendering because we've already added its border to the command list.
4139 // (we could perform the check earlier in the function but it is simpler at this point)
4140 if (window->Collapsed)
4141 window->Active = false;
4142 }
4143 if (style.Alpha <= 0.0f)
4144 window->Active = false;
4145
4146 // Return false if we don't intend to display anything to allow user to perform an early out optimization
4147 window->SkipItems = (window->Collapsed || !window->Active) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0;
4148 return !window->SkipItems;
4149 }
4150
End()4151 void ImGui::End()
4152 {
4153 ImGuiState& g = *GImGui;
4154 ImGuiWindow* window = g.CurrentWindow;
4155
4156 ImGui::Columns(1, "#CloseColumns");
4157 PopClipRect(); // inner window clip rectangle
4158
4159 // Stop logging
4160 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
4161 ImGui::LogFinish();
4162
4163 // Pop
4164 // NB: we don't clear 'window->RootWindow'. The pointer is allowed to live until the next call to Begin().
4165 g.CurrentWindowStack.pop_back();
4166 if (window->Flags & ImGuiWindowFlags_Popup)
4167 g.CurrentPopupStack.pop_back();
4168 CheckStacksSize(window, false);
4169 SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
4170 }
4171
4172 // Vertical scrollbar
4173 // The entire piece of code below is rather confusing because:
4174 // - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
4175 // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar
4176 // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.
Scrollbar(ImGuiWindow * window,bool horizontal)4177 static void Scrollbar(ImGuiWindow* window, bool horizontal)
4178 {
4179 ImGuiState& g = *GImGui;
4180 const ImGuiStyle& style = g.Style;
4181 const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY");
4182
4183 // Render background
4184 bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX);
4185 float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f;
4186 const ImRect window_rect = window->Rect();
4187 const float border_size = window->BorderSize;
4188 ImRect bb = horizontal
4189 ? ImRect(window->Pos.x + border_size, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - other_scrollbar_size_w - border_size, window_rect.Max.y - border_size)
4190 : ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size);
4191 if (!horizontal)
4192 bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() - border_size : 0.0f);
4193
4194 float window_rounding = (window->Flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding;
4195 int window_rounding_corners;
4196 if (horizontal)
4197 window_rounding_corners = 8 | (other_scrollbar ? 0 : 4);
4198 else
4199 window_rounding_corners = ((window->Flags & ImGuiWindowFlags_NoTitleBar) ? 2 : 0) | (other_scrollbar ? 0 : 4);
4200 window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_ScrollbarBg), window_rounding, window_rounding_corners);
4201 bb.Reduce(ImVec2(ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f)));
4202
4203 // V denote the main axis of the scrollbar
4204 float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight();
4205 float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y;
4206 float win_size_avail_v = (horizontal ? window->Size.x : window->Size.y) - other_scrollbar_size_w;
4207 float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y;
4208
4209 // The grabable box size generally represent the amount visible (vs the total scrollable amount)
4210 // But we maintain a minimum size in pixel to allow for the user to still aim inside.
4211 const float grab_h_pixels = ImMin(ImMax(scrollbar_size_v * ImSaturate(win_size_avail_v / ImMax(win_size_contents_v, win_size_avail_v)), style.GrabMinSize), scrollbar_size_v);
4212 const float grab_h_norm = grab_h_pixels / scrollbar_size_v;
4213
4214 // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().
4215 bool held = false;
4216 bool hovered = false;
4217 const bool previously_held = (g.ActiveId == id);
4218 ImGui::ButtonBehavior(bb, id, &hovered, &held);
4219
4220 float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v);
4221 float scroll_ratio = ImSaturate(scroll_v / scroll_max);
4222 float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
4223 if (held && grab_h_norm < 1.0f)
4224 {
4225 float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y;
4226 float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
4227 float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y;
4228
4229 // Click position in scrollbar normalized space (0.0f->1.0f)
4230 const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
4231 ImGui::SetHoveredID(id);
4232
4233 bool seek_absolute = false;
4234 if (!previously_held)
4235 {
4236 // On initial click calculate the distance between mouse and the center of the grab
4237 if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm)
4238 {
4239 *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
4240 }
4241 else
4242 {
4243 seek_absolute = true;
4244 *click_delta_to_grab_center_v = 0.0f;
4245 }
4246 }
4247
4248 // Apply scroll
4249 // It is ok to modify Scroll here because we are being called in Begin() after the calculation of SizeContents and before setting up our starting position
4250 const float scroll_v_norm = ImSaturate((clicked_v_norm - *click_delta_to_grab_center_v - grab_h_norm*0.5f) / (1.0f - grab_h_norm));
4251 scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v));
4252 if (horizontal)
4253 window->Scroll.x = scroll_v;
4254 else
4255 window->Scroll.y = scroll_v;
4256
4257 // Update values for rendering
4258 scroll_ratio = ImSaturate(scroll_v / scroll_max);
4259 grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
4260
4261 // Update distance to grab now that we have seeked and saturated
4262 if (seek_absolute)
4263 *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
4264 }
4265
4266 // Render
4267 const ImU32 grab_col = ImGui::GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab);
4268 if (horizontal)
4269 window->DrawList->AddRectFilled(ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y), ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y), grab_col, style.ScrollbarRounding);
4270 else
4271 window->DrawList->AddRectFilled(ImVec2(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm)), ImVec2(bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels), grab_col, style.ScrollbarRounding);
4272 }
4273
4274 // Moving window to front of display (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)4275 void ImGui::FocusWindow(ImGuiWindow* window)
4276 {
4277 ImGuiState& g = *GImGui;
4278
4279 // Always mark the window we passed as focused. This is used for keyboard interactions such as tabbing.
4280 g.FocusedWindow = window;
4281
4282 // Passing NULL allow to disable keyboard focus
4283 if (!window)
4284 return;
4285
4286 // And move its root window to the top of the pile
4287 if (window->RootWindow)
4288 window = window->RootWindow;
4289
4290 // Steal focus on active widgets
4291 if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
4292 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
4293 ImGui::SetActiveID(0);
4294
4295 // Bring to front
4296 if ((window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) || g.Windows.back() == window)
4297 return;
4298 for (int i = 0; i < g.Windows.Size; i++)
4299 if (g.Windows[i] == window)
4300 {
4301 g.Windows.erase(g.Windows.begin() + i);
4302 break;
4303 }
4304 g.Windows.push_back(window);
4305 }
4306
PushItemWidth(float item_width)4307 void ImGui::PushItemWidth(float item_width)
4308 {
4309 ImGuiWindow* window = GetCurrentWindow();
4310 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
4311 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
4312 }
4313
PushMultiItemsWidths(int components,float w_full)4314 static void PushMultiItemsWidths(int components, float w_full)
4315 {
4316 ImGuiWindow* window = ImGui::GetCurrentWindow();
4317 const ImGuiStyle& style = GImGui->Style;
4318 if (w_full <= 0.0f)
4319 w_full = ImGui::CalcItemWidth();
4320 const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
4321 const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
4322 window->DC.ItemWidthStack.push_back(w_item_last);
4323 for (int i = 0; i < components-1; i++)
4324 window->DC.ItemWidthStack.push_back(w_item_one);
4325 window->DC.ItemWidth = window->DC.ItemWidthStack.back();
4326 }
4327
PopItemWidth()4328 void ImGui::PopItemWidth()
4329 {
4330 ImGuiWindow* window = GetCurrentWindow();
4331 window->DC.ItemWidthStack.pop_back();
4332 window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
4333 }
4334
CalcItemWidth()4335 float ImGui::CalcItemWidth()
4336 {
4337 ImGuiWindow* window = GetCurrentWindowRead();
4338 float w = window->DC.ItemWidth;
4339 if (w < 0.0f)
4340 {
4341 // Align to a right-side limit. We include 1 frame padding in the calculation because this is how the width is always used (we add 2 frame padding to it), but we could move that responsibility to the widget as well.
4342 float width_to_right_edge = ImGui::GetContentRegionAvail().x;
4343 w = ImMax(1.0f, width_to_right_edge + w);
4344 }
4345 w = (float)(int)w;
4346 return w;
4347 }
4348
SetCurrentFont(ImFont * font)4349 static void SetCurrentFont(ImFont* font)
4350 {
4351 ImGuiState& g = *GImGui;
4352 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
4353 IM_ASSERT(font->Scale > 0.0f);
4354 g.Font = font;
4355 g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale;
4356 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
4357 g.FontTexUvWhitePixel = g.Font->ContainerAtlas->TexUvWhitePixel;
4358 }
4359
PushFont(ImFont * font)4360 void ImGui::PushFont(ImFont* font)
4361 {
4362 ImGuiState& g = *GImGui;
4363 if (!font)
4364 font = g.IO.Fonts->Fonts[0];
4365 SetCurrentFont(font);
4366 g.FontStack.push_back(font);
4367 g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
4368 }
4369
PopFont()4370 void ImGui::PopFont()
4371 {
4372 ImGuiState& g = *GImGui;
4373 g.CurrentWindow->DrawList->PopTextureID();
4374 g.FontStack.pop_back();
4375 SetCurrentFont(g.FontStack.empty() ? g.IO.Fonts->Fonts[0] : g.FontStack.back());
4376 }
4377
PushAllowKeyboardFocus(bool allow_keyboard_focus)4378 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
4379 {
4380 ImGuiWindow* window = GetCurrentWindow();
4381 window->DC.AllowKeyboardFocus = allow_keyboard_focus;
4382 window->DC.AllowKeyboardFocusStack.push_back(allow_keyboard_focus);
4383 }
4384
PopAllowKeyboardFocus()4385 void ImGui::PopAllowKeyboardFocus()
4386 {
4387 ImGuiWindow* window = GetCurrentWindow();
4388 window->DC.AllowKeyboardFocusStack.pop_back();
4389 window->DC.AllowKeyboardFocus = window->DC.AllowKeyboardFocusStack.empty() ? true : window->DC.AllowKeyboardFocusStack.back();
4390 }
4391
PushButtonRepeat(bool repeat)4392 void ImGui::PushButtonRepeat(bool repeat)
4393 {
4394 ImGuiWindow* window = GetCurrentWindow();
4395 window->DC.ButtonRepeat = repeat;
4396 window->DC.ButtonRepeatStack.push_back(repeat);
4397 }
4398
PopButtonRepeat()4399 void ImGui::PopButtonRepeat()
4400 {
4401 ImGuiWindow* window = GetCurrentWindow();
4402 window->DC.ButtonRepeatStack.pop_back();
4403 window->DC.ButtonRepeat = window->DC.ButtonRepeatStack.empty() ? false : window->DC.ButtonRepeatStack.back();
4404 }
4405
PushTextWrapPos(float wrap_pos_x)4406 void ImGui::PushTextWrapPos(float wrap_pos_x)
4407 {
4408 ImGuiWindow* window = GetCurrentWindow();
4409 window->DC.TextWrapPos = wrap_pos_x;
4410 window->DC.TextWrapPosStack.push_back(wrap_pos_x);
4411 }
4412
PopTextWrapPos()4413 void ImGui::PopTextWrapPos()
4414 {
4415 ImGuiWindow* window = GetCurrentWindow();
4416 window->DC.TextWrapPosStack.pop_back();
4417 window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
4418 }
4419
PushStyleColor(ImGuiCol idx,const ImVec4 & col)4420 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
4421 {
4422 ImGuiState& g = *GImGui;
4423 ImGuiColMod backup;
4424 backup.Col = idx;
4425 backup.PreviousValue = g.Style.Colors[idx];
4426 g.ColorModifiers.push_back(backup);
4427 g.Style.Colors[idx] = col;
4428 }
4429
PopStyleColor(int count)4430 void ImGui::PopStyleColor(int count)
4431 {
4432 ImGuiState& g = *GImGui;
4433 while (count > 0)
4434 {
4435 ImGuiColMod& backup = g.ColorModifiers.back();
4436 g.Style.Colors[backup.Col] = backup.PreviousValue;
4437 g.ColorModifiers.pop_back();
4438 count--;
4439 }
4440 }
4441
GetStyleVarFloatAddr(ImGuiStyleVar idx)4442 static float* GetStyleVarFloatAddr(ImGuiStyleVar idx)
4443 {
4444 ImGuiState& g = *GImGui;
4445 switch (idx)
4446 {
4447 case ImGuiStyleVar_Alpha: return &g.Style.Alpha;
4448 case ImGuiStyleVar_WindowRounding: return &g.Style.WindowRounding;
4449 case ImGuiStyleVar_ChildWindowRounding: return &g.Style.ChildWindowRounding;
4450 case ImGuiStyleVar_FrameRounding: return &g.Style.FrameRounding;
4451 case ImGuiStyleVar_IndentSpacing: return &g.Style.IndentSpacing;
4452 case ImGuiStyleVar_GrabMinSize: return &g.Style.GrabMinSize;
4453 }
4454 return NULL;
4455 }
4456
GetStyleVarVec2Addr(ImGuiStyleVar idx)4457 static ImVec2* GetStyleVarVec2Addr(ImGuiStyleVar idx)
4458 {
4459 ImGuiState& g = *GImGui;
4460 switch (idx)
4461 {
4462 case ImGuiStyleVar_WindowPadding: return &g.Style.WindowPadding;
4463 case ImGuiStyleVar_WindowMinSize: return &g.Style.WindowMinSize;
4464 case ImGuiStyleVar_FramePadding: return &g.Style.FramePadding;
4465 case ImGuiStyleVar_ItemSpacing: return &g.Style.ItemSpacing;
4466 case ImGuiStyleVar_ItemInnerSpacing: return &g.Style.ItemInnerSpacing;
4467 }
4468 return NULL;
4469 }
4470
PushStyleVar(ImGuiStyleVar idx,float val)4471 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
4472 {
4473 ImGuiState& g = *GImGui;
4474 float* pvar = GetStyleVarFloatAddr(idx);
4475 IM_ASSERT(pvar != NULL); // Called function with wrong-type? Variable is not a float.
4476 ImGuiStyleMod backup;
4477 backup.Var = idx;
4478 backup.PreviousValue = ImVec2(*pvar, 0.0f);
4479 g.StyleModifiers.push_back(backup);
4480 *pvar = val;
4481 }
4482
4483
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)4484 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
4485 {
4486 ImGuiState& g = *GImGui;
4487 ImVec2* pvar = GetStyleVarVec2Addr(idx);
4488 IM_ASSERT(pvar != NULL); // Called function with wrong-type? Variable is not a ImVec2.
4489 ImGuiStyleMod backup;
4490 backup.Var = idx;
4491 backup.PreviousValue = *pvar;
4492 g.StyleModifiers.push_back(backup);
4493 *pvar = val;
4494 }
4495
PopStyleVar(int count)4496 void ImGui::PopStyleVar(int count)
4497 {
4498 ImGuiState& g = *GImGui;
4499 while (count > 0)
4500 {
4501 ImGuiStyleMod& backup = g.StyleModifiers.back();
4502 if (float* pvar_f = GetStyleVarFloatAddr(backup.Var))
4503 *pvar_f = backup.PreviousValue.x;
4504 else if (ImVec2* pvar_v = GetStyleVarVec2Addr(backup.Var))
4505 *pvar_v = backup.PreviousValue;
4506 g.StyleModifiers.pop_back();
4507 count--;
4508 }
4509 }
4510
GetStyleColName(ImGuiCol idx)4511 const char* ImGui::GetStyleColName(ImGuiCol idx)
4512 {
4513 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
4514 switch (idx)
4515 {
4516 case ImGuiCol_Text: return "Text";
4517 case ImGuiCol_TextDisabled: return "TextDisabled";
4518 case ImGuiCol_WindowBg: return "WindowBg";
4519 case ImGuiCol_ChildWindowBg: return "ChildWindowBg";
4520 case ImGuiCol_PopupBg: return "PopupBg";
4521 case ImGuiCol_Border: return "Border";
4522 case ImGuiCol_BorderShadow: return "BorderShadow";
4523 case ImGuiCol_FrameBg: return "FrameBg";
4524 case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
4525 case ImGuiCol_FrameBgActive: return "FrameBgActive";
4526 case ImGuiCol_TitleBg: return "TitleBg";
4527 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
4528 case ImGuiCol_TitleBgActive: return "TitleBgActive";
4529 case ImGuiCol_MenuBarBg: return "MenuBarBg";
4530 case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
4531 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
4532 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
4533 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
4534 case ImGuiCol_ComboBg: return "ComboBg";
4535 case ImGuiCol_CheckMark: return "CheckMark";
4536 case ImGuiCol_SliderGrab: return "SliderGrab";
4537 case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
4538 case ImGuiCol_Button: return "Button";
4539 case ImGuiCol_ButtonHovered: return "ButtonHovered";
4540 case ImGuiCol_ButtonActive: return "ButtonActive";
4541 case ImGuiCol_Header: return "Header";
4542 case ImGuiCol_HeaderHovered: return "HeaderHovered";
4543 case ImGuiCol_HeaderActive: return "HeaderActive";
4544 case ImGuiCol_Column: return "Column";
4545 case ImGuiCol_ColumnHovered: return "ColumnHovered";
4546 case ImGuiCol_ColumnActive: return "ColumnActive";
4547 case ImGuiCol_ResizeGrip: return "ResizeGrip";
4548 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
4549 case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
4550 case ImGuiCol_CloseButton: return "CloseButton";
4551 case ImGuiCol_CloseButtonHovered: return "CloseButtonHovered";
4552 case ImGuiCol_CloseButtonActive: return "CloseButtonActive";
4553 case ImGuiCol_PlotLines: return "PlotLines";
4554 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
4555 case ImGuiCol_PlotHistogram: return "PlotHistogram";
4556 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
4557 case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
4558 case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening";
4559 }
4560 IM_ASSERT(0);
4561 return "Unknown";
4562 }
4563
IsWindowHovered()4564 bool ImGui::IsWindowHovered()
4565 {
4566 ImGuiState& g = *GImGui;
4567 return g.HoveredWindow == g.CurrentWindow && IsWindowContentHoverable(g.HoveredRootWindow);
4568 }
4569
IsWindowFocused()4570 bool ImGui::IsWindowFocused()
4571 {
4572 ImGuiState& g = *GImGui;
4573 return g.FocusedWindow == g.CurrentWindow;
4574 }
4575
IsRootWindowFocused()4576 bool ImGui::IsRootWindowFocused()
4577 {
4578 ImGuiState& g = *GImGui;
4579 ImGuiWindow* root_window = g.CurrentWindow->RootWindow;
4580 return g.FocusedWindow == root_window;
4581 }
4582
IsRootWindowOrAnyChildFocused()4583 bool ImGui::IsRootWindowOrAnyChildFocused()
4584 {
4585 ImGuiState& g = *GImGui;
4586 ImGuiWindow* root_window = g.CurrentWindow->RootWindow;
4587 return g.FocusedWindow && g.FocusedWindow->RootWindow == root_window;
4588 }
4589
GetWindowWidth()4590 float ImGui::GetWindowWidth()
4591 {
4592 ImGuiWindow* window = GImGui->CurrentWindow;
4593 return window->Size.x;
4594 }
4595
GetWindowHeight()4596 float ImGui::GetWindowHeight()
4597 {
4598 ImGuiWindow* window = GImGui->CurrentWindow;
4599 return window->Size.y;
4600 }
4601
GetWindowPos()4602 ImVec2 ImGui::GetWindowPos()
4603 {
4604 ImGuiState& g = *GImGui;
4605 ImGuiWindow* window = g.CurrentWindow;
4606 return window->Pos;
4607 }
4608
SetWindowScrollY(ImGuiWindow * window,float new_scroll_y)4609 static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
4610 {
4611 window->DC.CursorMaxPos.y += window->Scroll.y;
4612 window->Scroll.y = new_scroll_y;
4613 window->DC.CursorMaxPos.y -= window->Scroll.y;
4614 }
4615
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiSetCond cond)4616 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond)
4617 {
4618 // Test condition (NB: bit 0 is always true) and clear flags for next time
4619 if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
4620 return;
4621 window->SetWindowPosAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
4622 window->SetWindowPosCenterWanted = false;
4623
4624 // Set
4625 const ImVec2 old_pos = window->Pos;
4626 window->PosFloat = pos;
4627 window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
4628 window->DC.CursorPos += (window->Pos - old_pos); // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor
4629 window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected.
4630 }
4631
SetWindowPos(const ImVec2 & pos,ImGuiSetCond cond)4632 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiSetCond cond)
4633 {
4634 ImGuiWindow* window = GetCurrentWindow();
4635 SetWindowPos(window, pos, cond);
4636 }
4637
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiSetCond cond)4638 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiSetCond cond)
4639 {
4640 ImGuiWindow* window = FindWindowByName(name);
4641 if (window)
4642 SetWindowPos(window, pos, cond);
4643 }
4644
GetWindowSize()4645 ImVec2 ImGui::GetWindowSize()
4646 {
4647 ImGuiWindow* window = GetCurrentWindowRead();
4648 return window->Size;
4649 }
4650
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiSetCond cond)4651 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond)
4652 {
4653 // Test condition (NB: bit 0 is always true) and clear flags for next time
4654 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
4655 return;
4656 window->SetWindowSizeAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
4657
4658 // Set
4659 if (size.x > 0.0f)
4660 {
4661 window->AutoFitFramesX = 0;
4662 window->SizeFull.x = size.x;
4663 }
4664 else
4665 {
4666 window->AutoFitFramesX = 2;
4667 window->AutoFitOnlyGrows = false;
4668 }
4669 if (size.y > 0.0f)
4670 {
4671 window->AutoFitFramesY = 0;
4672 window->SizeFull.y = size.y;
4673 }
4674 else
4675 {
4676 window->AutoFitFramesY = 2;
4677 window->AutoFitOnlyGrows = false;
4678 }
4679 }
4680
SetWindowSize(const ImVec2 & size,ImGuiSetCond cond)4681 void ImGui::SetWindowSize(const ImVec2& size, ImGuiSetCond cond)
4682 {
4683 SetWindowSize(GImGui->CurrentWindow, size, cond);
4684 }
4685
SetWindowSize(const char * name,const ImVec2 & size,ImGuiSetCond cond)4686 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiSetCond cond)
4687 {
4688 ImGuiWindow* window = FindWindowByName(name);
4689 if (window)
4690 SetWindowSize(window, size, cond);
4691 }
4692
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiSetCond cond)4693 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiSetCond cond)
4694 {
4695 // Test condition (NB: bit 0 is always true) and clear flags for next time
4696 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
4697 return;
4698 window->SetWindowCollapsedAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing);
4699
4700 // Set
4701 window->Collapsed = collapsed;
4702 }
4703
SetWindowCollapsed(bool collapsed,ImGuiSetCond cond)4704 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiSetCond cond)
4705 {
4706 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
4707 }
4708
IsWindowCollapsed()4709 bool ImGui::IsWindowCollapsed()
4710 {
4711 return GImGui->CurrentWindow->Collapsed;
4712 }
4713
SetWindowCollapsed(const char * name,bool collapsed,ImGuiSetCond cond)4714 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiSetCond cond)
4715 {
4716 ImGuiWindow* window = FindWindowByName(name);
4717 if (window)
4718 SetWindowCollapsed(window, collapsed, cond);
4719 }
4720
SetWindowFocus()4721 void ImGui::SetWindowFocus()
4722 {
4723 FocusWindow(GImGui->CurrentWindow);
4724 }
4725
SetWindowFocus(const char * name)4726 void ImGui::SetWindowFocus(const char* name)
4727 {
4728 if (name)
4729 {
4730 ImGuiWindow* window = FindWindowByName(name);
4731 if (window)
4732 FocusWindow(window);
4733 }
4734 else
4735 {
4736 FocusWindow(NULL);
4737 }
4738 }
4739
SetNextWindowPos(const ImVec2 & pos,ImGuiSetCond cond)4740 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiSetCond cond)
4741 {
4742 ImGuiState& g = *GImGui;
4743 g.SetNextWindowPosVal = pos;
4744 g.SetNextWindowPosCond = cond ? cond : ImGuiSetCond_Always;
4745 }
4746
SetNextWindowPosCenter(ImGuiSetCond cond)4747 void ImGui::SetNextWindowPosCenter(ImGuiSetCond cond)
4748 {
4749 ImGuiState& g = *GImGui;
4750 g.SetNextWindowPosVal = ImVec2(-FLT_MAX, -FLT_MAX);
4751 g.SetNextWindowPosCond = cond ? cond : ImGuiSetCond_Always;
4752 }
4753
SetNextWindowSize(const ImVec2 & size,ImGuiSetCond cond)4754 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiSetCond cond)
4755 {
4756 ImGuiState& g = *GImGui;
4757 g.SetNextWindowSizeVal = size;
4758 g.SetNextWindowSizeCond = cond ? cond : ImGuiSetCond_Always;
4759 }
4760
SetNextWindowContentSize(const ImVec2 & size)4761 void ImGui::SetNextWindowContentSize(const ImVec2& size)
4762 {
4763 ImGuiState& g = *GImGui;
4764 g.SetNextWindowContentSizeVal = size;
4765 g.SetNextWindowContentSizeCond = ImGuiSetCond_Always;
4766 }
4767
SetNextWindowContentWidth(float width)4768 void ImGui::SetNextWindowContentWidth(float width)
4769 {
4770 ImGuiState& g = *GImGui;
4771 g.SetNextWindowContentSizeVal = ImVec2(width, g.SetNextWindowContentSizeCond ? g.SetNextWindowContentSizeVal.y : 0.0f);
4772 g.SetNextWindowContentSizeCond = ImGuiSetCond_Always;
4773 }
4774
SetNextWindowCollapsed(bool collapsed,ImGuiSetCond cond)4775 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiSetCond cond)
4776 {
4777 ImGuiState& g = *GImGui;
4778 g.SetNextWindowCollapsedVal = collapsed;
4779 g.SetNextWindowCollapsedCond = cond ? cond : ImGuiSetCond_Always;
4780 }
4781
SetNextWindowFocus()4782 void ImGui::SetNextWindowFocus()
4783 {
4784 ImGuiState& g = *GImGui;
4785 g.SetNextWindowFocus = true;
4786 }
4787
4788 // In window space (not screen space!)
4789 // FIXME-OPT: Could cache and maintain it (pretty much only change on columns change)
GetContentRegionMax()4790 ImVec2 ImGui::GetContentRegionMax()
4791 {
4792 ImGuiWindow* window = GetCurrentWindowRead();
4793 ImVec2 content_region_size = ImVec2(window->SizeContentsExplicit.x ? window->SizeContentsExplicit.x : window->Size.x - window->ScrollbarSizes.x, window->SizeContentsExplicit.y ? window->SizeContentsExplicit.y : window->Size.y - window->ScrollbarSizes.y);
4794 ImVec2 mx = content_region_size - window->Scroll - window->WindowPadding;
4795 if (window->DC.ColumnsCount != 1)
4796 mx.x = ImGui::GetColumnOffset(window->DC.ColumnsCurrent + 1) - window->WindowPadding.x;
4797 return mx;
4798 }
4799
GetContentRegionAvail()4800 ImVec2 ImGui::GetContentRegionAvail()
4801 {
4802 ImGuiWindow* window = GetCurrentWindowRead();
4803 return GetContentRegionMax() - (window->DC.CursorPos - window->Pos);
4804 }
4805
GetContentRegionAvailWidth()4806 float ImGui::GetContentRegionAvailWidth()
4807 {
4808 return GetContentRegionAvail().x;
4809 }
4810
4811 // In window space (not screen space!)
GetWindowContentRegionMin()4812 ImVec2 ImGui::GetWindowContentRegionMin()
4813 {
4814 ImGuiWindow* window = GetCurrentWindowRead();
4815 return ImVec2(-window->Scroll.x, -window->Scroll.y + window->TitleBarHeight() + window->MenuBarHeight()) + window->WindowPadding;
4816 }
4817
GetWindowContentRegionMax()4818 ImVec2 ImGui::GetWindowContentRegionMax()
4819 {
4820 ImGuiWindow* window = GetCurrentWindowRead();
4821 ImVec2 content_region_size = ImVec2(window->SizeContentsExplicit.x ? window->SizeContentsExplicit.x : window->Size.x, window->SizeContentsExplicit.y ? window->SizeContentsExplicit.y : window->Size.y);
4822 ImVec2 m = content_region_size - window->Scroll - window->WindowPadding - window->ScrollbarSizes;
4823 return m;
4824 }
4825
GetWindowContentRegionWidth()4826 float ImGui::GetWindowContentRegionWidth()
4827 {
4828 return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x;
4829 }
4830
GetTextLineHeight()4831 float ImGui::GetTextLineHeight()
4832 {
4833 ImGuiState& g = *GImGui;
4834 return g.FontSize;
4835 }
4836
GetTextLineHeightWithSpacing()4837 float ImGui::GetTextLineHeightWithSpacing()
4838 {
4839 ImGuiState& g = *GImGui;
4840 return g.FontSize + g.Style.ItemSpacing.y;
4841 }
4842
GetItemsLineHeightWithSpacing()4843 float ImGui::GetItemsLineHeightWithSpacing()
4844 {
4845 ImGuiState& g = *GImGui;
4846 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
4847 }
4848
GetWindowDrawList()4849 ImDrawList* ImGui::GetWindowDrawList()
4850 {
4851 ImGuiWindow* window = GetCurrentWindow();
4852 return window->DrawList;
4853 }
4854
GetFont()4855 ImFont* ImGui::GetFont()
4856 {
4857 return GImGui->Font;
4858 }
4859
GetFontSize()4860 float ImGui::GetFontSize()
4861 {
4862 return GImGui->FontSize;
4863 }
4864
GetFontTexUvWhitePixel()4865 ImVec2 ImGui::GetFontTexUvWhitePixel()
4866 {
4867 return GImGui->FontTexUvWhitePixel;
4868 }
4869
SetWindowFontScale(float scale)4870 void ImGui::SetWindowFontScale(float scale)
4871 {
4872 ImGuiState& g = *GImGui;
4873 ImGuiWindow* window = GetCurrentWindow();
4874 window->FontWindowScale = scale;
4875 g.FontSize = window->CalcFontSize();
4876 }
4877
4878 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
4879 // Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'.
GetCursorPos()4880 ImVec2 ImGui::GetCursorPos()
4881 {
4882 ImGuiWindow* window = GetCurrentWindowRead();
4883 return window->DC.CursorPos - window->Pos + window->Scroll;
4884 }
4885
GetCursorPosX()4886 float ImGui::GetCursorPosX()
4887 {
4888 ImGuiWindow* window = GetCurrentWindow();
4889 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
4890 }
4891
GetCursorPosY()4892 float ImGui::GetCursorPosY()
4893 {
4894 ImGuiWindow* window = GetCurrentWindow();
4895 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
4896 }
4897
SetCursorPos(const ImVec2 & local_pos)4898 void ImGui::SetCursorPos(const ImVec2& local_pos)
4899 {
4900 ImGuiWindow* window = GetCurrentWindow();
4901 window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
4902 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
4903 }
4904
SetCursorPosX(float x)4905 void ImGui::SetCursorPosX(float x)
4906 {
4907 ImGuiWindow* window = GetCurrentWindow();
4908 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
4909 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
4910 }
4911
SetCursorPosY(float y)4912 void ImGui::SetCursorPosY(float y)
4913 {
4914 ImGuiWindow* window = GetCurrentWindow();
4915 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
4916 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
4917 }
4918
GetCursorStartPos()4919 ImVec2 ImGui::GetCursorStartPos()
4920 {
4921 ImGuiWindow* window = GetCurrentWindowRead();
4922 return window->DC.CursorStartPos - window->Pos;
4923 }
4924
GetCursorScreenPos()4925 ImVec2 ImGui::GetCursorScreenPos()
4926 {
4927 ImGuiWindow* window = GetCurrentWindowRead();
4928 return window->DC.CursorPos;
4929 }
4930
SetCursorScreenPos(const ImVec2 & screen_pos)4931 void ImGui::SetCursorScreenPos(const ImVec2& screen_pos)
4932 {
4933 ImGuiWindow* window = GetCurrentWindow();
4934 window->DC.CursorPos = screen_pos;
4935 }
4936
GetScrollX()4937 float ImGui::GetScrollX()
4938 {
4939 return GImGui->CurrentWindow->Scroll.x;
4940 }
4941
GetScrollY()4942 float ImGui::GetScrollY()
4943 {
4944 return GImGui->CurrentWindow->Scroll.y;
4945 }
4946
GetScrollMaxX()4947 float ImGui::GetScrollMaxX()
4948 {
4949 ImGuiWindow* window = GetCurrentWindowRead();
4950 return window->SizeContents.x - window->SizeFull.x - window->ScrollbarSizes.x;
4951 }
4952
GetScrollMaxY()4953 float ImGui::GetScrollMaxY()
4954 {
4955 ImGuiWindow* window = GetCurrentWindowRead();
4956 return window->SizeContents.y - window->SizeFull.y - window->ScrollbarSizes.y;
4957 }
4958
SetScrollX(float scroll_x)4959 void ImGui::SetScrollX(float scroll_x)
4960 {
4961 ImGuiWindow* window = GetCurrentWindow();
4962 window->ScrollTarget.x = scroll_x;
4963 window->ScrollTargetCenterRatio.x = 0.0f;
4964 }
4965
SetScrollY(float scroll_y)4966 void ImGui::SetScrollY(float scroll_y)
4967 {
4968 ImGuiWindow* window = GetCurrentWindow();
4969 window->ScrollTarget.y = scroll_y + window->TitleBarHeight(); // title bar height canceled out when using ScrollTargetRelY
4970 window->ScrollTargetCenterRatio.y = 0.0f;
4971 }
4972
SetScrollFromPosY(float pos_y,float center_y_ratio)4973 void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio)
4974 {
4975 // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
4976 ImGuiWindow* window = GetCurrentWindow();
4977 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
4978 window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y);
4979 if (center_y_ratio <= 0.0f && window->ScrollTarget.y <= window->WindowPadding.y) // Minor hack to make "scroll to top" take account of WindowPadding, else it would scroll to (WindowPadding.y - ItemSpacing.y)
4980 window->ScrollTarget.y = 0.0f;
4981 window->ScrollTargetCenterRatio.y = center_y_ratio;
4982 }
4983
4984 // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
SetScrollHere(float center_y_ratio)4985 void ImGui::SetScrollHere(float center_y_ratio)
4986 {
4987 ImGuiWindow* window = GetCurrentWindow();
4988 float target_y = window->DC.CursorPosPrevLine.y + (window->DC.PrevLineHeight * center_y_ratio) + (GImGui->Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line.
4989 ImGui::SetScrollFromPosY(target_y - window->Pos.y, center_y_ratio);
4990 }
4991
SetKeyboardFocusHere(int offset)4992 void ImGui::SetKeyboardFocusHere(int offset)
4993 {
4994 ImGuiWindow* window = GetCurrentWindow();
4995 window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
4996 window->FocusIdxTabRequestNext = IM_INT_MAX;
4997 }
4998
SetStateStorage(ImGuiStorage * tree)4999 void ImGui::SetStateStorage(ImGuiStorage* tree)
5000 {
5001 ImGuiWindow* window = GetCurrentWindow();
5002 window->DC.StateStorage = tree ? tree : &window->StateStorage;
5003 }
5004
GetStateStorage()5005 ImGuiStorage* ImGui::GetStateStorage()
5006 {
5007 ImGuiWindow* window = GetCurrentWindowRead();
5008 return window->DC.StateStorage;
5009 }
5010
TextV(const char * fmt,va_list args)5011 void ImGui::TextV(const char* fmt, va_list args)
5012 {
5013 ImGuiWindow* window = GetCurrentWindow();
5014 if (window->SkipItems)
5015 return;
5016
5017 ImGuiState& g = *GImGui;
5018 const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5019 TextUnformatted(g.TempBuffer, text_end);
5020 }
5021
Text(const char * fmt,...)5022 void ImGui::Text(const char* fmt, ...)
5023 {
5024 va_list args;
5025 va_start(args, fmt);
5026 TextV(fmt, args);
5027 va_end(args);
5028 }
5029
TextColoredV(const ImVec4 & col,const char * fmt,va_list args)5030 void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args)
5031 {
5032 ImGui::PushStyleColor(ImGuiCol_Text, col);
5033 TextV(fmt, args);
5034 ImGui::PopStyleColor();
5035 }
5036
TextColored(const ImVec4 & col,const char * fmt,...)5037 void ImGui::TextColored(const ImVec4& col, const char* fmt, ...)
5038 {
5039 va_list args;
5040 va_start(args, fmt);
5041 TextColoredV(col, fmt, args);
5042 va_end(args);
5043 }
5044
TextDisabledV(const char * fmt,va_list args)5045 void ImGui::TextDisabledV(const char* fmt, va_list args)
5046 {
5047 ImGui::PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]);
5048 TextV(fmt, args);
5049 ImGui::PopStyleColor();
5050 }
5051
TextDisabled(const char * fmt,...)5052 void ImGui::TextDisabled(const char* fmt, ...)
5053 {
5054 va_list args;
5055 va_start(args, fmt);
5056 TextDisabledV(fmt, args);
5057 va_end(args);
5058 }
5059
TextWrappedV(const char * fmt,va_list args)5060 void ImGui::TextWrappedV(const char* fmt, va_list args)
5061 {
5062 ImGui::PushTextWrapPos(0.0f);
5063 TextV(fmt, args);
5064 ImGui::PopTextWrapPos();
5065 }
5066
TextWrapped(const char * fmt,...)5067 void ImGui::TextWrapped(const char* fmt, ...)
5068 {
5069 va_list args;
5070 va_start(args, fmt);
5071 TextWrappedV(fmt, args);
5072 va_end(args);
5073 }
5074
TextUnformatted(const char * text,const char * text_end)5075 void ImGui::TextUnformatted(const char* text, const char* text_end)
5076 {
5077 ImGuiWindow* window = GetCurrentWindow();
5078 if (window->SkipItems)
5079 return;
5080
5081 ImGuiState& g = *GImGui;
5082 IM_ASSERT(text != NULL);
5083 const char* text_begin = text;
5084 if (text_end == NULL)
5085 text_end = text + strlen(text); // FIXME-OPT
5086
5087 const float wrap_pos_x = window->DC.TextWrapPos;
5088 const bool wrap_enabled = wrap_pos_x >= 0.0f;
5089 if (text_end - text > 2000 && !wrap_enabled)
5090 {
5091 // Long text!
5092 // Perform manual coarse clipping to optimize for long multi-line text
5093 // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.
5094 // We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line.
5095 const char* line = text;
5096 const float line_height = ImGui::GetTextLineHeight();
5097 const ImVec2 text_pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrentLineTextBaseOffset);
5098 const ImRect clip_rect = window->ClipRect;
5099 ImVec2 text_size(0,0);
5100
5101 if (text_pos.y <= clip_rect.Max.y)
5102 {
5103 ImVec2 pos = text_pos;
5104
5105 // Lines to skip (can't skip when logging text)
5106 if (!g.LogEnabled)
5107 {
5108 int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height);
5109 if (lines_skippable > 0)
5110 {
5111 int lines_skipped = 0;
5112 while (line < text_end && lines_skipped < lines_skippable)
5113 {
5114 const char* line_end = strchr(line, '\n');
5115 if (!line_end)
5116 line_end = text_end;
5117 line = line_end + 1;
5118 lines_skipped++;
5119 }
5120 pos.y += lines_skipped * line_height;
5121 }
5122 }
5123
5124 // Lines to render
5125 if (line < text_end)
5126 {
5127 ImRect line_rect(pos, pos + ImVec2(ImGui::GetWindowWidth(), line_height));
5128 while (line < text_end)
5129 {
5130 const char* line_end = strchr(line, '\n');
5131 if (IsClippedEx(line_rect, NULL, false))
5132 break;
5133
5134 const ImVec2 line_size = CalcTextSize(line, line_end, false);
5135 text_size.x = ImMax(text_size.x, line_size.x);
5136 RenderText(pos, line, line_end, false);
5137 if (!line_end)
5138 line_end = text_end;
5139 line = line_end + 1;
5140 line_rect.Min.y += line_height;
5141 line_rect.Max.y += line_height;
5142 pos.y += line_height;
5143 }
5144
5145 // Count remaining lines
5146 int lines_skipped = 0;
5147 while (line < text_end)
5148 {
5149 const char* line_end = strchr(line, '\n');
5150 if (!line_end)
5151 line_end = text_end;
5152 line = line_end + 1;
5153 lines_skipped++;
5154 }
5155 pos.y += lines_skipped * line_height;
5156 }
5157
5158 text_size.y += (pos - text_pos).y;
5159 }
5160
5161 ImRect bb(text_pos, text_pos + text_size);
5162 ItemSize(bb);
5163 ItemAdd(bb, NULL);
5164 }
5165 else
5166 {
5167 const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;
5168 const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);
5169
5170 // Account of baseline offset
5171 ImVec2 text_pos = window->DC.CursorPos;
5172 text_pos.y += window->DC.CurrentLineTextBaseOffset;
5173
5174 ImRect bb(text_pos, text_pos + text_size);
5175 ItemSize(text_size);
5176 if (!ItemAdd(bb, NULL))
5177 return;
5178
5179 // Render (we don't hide text after ## in this end-user function)
5180 RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width);
5181 }
5182 }
5183
AlignFirstTextHeightToWidgets()5184 void ImGui::AlignFirstTextHeightToWidgets()
5185 {
5186 ImGuiWindow* window = GetCurrentWindow();
5187 if (window->SkipItems)
5188 return;
5189
5190 // Declare a dummy item size to that upcoming items that are smaller will center-align on the newly expanded line height.
5191 ImGuiState& g = *GImGui;
5192 ItemSize(ImVec2(0, g.FontSize + g.Style.FramePadding.y*2), g.Style.FramePadding.y);
5193 ImGui::SameLine(0, 0);
5194 }
5195
5196 // Add a label+text combo aligned to other label+value widgets
LabelTextV(const char * label,const char * fmt,va_list args)5197 void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
5198 {
5199 ImGuiWindow* window = GetCurrentWindow();
5200 if (window->SkipItems)
5201 return;
5202
5203 ImGuiState& g = *GImGui;
5204 const ImGuiStyle& style = g.Style;
5205 const float w = CalcItemWidth();
5206
5207 const ImVec2 label_size = CalcTextSize(label, NULL, true);
5208 const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2));
5209 const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y*2) + label_size);
5210 ItemSize(total_bb, style.FramePadding.y);
5211 if (!ItemAdd(total_bb, NULL))
5212 return;
5213
5214 // Render
5215 const char* value_text_begin = &g.TempBuffer[0];
5216 const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5217 RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImGuiAlign_VCenter);
5218 if (label_size.x > 0.0f)
5219 RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
5220 }
5221
LabelText(const char * label,const char * fmt,...)5222 void ImGui::LabelText(const char* label, const char* fmt, ...)
5223 {
5224 va_list args;
5225 va_start(args, fmt);
5226 LabelTextV(label, fmt, args);
5227 va_end(args);
5228 }
5229
IsWindowContentHoverable(ImGuiWindow * window)5230 static inline bool IsWindowContentHoverable(ImGuiWindow* window)
5231 {
5232 // An active popup disable hovering on other windows (apart from its own children)
5233 ImGuiState& g = *GImGui;
5234 if (ImGuiWindow* focused_window = g.FocusedWindow)
5235 if (ImGuiWindow* focused_root_window = focused_window->RootWindow)
5236 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) != 0 && focused_root_window->WasActive && focused_root_window != window->RootWindow)
5237 return false;
5238
5239 return true;
5240 }
5241
ButtonBehavior(const ImRect & bb,ImGuiID id,bool * out_hovered,bool * out_held,ImGuiButtonFlags flags)5242 bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)
5243 {
5244 ImGuiState& g = *GImGui;
5245 ImGuiWindow* window = GetCurrentWindow();
5246
5247 if (flags & ImGuiButtonFlags_Disabled)
5248 {
5249 if (out_hovered) *out_hovered = false;
5250 if (out_held) *out_held = false;
5251 if (g.ActiveId == id) SetActiveID(0);
5252 return false;
5253 }
5254
5255 bool pressed = false;
5256 const bool hovered = IsHovered(bb, id, (flags & ImGuiButtonFlags_FlattenChilds) != 0);
5257 if (hovered)
5258 {
5259 SetHoveredID(id);
5260 if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
5261 {
5262 if (g.IO.MouseDoubleClicked[0] && (flags & ImGuiButtonFlags_PressedOnDoubleClick))
5263 {
5264 pressed = true;
5265 }
5266 else if (g.IO.MouseClicked[0])
5267 {
5268 if (flags & ImGuiButtonFlags_PressedOnClick)
5269 {
5270 pressed = true;
5271 SetActiveID(0);
5272 }
5273 else
5274 {
5275 SetActiveID(id, window);
5276 }
5277 FocusWindow(window);
5278 }
5279 else if (g.IO.MouseReleased[0] && (flags & ImGuiButtonFlags_PressedOnRelease))
5280 {
5281 pressed = true;
5282 SetActiveID(0);
5283 }
5284 else if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && ImGui::IsMouseClicked(0, true))
5285 {
5286 pressed = true;
5287 }
5288 }
5289 }
5290
5291 bool held = false;
5292 if (g.ActiveId == id)
5293 {
5294 if (g.IO.MouseDown[0])
5295 {
5296 held = true;
5297 }
5298 else
5299 {
5300 if (hovered)
5301 pressed = true;
5302 SetActiveID(0);
5303 }
5304 }
5305
5306 if (out_hovered) *out_hovered = hovered;
5307 if (out_held) *out_held = held;
5308
5309 return pressed;
5310 }
5311
ButtonEx(const char * label,const ImVec2 & size_arg,ImGuiButtonFlags flags)5312 bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags)
5313 {
5314 ImGuiWindow* window = GetCurrentWindow();
5315 if (window->SkipItems)
5316 return false;
5317
5318 ImGuiState& g = *GImGui;
5319 const ImGuiStyle& style = g.Style;
5320 const ImGuiID id = window->GetID(label);
5321 const ImVec2 label_size = CalcTextSize(label, NULL, true);
5322
5323 ImVec2 pos = window->DC.CursorPos;
5324 if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset)
5325 pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y;
5326 ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);
5327
5328 const ImRect bb(pos, pos + size);
5329 ItemSize(bb, style.FramePadding.y);
5330 if (!ItemAdd(bb, &id))
5331 return false;
5332
5333 if (window->DC.ButtonRepeat) flags |= ImGuiButtonFlags_Repeat;
5334 bool hovered, held;
5335 bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
5336
5337 // Render
5338 const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
5339 RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
5340 RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, ImGuiAlign_Center | ImGuiAlign_VCenter);
5341
5342 // Automatically close popups
5343 //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
5344 // ImGui::CloseCurrentPopup();
5345
5346 return pressed;
5347 }
5348
Button(const char * label,const ImVec2 & size_arg)5349 bool ImGui::Button(const char* label, const ImVec2& size_arg)
5350 {
5351 return ButtonEx(label, size_arg, 0);
5352 }
5353
5354 // Small buttons fits within text without additional vertical spacing.
SmallButton(const char * label)5355 bool ImGui::SmallButton(const char* label)
5356 {
5357 ImGuiState& g = *GImGui;
5358 float backup_padding_y = g.Style.FramePadding.y;
5359 g.Style.FramePadding.y = 0.0f;
5360 bool pressed = ButtonEx(label, ImVec2(0,0), ImGuiButtonFlags_AlignTextBaseLine);
5361 g.Style.FramePadding.y = backup_padding_y;
5362 return pressed;
5363 }
5364
5365 // Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack.
5366 // Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id)
InvisibleButton(const char * str_id,const ImVec2 & size_arg)5367 bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg)
5368 {
5369 ImGuiWindow* window = GetCurrentWindow();
5370 if (window->SkipItems)
5371 return false;
5372
5373 const ImGuiID id = window->GetID(str_id);
5374 ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f);
5375 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
5376 ItemSize(bb);
5377 if (!ItemAdd(bb, &id))
5378 return false;
5379
5380 bool hovered, held;
5381 bool pressed = ButtonBehavior(bb, id, &hovered, &held);
5382
5383 return pressed;
5384 }
5385
5386 // Upper-right button to close a window.
CloseWindowButton(bool * p_opened)5387 static bool CloseWindowButton(bool* p_opened)
5388 {
5389 ImGuiWindow* window = ImGui::GetCurrentWindow();
5390
5391 const ImGuiID id = window->GetID("#CLOSE");
5392 const float size = window->TitleBarHeight() - 4.0f;
5393 const ImRect bb(window->Rect().GetTR() + ImVec2(-2.0f-size,2.0f), window->Rect().GetTR() + ImVec2(-2.0f,2.0f+size));
5394
5395 bool hovered, held;
5396 bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held);
5397
5398 // Render
5399 const ImU32 col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton);
5400 const ImVec2 center = bb.GetCenter();
5401 window->DrawList->AddCircleFilled(center, ImMax(2.0f,size*0.5f), col, 16);
5402
5403 const float cross_extent = (size * 0.5f * 0.7071f) - 1.0f;
5404 if (hovered)
5405 {
5406 window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), ImGui::GetColorU32(ImGuiCol_Text));
5407 window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), ImGui::GetColorU32(ImGuiCol_Text));
5408 }
5409
5410 if (p_opened != NULL && pressed)
5411 *p_opened = false;
5412
5413 return pressed;
5414 }
5415
Image(ImTextureID user_texture_id,const ImVec2 & size,const ImVec2 & uv0,const ImVec2 & uv1,const ImVec4 & tint_col,const ImVec4 & border_col)5416 void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
5417 {
5418 ImGuiWindow* window = GetCurrentWindow();
5419 if (window->SkipItems)
5420 return;
5421
5422 ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
5423 if (border_col.w > 0.0f)
5424 bb.Max += ImVec2(2,2);
5425 ItemSize(bb);
5426 if (!ItemAdd(bb, NULL))
5427 return;
5428
5429 if (border_col.w > 0.0f)
5430 {
5431 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f);
5432 window->DrawList->AddImage(user_texture_id, bb.Min+ImVec2(1,1), bb.Max-ImVec2(1,1), uv0, uv1, GetColorU32(tint_col));
5433 }
5434 else
5435 {
5436 window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col));
5437 }
5438 }
5439
5440 // frame_padding < 0: uses FramePadding from style (default)
5441 // frame_padding = 0: no framing
5442 // frame_padding > 0: set framing size
5443 // The color used are the button colors.
ImageButton(ImTextureID user_texture_id,const ImVec2 & size,const ImVec2 & uv0,const ImVec2 & uv1,int frame_padding,const ImVec4 & bg_col,const ImVec4 & tint_col)5444 bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col)
5445 {
5446 ImGuiWindow* window = GetCurrentWindow();
5447 if (window->SkipItems)
5448 return false;
5449
5450 ImGuiState& g = *GImGui;
5451 const ImGuiStyle& style = g.Style;
5452
5453 // Default to using texture ID as ID. User can still push string/integer prefixes.
5454 // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV.
5455 ImGui::PushID((void *)user_texture_id);
5456 const ImGuiID id = window->GetID("#image");
5457 ImGui::PopID();
5458
5459 const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding;
5460 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding*2);
5461 const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size);
5462 ItemSize(bb);
5463 if (!ItemAdd(bb, &id))
5464 return false;
5465
5466 bool hovered, held;
5467 bool pressed = ButtonBehavior(bb, id, &hovered, &held);
5468
5469 // Render
5470 const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
5471 RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding));
5472 if (bg_col.w > 0.0f)
5473 window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col));
5474 window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col));
5475
5476 return pressed;
5477 }
5478
5479 // Start logging ImGui output to TTY
LogToTTY(int max_depth)5480 void ImGui::LogToTTY(int max_depth)
5481 {
5482 ImGuiState& g = *GImGui;
5483 if (g.LogEnabled)
5484 return;
5485 ImGuiWindow* window = GetCurrentWindowRead();
5486
5487 g.LogEnabled = true;
5488 g.LogFile = stdout;
5489 g.LogStartDepth = window->DC.TreeDepth;
5490 if (max_depth >= 0)
5491 g.LogAutoExpandMaxDepth = max_depth;
5492 }
5493
5494 // Start logging ImGui output to given file
LogToFile(int max_depth,const char * filename)5495 void ImGui::LogToFile(int max_depth, const char* filename)
5496 {
5497 ImGuiState& g = *GImGui;
5498 if (g.LogEnabled)
5499 return;
5500 ImGuiWindow* window = GetCurrentWindowRead();
5501
5502 if (!filename)
5503 {
5504 filename = g.IO.LogFilename;
5505 if (!filename)
5506 return;
5507 }
5508
5509 g.LogFile = fopen(filename, "ab");
5510 if (!g.LogFile)
5511 {
5512 IM_ASSERT(g.LogFile != NULL); // Consider this an error
5513 return;
5514 }
5515 g.LogEnabled = true;
5516 g.LogStartDepth = window->DC.TreeDepth;
5517 if (max_depth >= 0)
5518 g.LogAutoExpandMaxDepth = max_depth;
5519 }
5520
5521 // Start logging ImGui output to clipboard
LogToClipboard(int max_depth)5522 void ImGui::LogToClipboard(int max_depth)
5523 {
5524 ImGuiState& g = *GImGui;
5525 if (g.LogEnabled)
5526 return;
5527 ImGuiWindow* window = GetCurrentWindowRead();
5528
5529 g.LogEnabled = true;
5530 g.LogFile = NULL;
5531 g.LogStartDepth = window->DC.TreeDepth;
5532 if (max_depth >= 0)
5533 g.LogAutoExpandMaxDepth = max_depth;
5534 }
5535
LogFinish()5536 void ImGui::LogFinish()
5537 {
5538 ImGuiState& g = *GImGui;
5539 if (!g.LogEnabled)
5540 return;
5541
5542 ImGui::LogText(IM_NEWLINE);
5543 g.LogEnabled = false;
5544 if (g.LogFile != NULL)
5545 {
5546 if (g.LogFile == stdout)
5547 fflush(g.LogFile);
5548 else
5549 fclose(g.LogFile);
5550 g.LogFile = NULL;
5551 }
5552 if (g.LogClipboard->size() > 1)
5553 {
5554 if (g.IO.SetClipboardTextFn)
5555 g.IO.SetClipboardTextFn(g.LogClipboard->begin());
5556 g.LogClipboard->clear();
5557 }
5558 }
5559
5560 // Helper to display logging buttons
LogButtons()5561 void ImGui::LogButtons()
5562 {
5563 ImGuiState& g = *GImGui;
5564
5565 ImGui::PushID("LogButtons");
5566 const bool log_to_tty = ImGui::Button("Log To TTY");
5567 ImGui::SameLine();
5568 const bool log_to_file = ImGui::Button("Log To File");
5569 ImGui::SameLine();
5570 const bool log_to_clipboard = ImGui::Button("Log To Clipboard");
5571 ImGui::SameLine();
5572
5573 ImGui::PushItemWidth(80.0f);
5574 ImGui::PushAllowKeyboardFocus(false);
5575 ImGui::SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL);
5576 ImGui::PopAllowKeyboardFocus();
5577 ImGui::PopItemWidth();
5578 ImGui::PopID();
5579
5580 // Start logging at the end of the function so that the buttons don't appear in the log
5581 if (log_to_tty)
5582 LogToTTY(g.LogAutoExpandMaxDepth);
5583 if (log_to_file)
5584 LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename);
5585 if (log_to_clipboard)
5586 LogToClipboard(g.LogAutoExpandMaxDepth);
5587 }
5588
TreeNodeBehaviorIsOpened(ImGuiID id,ImGuiTreeNodeFlags flags)5589 bool ImGui::TreeNodeBehaviorIsOpened(ImGuiID id, ImGuiTreeNodeFlags flags)
5590 {
5591 // We only write to the tree storage if the user clicks (or explicitely use SetNextTreeNode*** functions)
5592 ImGuiState& g = *GImGui;
5593 ImGuiWindow* window = g.CurrentWindow;
5594 ImGuiStorage* storage = window->DC.StateStorage;
5595
5596 bool opened;
5597 if (g.SetNextTreeNodeOpenedCond != 0)
5598 {
5599 if (g.SetNextTreeNodeOpenedCond & ImGuiSetCond_Always)
5600 {
5601 opened = g.SetNextTreeNodeOpenedVal;
5602 storage->SetInt(id, opened);
5603 }
5604 else
5605 {
5606 // We treat ImGuiSetCondition_Once and ImGuiSetCondition_FirstUseEver the same because tree node state are not saved persistently.
5607 const int stored_value = storage->GetInt(id, -1);
5608 if (stored_value == -1)
5609 {
5610 opened = g.SetNextTreeNodeOpenedVal;
5611 storage->SetInt(id, opened);
5612 }
5613 else
5614 {
5615 opened = stored_value != 0;
5616 }
5617 }
5618 g.SetNextTreeNodeOpenedCond = 0;
5619 }
5620 else
5621 {
5622 opened = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;
5623 }
5624
5625 // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior).
5626 // NB- If we are above max depth we still allow manually opened nodes to be logged.
5627 if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoExpandOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth)
5628 opened = true;
5629
5630 return opened;
5631 }
5632
5633 // FIXME: Split into CollapsingHeader(label, default_open?) and TreeNodeBehavior(label), obsolete the 4 parameters function.
CollapsingHeader(const char * label,const char * str_id,bool display_frame,bool default_open)5634 bool ImGui::CollapsingHeader(const char* label, const char* str_id, bool display_frame, bool default_open)
5635 {
5636 ImGuiWindow* window = GetCurrentWindow();
5637 if (window->SkipItems)
5638 return false;
5639
5640 ImGuiState& g = *GImGui;
5641 const ImGuiStyle& style = g.Style;
5642 const ImVec2 padding = display_frame ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f);
5643
5644 IM_ASSERT(str_id != NULL || label != NULL);
5645 if (str_id == NULL)
5646 str_id = label;
5647 if (label == NULL)
5648 label = str_id;
5649 const bool label_hide_text_after_double_hash = (label == str_id); // Only search and hide text after ## if we have passed label and ID separately, otherwise allow "##" within format string.
5650 const ImGuiID id = window->GetID(str_id);
5651 const ImVec2 label_size = CalcTextSize(label, NULL, label_hide_text_after_double_hash);
5652
5653 // We vertically grow up to current line height up the typical widget height.
5654 const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset - padding.y); // Latch before ItemSize changes it
5655 const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), label_size.y + padding.y*2);
5656 ImRect bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height));
5657 if (display_frame)
5658 {
5659 // Framed header expand a little outside the default padding
5660 bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1;
5661 bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1;
5662 }
5663
5664 const float collapser_width = g.FontSize + (display_frame ? padding.x*2 : padding.x);
5665 const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f); // Include collapser
5666 ItemSize(ImVec2(text_width, frame_height), text_base_offset_y);
5667
5668 // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
5669 // (Ideally we'd want to add a flag for the user to specify we want want the hit test to be done up to the right side of the content or not)
5670 const ImRect interact_bb = display_frame ? bb : ImRect(bb.Min.x, bb.Min.y, bb.Min.x + text_width + style.ItemSpacing.x*2, bb.Max.y);
5671 bool opened = TreeNodeBehaviorIsOpened(id, (default_open ? ImGuiTreeNodeFlags_DefaultOpen : 0) | (display_frame ? ImGuiTreeNodeFlags_NoAutoExpandOnLog : 0));
5672 if (!ItemAdd(interact_bb, &id))
5673 return opened;
5674
5675 bool hovered, held;
5676 bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, ImGuiButtonFlags_NoKeyModifiers);
5677 if (pressed)
5678 {
5679 opened = !opened;
5680 window->DC.StateStorage->SetInt(id, opened);
5681 }
5682
5683 // Render
5684 const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
5685 const ImVec2 text_pos = bb.Min + padding + ImVec2(collapser_width, text_base_offset_y);
5686 if (display_frame)
5687 {
5688 // Framed type
5689 RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
5690 RenderCollapseTriangle(bb.Min + padding + ImVec2(0.0f, text_base_offset_y), opened, 1.0f, true);
5691 if (g.LogEnabled)
5692 {
5693 // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here.
5694 const char log_prefix[] = "\n##";
5695 const char log_suffix[] = "##";
5696 LogRenderedText(text_pos, log_prefix, log_prefix+3);
5697 RenderTextClipped(text_pos, bb.Max, label, NULL, &label_size);
5698 LogRenderedText(text_pos, log_suffix+1, log_suffix+3);
5699 }
5700 else
5701 {
5702 RenderTextClipped(text_pos, bb.Max, label, NULL, &label_size);
5703 }
5704 }
5705 else
5706 {
5707 // Unframed typed for tree nodes
5708 if (hovered)
5709 RenderFrame(bb.Min, bb.Max, col, false);
5710
5711 RenderCollapseTriangle(bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), opened, 0.70f, false);
5712 if (g.LogEnabled)
5713 LogRenderedText(text_pos, ">");
5714 RenderText(text_pos, label, NULL, label_hide_text_after_double_hash);
5715 }
5716
5717 return opened;
5718 }
5719
Bullet()5720 void ImGui::Bullet()
5721 {
5722 ImGuiWindow* window = GetCurrentWindow();
5723 if (window->SkipItems)
5724 return;
5725
5726 ImGuiState& g = *GImGui;
5727 const ImGuiStyle& style = g.Style;
5728 const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
5729 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height));
5730 ItemSize(bb);
5731 if (!ItemAdd(bb, NULL))
5732 {
5733 ImGui::SameLine(0, style.FramePadding.x*2);
5734 return;
5735 }
5736
5737 // Render
5738 const float bullet_size = g.FontSize*0.15f;
5739 window->DrawList->AddCircleFilled(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f), bullet_size, GetColorU32(ImGuiCol_Text));
5740
5741 // Stay on same line
5742 ImGui::SameLine(0, style.FramePadding.x*2);
5743 }
5744
5745 // Text with a little bullet aligned to the typical tree node.
BulletTextV(const char * fmt,va_list args)5746 void ImGui::BulletTextV(const char* fmt, va_list args)
5747 {
5748 ImGuiWindow* window = GetCurrentWindow();
5749 if (window->SkipItems)
5750 return;
5751
5752 ImGuiState& g = *GImGui;
5753 const ImGuiStyle& style = g.Style;
5754
5755 const char* text_begin = g.TempBuffer;
5756 const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5757 const ImVec2 label_size = CalcTextSize(text_begin, text_end, true);
5758 const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
5759 const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
5760 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x*2) : 0.0f), ImMax(line_height, label_size.y))); // Empty text doesn't add padding
5761 ItemSize(bb);
5762 if (!ItemAdd(bb, NULL))
5763 return;
5764
5765 // Render
5766 const float bullet_size = g.FontSize*0.15f;
5767 window->DrawList->AddCircleFilled(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f), bullet_size, GetColorU32(ImGuiCol_Text));
5768 RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end);
5769 }
5770
BulletText(const char * fmt,...)5771 void ImGui::BulletText(const char* fmt, ...)
5772 {
5773 va_list args;
5774 va_start(args, fmt);
5775 BulletTextV(fmt, args);
5776 va_end(args);
5777 }
5778
5779 // If returning 'true' the node is open and the user is responsible for calling TreePop
TreeNodeV(const char * str_id,const char * fmt,va_list args)5780 bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)
5781 {
5782 ImGuiWindow* window = GetCurrentWindow();
5783 if (window->SkipItems)
5784 return false;
5785
5786 ImGuiState& g = *GImGui;
5787 ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5788 if (!str_id || !str_id[0])
5789 str_id = fmt;
5790
5791 ImGui::PushID(str_id);
5792 const bool opened = ImGui::CollapsingHeader(g.TempBuffer, "", false);
5793 ImGui::PopID();
5794
5795 if (opened)
5796 ImGui::TreePush(str_id);
5797
5798 return opened;
5799 }
5800
TreeNode(const char * str_id,const char * fmt,...)5801 bool ImGui::TreeNode(const char* str_id, const char* fmt, ...)
5802 {
5803 va_list args;
5804 va_start(args, fmt);
5805 bool s = TreeNodeV(str_id, fmt, args);
5806 va_end(args);
5807 return s;
5808 }
5809
5810 // If returning 'true' the node is open and the user is responsible for calling TreePop
TreeNodeV(const void * ptr_id,const char * fmt,va_list args)5811 bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)
5812 {
5813 ImGuiWindow* window = GetCurrentWindow();
5814 if (window->SkipItems)
5815 return false;
5816
5817 ImGuiState& g = *GImGui;
5818 ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
5819
5820 if (!ptr_id)
5821 ptr_id = fmt;
5822
5823 ImGui::PushID(ptr_id);
5824 const bool opened = ImGui::CollapsingHeader(g.TempBuffer, "", false);
5825 ImGui::PopID();
5826
5827 if (opened)
5828 ImGui::TreePush(ptr_id);
5829
5830 return opened;
5831 }
5832
TreeNode(const void * ptr_id,const char * fmt,...)5833 bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)
5834 {
5835 va_list args;
5836 va_start(args, fmt);
5837 bool s = TreeNodeV(ptr_id, fmt, args);
5838 va_end(args);
5839 return s;
5840 }
5841
TreeNode(const char * str_label_id)5842 bool ImGui::TreeNode(const char* str_label_id)
5843 {
5844 return TreeNode(str_label_id, "%s", str_label_id);
5845 }
5846
SetNextTreeNodeOpened(bool opened,ImGuiSetCond cond)5847 void ImGui::SetNextTreeNodeOpened(bool opened, ImGuiSetCond cond)
5848 {
5849 ImGuiState& g = *GImGui;
5850 g.SetNextTreeNodeOpenedVal = opened;
5851 g.SetNextTreeNodeOpenedCond = cond ? cond : ImGuiSetCond_Always;
5852 }
5853
PushID(const char * str_id)5854 void ImGui::PushID(const char* str_id)
5855 {
5856 ImGuiWindow* window = GetCurrentWindow();
5857 window->IDStack.push_back(window->GetID(str_id));
5858 }
5859
PushID(const char * str_id_begin,const char * str_id_end)5860 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
5861 {
5862 ImGuiWindow* window = GetCurrentWindow();
5863 window->IDStack.push_back(window->GetID(str_id_begin, str_id_end));
5864 }
5865
PushID(const void * ptr_id)5866 void ImGui::PushID(const void* ptr_id)
5867 {
5868 ImGuiWindow* window = GetCurrentWindow();
5869 window->IDStack.push_back(window->GetID(ptr_id));
5870 }
5871
PushID(int int_id)5872 void ImGui::PushID(int int_id)
5873 {
5874 const void* ptr_id = (void*)(intptr_t)int_id;
5875 ImGuiWindow* window = GetCurrentWindow();
5876 window->IDStack.push_back(window->GetID(ptr_id));
5877 }
5878
PopID()5879 void ImGui::PopID()
5880 {
5881 ImGuiWindow* window = GetCurrentWindow();
5882 window->IDStack.pop_back();
5883 }
5884
GetID(const char * str_id)5885 ImGuiID ImGui::GetID(const char* str_id)
5886 {
5887 return GImGui->CurrentWindow->GetID(str_id);
5888 }
5889
GetID(const char * str_id_begin,const char * str_id_end)5890 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
5891 {
5892 return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end);
5893 }
5894
GetID(const void * ptr_id)5895 ImGuiID ImGui::GetID(const void* ptr_id)
5896 {
5897 return GImGui->CurrentWindow->GetID(ptr_id);
5898 }
5899
DataTypeFormatString(ImGuiDataType data_type,void * data_ptr,const char * display_format,char * buf,int buf_size)5900 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size)
5901 {
5902 if (data_type == ImGuiDataType_Int)
5903 ImFormatString(buf, buf_size, display_format, *(int*)data_ptr);
5904 else if (data_type == ImGuiDataType_Float)
5905 ImFormatString(buf, buf_size, display_format, *(float*)data_ptr);
5906 }
5907
DataTypeFormatString(ImGuiDataType data_type,void * data_ptr,int decimal_precision,char * buf,int buf_size)5908 static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size)
5909 {
5910 if (data_type == ImGuiDataType_Int)
5911 {
5912 if (decimal_precision < 0)
5913 ImFormatString(buf, buf_size, "%d", *(int*)data_ptr);
5914 else
5915 ImFormatString(buf, buf_size, "%.*d", decimal_precision, *(int*)data_ptr);
5916 }
5917 else if (data_type == ImGuiDataType_Float)
5918 {
5919 if (decimal_precision < 0)
5920 ImFormatString(buf, buf_size, "%f", *(float*)data_ptr); // Ideally we'd have a minimum decimal precision of 1 to visually denote that it is a float, while hiding non-significant digits?
5921 else
5922 ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(float*)data_ptr);
5923 }
5924 }
5925
DataTypeApplyOp(ImGuiDataType data_type,int op,void * value1,const void * value2)5926 static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2)// Store into value1
5927 {
5928 if (data_type == ImGuiDataType_Int)
5929 {
5930 if (op == '+')
5931 *(int*)value1 = *(int*)value1 + *(const int*)value2;
5932 else if (op == '-')
5933 *(int*)value1 = *(int*)value1 - *(const int*)value2;
5934 }
5935 else if (data_type == ImGuiDataType_Float)
5936 {
5937 if (op == '+')
5938 *(float*)value1 = *(float*)value1 + *(const float*)value2;
5939 else if (op == '-')
5940 *(float*)value1 = *(float*)value1 - *(const float*)value2;
5941 }
5942 }
5943
5944 // User can input math operators (e.g. +100) to edit a numerical values.
DataTypeApplyOpFromText(const char * buf,const char * initial_value_buf,ImGuiDataType data_type,void * data_ptr,const char * scalar_format)5945 static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format)
5946 {
5947 while (ImCharIsSpace(*buf))
5948 buf++;
5949
5950 // We don't support '-' op because it would conflict with inputing negative value.
5951 // Instead you can use +-100 to subtract from an existing value
5952 char op = buf[0];
5953 if (op == '+' || op == '*' || op == '/')
5954 {
5955 buf++;
5956 while (ImCharIsSpace(*buf))
5957 buf++;
5958 }
5959 else
5960 {
5961 op = 0;
5962 }
5963 if (!buf[0])
5964 return false;
5965
5966 if (data_type == ImGuiDataType_Int)
5967 {
5968 if (!scalar_format)
5969 scalar_format = "%d";
5970 int* v = (int*)data_ptr;
5971 const int old_v = *v;
5972 int arg0 = *v;
5973 if (op && sscanf(initial_value_buf, scalar_format, &arg0) < 1)
5974 return false;
5975
5976 // Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision
5977 float arg1 = 0.0f;
5978 if (op == '+') { if (sscanf(buf, "%f", &arg1) == 1) *v = (int)(arg0 + arg1); } // Add (use "+-" to subtract)
5979 else if (op == '*') { if (sscanf(buf, "%f", &arg1) == 1) *v = (int)(arg0 * arg1); } // Multiply
5980 else if (op == '/') { if (sscanf(buf, "%f", &arg1) == 1 && arg1 != 0.0f) *v = (int)(arg0 / arg1); }// Divide
5981 else { if (sscanf(buf, scalar_format, &arg0) == 1) *v = arg0; } // Assign constant
5982 return (old_v != *v);
5983 }
5984 else if (data_type == ImGuiDataType_Float)
5985 {
5986 // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in
5987 scalar_format = "%f";
5988 float* v = (float*)data_ptr;
5989 const float old_v = *v;
5990 float arg0 = *v;
5991 if (op && sscanf(initial_value_buf, scalar_format, &arg0) < 1)
5992 return false;
5993
5994 float arg1 = 0.0f;
5995 if (sscanf(buf, scalar_format, &arg1) < 1)
5996 return false;
5997 if (op == '+') { *v = arg0 + arg1; } // Add (use "+-" to subtract)
5998 else if (op == '*') { *v = arg0 * arg1; } // Multiply
5999 else if (op == '/') { if (arg1 != 0.0f) *v = arg0 / arg1; } // Divide
6000 else { *v = arg1; } // Assign constant
6001 return (old_v != *v);
6002 }
6003
6004 return false;
6005 }
6006
6007 // Create text input in place of a slider (when CTRL+Clicking on slider)
InputScalarAsWidgetReplacement(const ImRect & aabb,const char * label,ImGuiDataType data_type,void * data_ptr,ImGuiID id,int decimal_precision)6008 bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision)
6009 {
6010 ImGuiState& g = *GImGui;
6011 ImGuiWindow* window = GetCurrentWindow();
6012
6013 // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen)
6014 SetActiveID(g.ScalarAsInputTextId, window);
6015 SetHoveredID(0);
6016 FocusableItemUnregister(window);
6017
6018 char buf[32];
6019 DataTypeFormatString(data_type, data_ptr, decimal_precision, buf, IM_ARRAYSIZE(buf));
6020 bool text_value_changed = InputTextEx(label, buf, IM_ARRAYSIZE(buf), aabb.GetSize(), ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll);
6021 if (g.ScalarAsInputTextId == 0)
6022 {
6023 // First frame
6024 IM_ASSERT(g.ActiveId == id); // InputText ID expected to match the Slider ID (else we'd need to store them both, which is also possible)
6025 g.ScalarAsInputTextId = g.ActiveId;
6026 SetHoveredID(id);
6027 }
6028 else if (g.ActiveId != g.ScalarAsInputTextId)
6029 {
6030 // Release
6031 g.ScalarAsInputTextId = 0;
6032 }
6033 if (text_value_changed)
6034 return DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, NULL);
6035 return false;
6036 }
6037
6038 // Parse display precision back from the display format string
ParseFormatPrecision(const char * fmt,int default_precision)6039 int ImGui::ParseFormatPrecision(const char* fmt, int default_precision)
6040 {
6041 int precision = default_precision;
6042 while ((fmt = strchr(fmt, '%')) != NULL)
6043 {
6044 fmt++;
6045 if (fmt[0] == '%') { fmt++; continue; } // Ignore "%%"
6046 while (*fmt >= '0' && *fmt <= '9')
6047 fmt++;
6048 if (*fmt == '.')
6049 {
6050 precision = atoi(fmt + 1);
6051 if (precision < 0 || precision > 10)
6052 precision = default_precision;
6053 }
6054 break;
6055 }
6056 return precision;
6057 }
6058
RoundScalar(float value,int decimal_precision)6059 float ImGui::RoundScalar(float value, int decimal_precision)
6060 {
6061 // Round past decimal precision
6062 // So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0
6063 // FIXME: Investigate better rounding methods
6064 static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f };
6065 float min_step = (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision);
6066 bool negative = value < 0.0f;
6067 value = fabsf(value);
6068 float remainder = fmodf(value, min_step);
6069 if (remainder <= min_step*0.5f)
6070 value -= remainder;
6071 else
6072 value += (min_step - remainder);
6073 return negative ? -value : value;
6074 }
6075
SliderBehavior(const ImRect & frame_bb,ImGuiID id,float * v,float v_min,float v_max,float power,int decimal_precision,ImGuiSliderFlags flags)6076 bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags)
6077 {
6078 ImGuiState& g = *GImGui;
6079 ImGuiWindow* window = GetCurrentWindow();
6080 const ImGuiStyle& style = g.Style;
6081
6082 // Draw frame
6083 RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
6084
6085 const bool is_non_linear = fabsf(power - 1.0f) > 0.0001f;
6086 const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0;
6087
6088 const float grab_padding = 2.0f;
6089 const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f);
6090 float grab_sz;
6091 if (decimal_precision > 0)
6092 grab_sz = ImMin(style.GrabMinSize, slider_sz);
6093 else
6094 grab_sz = ImMin(ImMax(1.0f * (slider_sz / (v_max-v_min+1.0f)), style.GrabMinSize), slider_sz); // Integer sliders, if possible have the grab size represent 1 unit
6095 const float slider_usable_sz = slider_sz - grab_sz;
6096 const float slider_usable_pos_min = (is_horizontal ? frame_bb.Min.x : frame_bb.Min.y) + grab_padding + grab_sz*0.5f;
6097 const float slider_usable_pos_max = (is_horizontal ? frame_bb.Max.x : frame_bb.Max.y) - grab_padding - grab_sz*0.5f;
6098
6099 // For logarithmic sliders that cross over sign boundary we want the exponential increase to be symmetric around 0.0f
6100 float linear_zero_pos = 0.0f; // 0.0->1.0f
6101 if (v_min * v_max < 0.0f)
6102 {
6103 // Different sign
6104 const float linear_dist_min_to_0 = powf(fabsf(0.0f - v_min), 1.0f/power);
6105 const float linear_dist_max_to_0 = powf(fabsf(v_max - 0.0f), 1.0f/power);
6106 linear_zero_pos = linear_dist_min_to_0 / (linear_dist_min_to_0+linear_dist_max_to_0);
6107 }
6108 else
6109 {
6110 // Same sign
6111 linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f;
6112 }
6113
6114 // Process clicking on the slider
6115 bool value_changed = false;
6116 if (g.ActiveId == id)
6117 {
6118 if (g.IO.MouseDown[0])
6119 {
6120 const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
6121 float normalized_pos = ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f);
6122 if (!is_horizontal)
6123 normalized_pos = 1.0f - normalized_pos;
6124
6125 float new_value;
6126 if (is_non_linear)
6127 {
6128 // Account for logarithmic scale on both sides of the zero
6129 if (normalized_pos < linear_zero_pos)
6130 {
6131 // Negative: rescale to the negative range before powering
6132 float a = 1.0f - (normalized_pos / linear_zero_pos);
6133 a = powf(a, power);
6134 new_value = ImLerp(ImMin(v_max,0.0f), v_min, a);
6135 }
6136 else
6137 {
6138 // Positive: rescale to the positive range before powering
6139 float a;
6140 if (fabsf(linear_zero_pos - 1.0f) > 1.e-6)
6141 a = (normalized_pos - linear_zero_pos) / (1.0f - linear_zero_pos);
6142 else
6143 a = normalized_pos;
6144 a = powf(a, power);
6145 new_value = ImLerp(ImMax(v_min,0.0f), v_max, a);
6146 }
6147 }
6148 else
6149 {
6150 // Linear slider
6151 new_value = ImLerp(v_min, v_max, normalized_pos);
6152 }
6153
6154 // Round past decimal precision
6155 new_value = RoundScalar(new_value, decimal_precision);
6156 if (*v != new_value)
6157 {
6158 *v = new_value;
6159 value_changed = true;
6160 }
6161 }
6162 else
6163 {
6164 SetActiveID(0);
6165 }
6166 }
6167
6168 // Calculate slider grab positioning
6169 float grab_t;
6170 if (is_non_linear)
6171 {
6172 float v_clamped = ImClamp(*v, v_min, v_max);
6173 if (v_clamped < 0.0f)
6174 {
6175 const float f = 1.0f - (v_clamped - v_min) / (ImMin(0.0f,v_max) - v_min);
6176 grab_t = (1.0f - powf(f, 1.0f/power)) * linear_zero_pos;
6177 }
6178 else
6179 {
6180 const float f = (v_clamped - ImMax(0.0f,v_min)) / (v_max - ImMax(0.0f,v_min));
6181 grab_t = linear_zero_pos + powf(f, 1.0f/power) * (1.0f - linear_zero_pos);
6182 }
6183 }
6184 else
6185 {
6186 // Linear slider
6187 grab_t = (ImClamp(*v, v_min, v_max) - v_min) / (v_max - v_min);
6188 }
6189
6190 // Draw
6191 if (!is_horizontal)
6192 grab_t = 1.0f - grab_t;
6193 const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
6194 ImRect grab_bb;
6195 if (is_horizontal)
6196 grab_bb = ImRect(ImVec2(grab_pos - grab_sz*0.5f, frame_bb.Min.y + grab_padding), ImVec2(grab_pos + grab_sz*0.5f, frame_bb.Max.y - grab_padding));
6197 else
6198 grab_bb = ImRect(ImVec2(frame_bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f), ImVec2(frame_bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f));
6199 window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
6200
6201 return value_changed;
6202 }
6203
6204 // Use power!=1.0 for logarithmic sliders.
6205 // Adjust display_format to decorate the value with a prefix or a suffix.
6206 // "%.3f" 1.234
6207 // "%5.2f secs" 01.23 secs
6208 // "Gold: %.0f" Gold: 1
SliderFloat(const char * label,float * v,float v_min,float v_max,const char * display_format,float power)6209 bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format, float power)
6210 {
6211 ImGuiWindow* window = GetCurrentWindow();
6212 if (window->SkipItems)
6213 return false;
6214
6215 ImGuiState& g = *GImGui;
6216 const ImGuiStyle& style = g.Style;
6217 const ImGuiID id = window->GetID(label);
6218 const float w = CalcItemWidth();
6219
6220 const ImVec2 label_size = CalcTextSize(label, NULL, true);
6221 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
6222 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
6223
6224 // NB- we don't call ItemSize() yet because we may turn into a text edit box below
6225 if (!ItemAdd(total_bb, &id))
6226 {
6227 ItemSize(total_bb, style.FramePadding.y);
6228 return false;
6229 }
6230
6231 const bool hovered = IsHovered(frame_bb, id);
6232 if (hovered)
6233 SetHoveredID(id);
6234
6235 if (!display_format)
6236 display_format = "%.3f";
6237 int decimal_precision = ParseFormatPrecision(display_format, 3);
6238
6239 // Tabbing or CTRL-clicking on Slider turns it into an input box
6240 bool start_text_input = false;
6241 const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id);
6242 if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]))
6243 {
6244 SetActiveID(id, window);
6245 FocusWindow(window);
6246
6247 if (tab_focus_requested || g.IO.KeyCtrl)
6248 {
6249 start_text_input = true;
6250 g.ScalarAsInputTextId = 0;
6251 }
6252 }
6253 if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
6254 return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
6255
6256 ItemSize(total_bb, style.FramePadding.y);
6257
6258 // Actual slider behavior + render grab
6259 const bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision);
6260
6261 // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
6262 char value_buf[64];
6263 const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
6264 RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImGuiAlign_Center|ImGuiAlign_VCenter);
6265
6266 if (label_size.x > 0.0f)
6267 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
6268
6269 return value_changed;
6270 }
6271
VSliderFloat(const char * label,const ImVec2 & size,float * v,float v_min,float v_max,const char * display_format,float power)6272 bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format, float power)
6273 {
6274 ImGuiWindow* window = GetCurrentWindow();
6275 if (window->SkipItems)
6276 return false;
6277
6278 ImGuiState& g = *GImGui;
6279 const ImGuiStyle& style = g.Style;
6280 const ImGuiID id = window->GetID(label);
6281
6282 const ImVec2 label_size = CalcTextSize(label, NULL, true);
6283 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
6284 const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
6285
6286 ItemSize(bb, style.FramePadding.y);
6287 if (!ItemAdd(frame_bb, &id))
6288 return false;
6289
6290 const bool hovered = IsHovered(frame_bb, id);
6291 if (hovered)
6292 SetHoveredID(id);
6293
6294 if (!display_format)
6295 display_format = "%.3f";
6296 int decimal_precision = ParseFormatPrecision(display_format, 3);
6297
6298 if (hovered && g.IO.MouseClicked[0])
6299 {
6300 SetActiveID(id, window);
6301 FocusWindow(window);
6302 }
6303
6304 // Actual slider behavior + render grab
6305 bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision, ImGuiSliderFlags_Vertical);
6306
6307 // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
6308 // For the vertical slider we allow centered text to overlap the frame padding
6309 char value_buf[64];
6310 char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
6311 RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImGuiAlign_Center);
6312 if (label_size.x > 0.0f)
6313 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
6314
6315 return value_changed;
6316 }
6317
SliderAngle(const char * label,float * v_rad,float v_degrees_min,float v_degrees_max)6318 bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max)
6319 {
6320 float v_deg = (*v_rad) * 360.0f / (2*IM_PI);
6321 bool value_changed = ImGui::SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f);
6322 *v_rad = v_deg * (2*IM_PI) / 360.0f;
6323 return value_changed;
6324 }
6325
SliderInt(const char * label,int * v,int v_min,int v_max,const char * display_format)6326 bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format)
6327 {
6328 if (!display_format)
6329 display_format = "%.0f";
6330 float v_f = (float)*v;
6331 bool value_changed = ImGui::SliderFloat(label, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
6332 *v = (int)v_f;
6333 return value_changed;
6334 }
6335
VSliderInt(const char * label,const ImVec2 & size,int * v,int v_min,int v_max,const char * display_format)6336 bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format)
6337 {
6338 if (!display_format)
6339 display_format = "%.0f";
6340 float v_f = (float)*v;
6341 bool value_changed = ImGui::VSliderFloat(label, size, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
6342 *v = (int)v_f;
6343 return value_changed;
6344 }
6345
6346 // Add multiple sliders on 1 line for compact edition of multiple components
SliderFloatN(const char * label,float * v,int components,float v_min,float v_max,const char * display_format,float power)6347 bool ImGui::SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power)
6348 {
6349 ImGuiWindow* window = GetCurrentWindow();
6350 if (window->SkipItems)
6351 return false;
6352
6353 ImGuiState& g = *GImGui;
6354 bool value_changed = false;
6355 ImGui::BeginGroup();
6356 ImGui::PushID(label);
6357 PushMultiItemsWidths(components);
6358 for (int i = 0; i < components; i++)
6359 {
6360 ImGui::PushID(i);
6361 value_changed |= ImGui::SliderFloat("##v", &v[i], v_min, v_max, display_format, power);
6362 ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
6363 ImGui::PopID();
6364 ImGui::PopItemWidth();
6365 }
6366 ImGui::PopID();
6367
6368 ImGui::TextUnformatted(label, FindRenderedTextEnd(label));
6369 ImGui::EndGroup();
6370
6371 return value_changed;
6372 }
6373
SliderFloat2(const char * label,float v[2],float v_min,float v_max,const char * display_format,float power)6374 bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format, float power)
6375 {
6376 return SliderFloatN(label, v, 2, v_min, v_max, display_format, power);
6377 }
6378
SliderFloat3(const char * label,float v[3],float v_min,float v_max,const char * display_format,float power)6379 bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format, float power)
6380 {
6381 return SliderFloatN(label, v, 3, v_min, v_max, display_format, power);
6382 }
6383
SliderFloat4(const char * label,float v[4],float v_min,float v_max,const char * display_format,float power)6384 bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format, float power)
6385 {
6386 return SliderFloatN(label, v, 4, v_min, v_max, display_format, power);
6387 }
6388
SliderIntN(const char * label,int * v,int components,int v_min,int v_max,const char * display_format)6389 bool ImGui::SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format)
6390 {
6391 ImGuiWindow* window = GetCurrentWindow();
6392 if (window->SkipItems)
6393 return false;
6394
6395 ImGuiState& g = *GImGui;
6396 bool value_changed = false;
6397 ImGui::BeginGroup();
6398 ImGui::PushID(label);
6399 PushMultiItemsWidths(components);
6400 for (int i = 0; i < components; i++)
6401 {
6402 ImGui::PushID(i);
6403 value_changed |= ImGui::SliderInt("##v", &v[i], v_min, v_max, display_format);
6404 ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
6405 ImGui::PopID();
6406 ImGui::PopItemWidth();
6407 }
6408 ImGui::PopID();
6409
6410 ImGui::TextUnformatted(label, FindRenderedTextEnd(label));
6411 ImGui::EndGroup();
6412
6413 return value_changed;
6414 }
6415
SliderInt2(const char * label,int v[2],int v_min,int v_max,const char * display_format)6416 bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format)
6417 {
6418 return SliderIntN(label, v, 2, v_min, v_max, display_format);
6419 }
6420
SliderInt3(const char * label,int v[3],int v_min,int v_max,const char * display_format)6421 bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format)
6422 {
6423 return SliderIntN(label, v, 3, v_min, v_max, display_format);
6424 }
6425
SliderInt4(const char * label,int v[4],int v_min,int v_max,const char * display_format)6426 bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format)
6427 {
6428 return SliderIntN(label, v, 4, v_min, v_max, display_format);
6429 }
6430
DragBehavior(const ImRect & frame_bb,ImGuiID id,float * v,float v_speed,float v_min,float v_max,int decimal_precision,float power)6431 bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power)
6432 {
6433 ImGuiState& g = *GImGui;
6434 const ImGuiStyle& style = g.Style;
6435
6436 // Draw frame
6437 const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
6438 RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
6439
6440 bool value_changed = false;
6441
6442 // Process clicking on the drag
6443 if (g.ActiveId == id)
6444 {
6445 if (g.IO.MouseDown[0])
6446 {
6447 if (g.ActiveIdIsJustActivated)
6448 {
6449 // Lock current value on click
6450 g.DragCurrentValue = *v;
6451 g.DragLastMouseDelta = ImVec2(0.f, 0.f);
6452 }
6453
6454 float v_cur = g.DragCurrentValue;
6455 const ImVec2 mouse_drag_delta = ImGui::GetMouseDragDelta(0, 1.0f);
6456 if (fabsf(mouse_drag_delta.x - g.DragLastMouseDelta.x) > 0.0f)
6457 {
6458 float speed = v_speed;
6459 if (speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX)
6460 speed = (v_max - v_min) * g.DragSpeedDefaultRatio;
6461 if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f)
6462 speed = speed * g.DragSpeedScaleFast;
6463 if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f)
6464 speed = speed * g.DragSpeedScaleSlow;
6465
6466 float delta = (mouse_drag_delta.x - g.DragLastMouseDelta.x) * speed;
6467 if (fabsf(power - 1.0f) > 0.001f)
6468 {
6469 // Logarithmic curve on both side of 0.0
6470 float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur;
6471 float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f;
6472 float v1 = powf(v0_abs, 1.0f / power) + (delta * v0_sign);
6473 float v1_abs = v1 >= 0.0f ? v1 : -v1;
6474 float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f; // Crossed sign line
6475 v_cur = powf(v1_abs, power) * v0_sign * v1_sign; // Reapply sign
6476 }
6477 else
6478 {
6479 v_cur += delta;
6480 }
6481 g.DragLastMouseDelta.x = mouse_drag_delta.x;
6482
6483 // Clamp
6484 if (v_min < v_max)
6485 v_cur = ImClamp(v_cur, v_min, v_max);
6486 g.DragCurrentValue = v_cur;
6487 }
6488
6489 // Round to user desired precision, then apply
6490 v_cur = RoundScalar(v_cur, decimal_precision);
6491 if (*v != v_cur)
6492 {
6493 *v = v_cur;
6494 value_changed = true;
6495 }
6496 }
6497 else
6498 {
6499 SetActiveID(0);
6500 }
6501 }
6502
6503 return value_changed;
6504 }
6505
DragFloat(const char * label,float * v,float v_speed,float v_min,float v_max,const char * display_format,float power)6506 bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* display_format, float power)
6507 {
6508 ImGuiWindow* window = GetCurrentWindow();
6509 if (window->SkipItems)
6510 return false;
6511
6512 ImGuiState& g = *GImGui;
6513 const ImGuiStyle& style = g.Style;
6514 const ImGuiID id = window->GetID(label);
6515 const float w = CalcItemWidth();
6516
6517 const ImVec2 label_size = CalcTextSize(label, NULL, true);
6518 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
6519 const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
6520 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
6521
6522 // NB- we don't call ItemSize() yet because we may turn into a text edit box below
6523 if (!ItemAdd(total_bb, &id))
6524 {
6525 ItemSize(total_bb, style.FramePadding.y);
6526 return false;
6527 }
6528
6529 const bool hovered = IsHovered(frame_bb, id);
6530 if (hovered)
6531 SetHoveredID(id);
6532
6533 if (!display_format)
6534 display_format = "%.3f";
6535 int decimal_precision = ParseFormatPrecision(display_format, 3);
6536
6537 // Tabbing or CTRL-clicking on Drag turns it into an input box
6538 bool start_text_input = false;
6539 const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id);
6540 if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] | g.IO.MouseDoubleClicked[0])))
6541 {
6542 SetActiveID(id, window);
6543 FocusWindow(window);
6544
6545 if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0])
6546 {
6547 start_text_input = true;
6548 g.ScalarAsInputTextId = 0;
6549 }
6550 }
6551 if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
6552 return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
6553
6554 // Actual drag behavior
6555 ItemSize(total_bb, style.FramePadding.y);
6556 const bool value_changed = DragBehavior(frame_bb, id, v, v_speed, v_min, v_max, decimal_precision, power);
6557
6558 // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
6559 char value_buf[64];
6560 const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
6561 RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImGuiAlign_Center|ImGuiAlign_VCenter);
6562
6563 if (label_size.x > 0.0f)
6564 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
6565
6566 return value_changed;
6567 }
6568
DragFloatN(const char * label,float * v,int components,float v_speed,float v_min,float v_max,const char * display_format,float power)6569 bool ImGui::DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power)
6570 {
6571 ImGuiWindow* window = GetCurrentWindow();
6572 if (window->SkipItems)
6573 return false;
6574
6575 ImGuiState& g = *GImGui;
6576 bool value_changed = false;
6577 ImGui::BeginGroup();
6578 ImGui::PushID(label);
6579 PushMultiItemsWidths(components);
6580 for (int i = 0; i < components; i++)
6581 {
6582 ImGui::PushID(i);
6583 value_changed |= ImGui::DragFloat("##v", &v[i], v_speed, v_min, v_max, display_format, power);
6584 ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
6585 ImGui::PopID();
6586 ImGui::PopItemWidth();
6587 }
6588 ImGui::PopID();
6589
6590 ImGui::TextUnformatted(label, FindRenderedTextEnd(label));
6591 ImGui::EndGroup();
6592
6593 return value_changed;
6594 }
6595
DragFloat2(const char * label,float v[2],float v_speed,float v_min,float v_max,const char * display_format,float power)6596 bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power)
6597 {
6598 return DragFloatN(label, v, 2, v_speed, v_min, v_max, display_format, power);
6599 }
6600
DragFloat3(const char * label,float v[3],float v_speed,float v_min,float v_max,const char * display_format,float power)6601 bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* display_format, float power)
6602 {
6603 return DragFloatN(label, v, 3, v_speed, v_min, v_max, display_format, power);
6604 }
6605
DragFloat4(const char * label,float v[4],float v_speed,float v_min,float v_max,const char * display_format,float power)6606 bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* display_format, float power)
6607 {
6608 return DragFloatN(label, v, 4, v_speed, v_min, v_max, display_format, power);
6609 }
6610
DragFloatRange2(const char * label,float * v_current_min,float * v_current_max,float v_speed,float v_min,float v_max,const char * display_format,const char * display_format_max,float power)6611 bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* display_format, const char* display_format_max, float power)
6612 {
6613 ImGuiWindow* window = GetCurrentWindow();
6614 if (window->SkipItems)
6615 return false;
6616
6617 ImGuiState& g = *GImGui;
6618 ImGui::PushID(label);
6619 ImGui::BeginGroup();
6620 PushMultiItemsWidths(2);
6621
6622 bool value_changed = ImGui::DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format, power);
6623 ImGui::PopItemWidth();
6624 ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
6625 value_changed |= ImGui::DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, display_format_max ? display_format_max : display_format, power);
6626 ImGui::PopItemWidth();
6627 ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
6628
6629 ImGui::TextUnformatted(label, FindRenderedTextEnd(label));
6630 ImGui::EndGroup();
6631 ImGui::PopID();
6632
6633 return value_changed;
6634 }
6635
6636 // NB: v_speed is float to allow adjusting the drag speed with more precision
DragInt(const char * label,int * v,float v_speed,int v_min,int v_max,const char * display_format)6637 bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* display_format)
6638 {
6639 if (!display_format)
6640 display_format = "%.0f";
6641 float v_f = (float)*v;
6642 bool value_changed = ImGui::DragFloat(label, &v_f, v_speed, (float)v_min, (float)v_max, display_format);
6643 *v = (int)v_f;
6644 return value_changed;
6645 }
6646
DragIntN(const char * label,int * v,int components,float v_speed,int v_min,int v_max,const char * display_format)6647 bool ImGui::DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format)
6648 {
6649 ImGuiWindow* window = GetCurrentWindow();
6650 if (window->SkipItems)
6651 return false;
6652
6653 ImGuiState& g = *GImGui;
6654 bool value_changed = false;
6655 ImGui::BeginGroup();
6656 ImGui::PushID(label);
6657 PushMultiItemsWidths(components);
6658 for (int i = 0; i < components; i++)
6659 {
6660 ImGui::PushID(i);
6661 value_changed |= ImGui::DragInt("##v", &v[i], v_speed, v_min, v_max, display_format);
6662 ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
6663 ImGui::PopID();
6664 ImGui::PopItemWidth();
6665 }
6666 ImGui::PopID();
6667
6668 ImGui::TextUnformatted(label, FindRenderedTextEnd(label));
6669 ImGui::EndGroup();
6670
6671 return value_changed;
6672 }
6673
DragInt2(const char * label,int v[2],float v_speed,int v_min,int v_max,const char * display_format)6674 bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* display_format)
6675 {
6676 return DragIntN(label, v, 2, v_speed, v_min, v_max, display_format);
6677 }
6678
DragInt3(const char * label,int v[3],float v_speed,int v_min,int v_max,const char * display_format)6679 bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* display_format)
6680 {
6681 return DragIntN(label, v, 3, v_speed, v_min, v_max, display_format);
6682 }
6683
DragInt4(const char * label,int v[4],float v_speed,int v_min,int v_max,const char * display_format)6684 bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* display_format)
6685 {
6686 return DragIntN(label, v, 4, v_speed, v_min, v_max, display_format);
6687 }
6688
DragIntRange2(const char * label,int * v_current_min,int * v_current_max,float v_speed,int v_min,int v_max,const char * display_format,const char * display_format_max)6689 bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* display_format, const char* display_format_max)
6690 {
6691 ImGuiWindow* window = GetCurrentWindow();
6692 if (window->SkipItems)
6693 return false;
6694
6695 ImGuiState& g = *GImGui;
6696 ImGui::PushID(label);
6697 ImGui::BeginGroup();
6698 PushMultiItemsWidths(2);
6699
6700 bool value_changed = ImGui::DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? IM_INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format);
6701 ImGui::PopItemWidth();
6702 ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
6703 value_changed |= ImGui::DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? IM_INT_MAX : v_max, display_format_max ? display_format_max : display_format);
6704 ImGui::PopItemWidth();
6705 ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
6706
6707 ImGui::TextUnformatted(label, FindRenderedTextEnd(label));
6708 ImGui::EndGroup();
6709 ImGui::PopID();
6710
6711 return value_changed;
6712 }
6713
PlotEx(ImGuiPlotType plot_type,const char * label,float (* values_getter)(void * data,int idx),void * data,int values_count,int values_offset,const char * overlay_text,float scale_min,float scale_max,ImVec2 graph_size)6714 void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
6715 {
6716 ImGuiWindow* window = GetCurrentWindow();
6717 if (window->SkipItems)
6718 return;
6719
6720 ImGuiState& g = *GImGui;
6721 const ImGuiStyle& style = g.Style;
6722
6723 const ImVec2 label_size = CalcTextSize(label, NULL, true);
6724 if (graph_size.x == 0.0f)
6725 graph_size.x = CalcItemWidth();
6726 if (graph_size.y == 0.0f)
6727 graph_size.y = label_size.y + (style.FramePadding.y * 2);
6728
6729 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y));
6730 const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
6731 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0));
6732 ItemSize(total_bb, style.FramePadding.y);
6733 if (!ItemAdd(total_bb, NULL))
6734 return;
6735
6736 // Determine scale from values if not specified
6737 if (scale_min == FLT_MAX || scale_max == FLT_MAX)
6738 {
6739 float v_min = FLT_MAX;
6740 float v_max = -FLT_MAX;
6741 for (int i = 0; i < values_count; i++)
6742 {
6743 const float v = values_getter(data, i);
6744 v_min = ImMin(v_min, v);
6745 v_max = ImMax(v_max, v);
6746 }
6747 if (scale_min == FLT_MAX)
6748 scale_min = v_min;
6749 if (scale_max == FLT_MAX)
6750 scale_max = v_max;
6751 }
6752
6753 RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
6754
6755 int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
6756 int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
6757
6758 // Tooltip on hover
6759 int v_hovered = -1;
6760 if (IsHovered(inner_bb, 0))
6761 {
6762 const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);
6763 const int v_idx = (int)(t * item_count);
6764 IM_ASSERT(v_idx >= 0 && v_idx < values_count);
6765
6766 const float v0 = values_getter(data, (v_idx + values_offset) % values_count);
6767 const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count);
6768 if (plot_type == ImGuiPlotType_Lines)
6769 ImGui::SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1);
6770 else if (plot_type == ImGuiPlotType_Histogram)
6771 ImGui::SetTooltip("%d: %8.4g", v_idx, v0);
6772 v_hovered = v_idx;
6773 }
6774
6775 const float t_step = 1.0f / (float)res_w;
6776
6777 float v0 = values_getter(data, (0 + values_offset) % values_count);
6778 float t0 = 0.0f;
6779 ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) / (scale_max - scale_min)) ); // Point in the normalized space of our target rectangle
6780
6781 const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram);
6782 const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered);
6783
6784 for (int n = 0; n < res_w; n++)
6785 {
6786 const float t1 = t0 + t_step;
6787 const int v1_idx = (int)(t0 * item_count + 0.5f);
6788 IM_ASSERT(v1_idx >= 0 && v1_idx < values_count);
6789 const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count);
6790 const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) / (scale_max - scale_min)) );
6791
6792 // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU.
6793 ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0);
6794 ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, 1.0f));
6795 if (plot_type == ImGuiPlotType_Lines)
6796 {
6797 window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
6798 }
6799 else if (plot_type == ImGuiPlotType_Histogram)
6800 {
6801 if (pos1.x >= pos0.x + 2.0f)
6802 pos1.x -= 1.0f;
6803 window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
6804 }
6805
6806 t0 = t1;
6807 tp0 = tp1;
6808 }
6809
6810 // Text overlay
6811 if (overlay_text)
6812 RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImGuiAlign_Center);
6813
6814 if (label_size.x > 0.0f)
6815 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
6816 }
6817
6818 struct ImGuiPlotArrayGetterData
6819 {
6820 const float* Values;
6821 int Stride;
6822
ImGuiPlotArrayGetterDataImGuiPlotArrayGetterData6823 ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; }
6824 };
6825
Plot_ArrayGetter(void * data,int idx)6826 static float Plot_ArrayGetter(void* data, int idx)
6827 {
6828 ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data;
6829 const float v = *(float*)(void*)((unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);
6830 return v;
6831 }
6832
PlotLines(const char * label,const float * values,int values_count,int values_offset,const char * overlay_text,float scale_min,float scale_max,ImVec2 graph_size,int stride)6833 void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)
6834 {
6835 ImGuiPlotArrayGetterData data(values, stride);
6836 PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
6837 }
6838
PlotLines(const char * label,float (* values_getter)(void * data,int idx),void * data,int values_count,int values_offset,const char * overlay_text,float scale_min,float scale_max,ImVec2 graph_size)6839 void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
6840 {
6841 PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
6842 }
6843
PlotHistogram(const char * label,const float * values,int values_count,int values_offset,const char * overlay_text,float scale_min,float scale_max,ImVec2 graph_size,int stride)6844 void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)
6845 {
6846 ImGuiPlotArrayGetterData data(values, stride);
6847 PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
6848 }
6849
PlotHistogram(const char * label,float (* values_getter)(void * data,int idx),void * data,int values_count,int values_offset,const char * overlay_text,float scale_min,float scale_max,ImVec2 graph_size)6850 void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
6851 {
6852 PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
6853 }
6854
6855 // size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size
ProgressBar(float fraction,const ImVec2 & size_arg,const char * overlay)6856 void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay)
6857 {
6858 ImGuiWindow* window = GetCurrentWindow();
6859 if (window->SkipItems)
6860 return;
6861
6862 ImGuiState& g = *GImGui;
6863 const ImGuiStyle& style = g.Style;
6864
6865 ImVec2 pos = window->DC.CursorPos;
6866 ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f));
6867 ItemSize(bb, style.FramePadding.y);
6868 if (!ItemAdd(bb, NULL))
6869 return;
6870
6871 // Render
6872 fraction = ImSaturate(fraction);
6873 RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
6874 bb.Reduce(ImVec2(window->BorderSize, window->BorderSize));
6875 const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y);
6876 RenderFrame(bb.Min, fill_br, GetColorU32(ImGuiCol_PlotHistogram), false, style.FrameRounding);
6877
6878 // Default displaying the fraction as percentage string, but user can override it
6879 char overlay_buf[32];
6880 if (!overlay)
6881 {
6882 ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f);
6883 overlay = overlay_buf;
6884 }
6885
6886 ImVec2 overlay_size = CalcTextSize(overlay, NULL);
6887 if (overlay_size.x > 0.0f)
6888 RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImGuiAlign_Left|ImGuiAlign_VCenter, &bb.Min, &bb.Max);
6889 }
6890
Checkbox(const char * label,bool * v)6891 bool ImGui::Checkbox(const char* label, bool* v)
6892 {
6893 ImGuiWindow* window = GetCurrentWindow();
6894 if (window->SkipItems)
6895 return false;
6896
6897 ImGuiState& g = *GImGui;
6898 const ImGuiStyle& style = g.Style;
6899 const ImGuiID id = window->GetID(label);
6900 const ImVec2 label_size = CalcTextSize(label, NULL, true);
6901
6902 const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2, label_size.y + style.FramePadding.y*2));
6903 ItemSize(check_bb, style.FramePadding.y);
6904
6905 ImRect total_bb = check_bb;
6906 if (label_size.x > 0)
6907 SameLine(0, style.ItemInnerSpacing.x);
6908 const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size);
6909 if (label_size.x > 0)
6910 {
6911 ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
6912 total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max));
6913 }
6914
6915 if (!ItemAdd(total_bb, &id))
6916 return false;
6917
6918 bool hovered, held;
6919 bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
6920 if (pressed)
6921 *v = !(*v);
6922
6923 RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
6924 if (*v)
6925 {
6926 const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
6927 const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
6928 window->DrawList->AddRectFilled(check_bb.Min+ImVec2(pad,pad), check_bb.Max-ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), style.FrameRounding);
6929 }
6930
6931 if (g.LogEnabled)
6932 LogRenderedText(text_bb.GetTL(), *v ? "[x]" : "[ ]");
6933 if (label_size.x > 0.0f)
6934 RenderText(text_bb.GetTL(), label);
6935
6936 return pressed;
6937 }
6938
CheckboxFlags(const char * label,unsigned int * flags,unsigned int flags_value)6939 bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value)
6940 {
6941 bool v = ((*flags & flags_value) == flags_value);
6942 bool pressed = ImGui::Checkbox(label, &v);
6943 if (pressed)
6944 {
6945 if (v)
6946 *flags |= flags_value;
6947 else
6948 *flags &= ~flags_value;
6949 }
6950
6951 return pressed;
6952 }
6953
RadioButton(const char * label,bool active)6954 bool ImGui::RadioButton(const char* label, bool active)
6955 {
6956 ImGuiWindow* window = GetCurrentWindow();
6957 if (window->SkipItems)
6958 return false;
6959
6960 ImGuiState& g = *GImGui;
6961 const ImGuiStyle& style = g.Style;
6962 const ImGuiID id = window->GetID(label);
6963 const ImVec2 label_size = CalcTextSize(label, NULL, true);
6964
6965 const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2-1, label_size.y + style.FramePadding.y*2-1));
6966 ItemSize(check_bb, style.FramePadding.y);
6967
6968 ImRect total_bb = check_bb;
6969 if (label_size.x > 0)
6970 SameLine(0, style.ItemInnerSpacing.x);
6971 const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size);
6972 if (label_size.x > 0)
6973 {
6974 ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
6975 total_bb.Add(text_bb);
6976 }
6977
6978 if (!ItemAdd(total_bb, &id))
6979 return false;
6980
6981 ImVec2 center = check_bb.GetCenter();
6982 center.x = (float)(int)center.x + 0.5f;
6983 center.y = (float)(int)center.y + 0.5f;
6984 const float radius = check_bb.GetHeight() * 0.5f;
6985
6986 bool hovered, held;
6987 bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
6988
6989 window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16);
6990 if (active)
6991 {
6992 const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
6993 const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
6994 window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16);
6995 }
6996
6997 if (window->Flags & ImGuiWindowFlags_ShowBorders)
6998 {
6999 window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16);
7000 window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16);
7001 }
7002
7003 if (g.LogEnabled)
7004 LogRenderedText(text_bb.GetTL(), active ? "(x)" : "( )");
7005 if (label_size.x > 0.0f)
7006 RenderText(text_bb.GetTL(), label);
7007
7008 return pressed;
7009 }
7010
RadioButton(const char * label,int * v,int v_button)7011 bool ImGui::RadioButton(const char* label, int* v, int v_button)
7012 {
7013 const bool pressed = ImGui::RadioButton(label, *v == v_button);
7014 if (pressed)
7015 {
7016 *v = v_button;
7017 }
7018 return pressed;
7019 }
7020
InputTextCalcTextLenAndLineCount(const char * text_begin,const char ** out_text_end)7021 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
7022 {
7023 int line_count = 0;
7024 const char* s = text_begin;
7025 while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding
7026 if (c == '\n')
7027 line_count++;
7028 s--;
7029 if (s[0] != '\n' && s[0] != '\r')
7030 line_count++;
7031 *out_text_end = s;
7032 return line_count;
7033 }
7034
InputTextCalcTextSizeW(const ImWchar * text_begin,const ImWchar * text_end,const ImWchar ** remaining,ImVec2 * out_offset,bool stop_on_new_line)7035 static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line)
7036 {
7037 ImFont* font = GImGui->Font;
7038 const float line_height = GImGui->FontSize;
7039 const float scale = line_height / font->FontSize;
7040
7041 ImVec2 text_size = ImVec2(0,0);
7042 float line_width = 0.0f;
7043
7044 const ImWchar* s = text_begin;
7045 while (s < text_end)
7046 {
7047 unsigned int c = (unsigned int)(*s++);
7048 if (c == '\n')
7049 {
7050 text_size.x = ImMax(text_size.x, line_width);
7051 text_size.y += line_height;
7052 line_width = 0.0f;
7053 if (stop_on_new_line)
7054 break;
7055 continue;
7056 }
7057 if (c == '\r')
7058 continue;
7059
7060 const float char_width = font->GetCharAdvance((unsigned short)c) * scale;
7061 line_width += char_width;
7062 }
7063
7064 if (text_size.x < line_width)
7065 text_size.x = line_width;
7066
7067 if (out_offset)
7068 *out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n
7069
7070 if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n
7071 text_size.y += line_height;
7072
7073 if (remaining)
7074 *remaining = s;
7075
7076 return text_size;
7077 }
7078
7079 // Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar)
7080 namespace ImGuiStb
7081 {
7082
STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING * obj)7083 static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; }
STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING * obj,int idx)7084 static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->Text[idx]; }
STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING * obj,int line_start_idx,int char_idx)7085 static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->Text[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); }
STB_TEXTEDIT_KEYTOTEXT(int key)7086 static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; }
7087 static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
STB_TEXTEDIT_LAYOUTROW(StbTexteditRow * r,STB_TEXTEDIT_STRING * obj,int line_start_idx)7088 static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
7089 {
7090 const ImWchar* text = obj->Text.Data;
7091 const ImWchar* text_remaining = NULL;
7092 const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true);
7093 r->x0 = 0.0f;
7094 r->x1 = size.x;
7095 r->baseline_y_delta = size.y;
7096 r->ymin = 0.0f;
7097 r->ymax = size.y;
7098 r->num_chars = (int)(text_remaining - (text + line_start_idx));
7099 }
7100
is_separator(unsigned int c)7101 static bool is_separator(unsigned int c) { return ImCharIsSpace(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; }
is_word_boundary_from_right(STB_TEXTEDIT_STRING * obj,int idx)7102 static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator( obj->Text[idx-1] ) && !is_separator( obj->Text[idx] ) ) : 1; }
STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING * obj,int idx)7103 static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
7104 #ifdef __APPLE__ // FIXME: Move setting to IO structure
is_word_boundary_from_left(STB_TEXTEDIT_STRING * obj,int idx)7105 static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator( obj->Text[idx-1] ) && is_separator( obj->Text[idx] ) ) : 1; }
STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING * obj,int idx)7106 static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
7107 #else
STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING * obj,int idx)7108 static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
7109 #endif
7110 #define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h
7111 #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
7112
STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING * obj,int pos,int n)7113 static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
7114 {
7115 ImWchar* dst = obj->Text.Data + pos;
7116
7117 // We maintain our buffer length in both UTF-8 and wchar formats
7118 obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);
7119 obj->CurLenW -= n;
7120
7121 // Offset remaining text
7122 const ImWchar* src = obj->Text.Data + pos + n;
7123 while (ImWchar c = *src++)
7124 *dst++ = c;
7125 *dst = '\0';
7126 }
7127
STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING * obj,int pos,const ImWchar * new_text,int new_text_len)7128 static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len)
7129 {
7130 const int text_len = obj->CurLenW;
7131 if (new_text_len + text_len + 1 > obj->Text.Size)
7132 return false;
7133
7134 const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
7135 if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA)
7136 return false;
7137
7138 ImWchar* text = obj->Text.Data;
7139 if (pos != text_len)
7140 memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));
7141 memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
7142
7143 obj->CurLenW += new_text_len;
7144 obj->CurLenA += new_text_len_utf8;
7145 obj->Text[obj->CurLenW] = '\0';
7146
7147 return true;
7148 }
7149
7150 // We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols)
7151 #define STB_TEXTEDIT_K_LEFT 0x10000 // keyboard input to move cursor left
7152 #define STB_TEXTEDIT_K_RIGHT 0x10001 // keyboard input to move cursor right
7153 #define STB_TEXTEDIT_K_UP 0x10002 // keyboard input to move cursor up
7154 #define STB_TEXTEDIT_K_DOWN 0x10003 // keyboard input to move cursor down
7155 #define STB_TEXTEDIT_K_LINESTART 0x10004 // keyboard input to move cursor to start of line
7156 #define STB_TEXTEDIT_K_LINEEND 0x10005 // keyboard input to move cursor to end of line
7157 #define STB_TEXTEDIT_K_TEXTSTART 0x10006 // keyboard input to move cursor to start of text
7158 #define STB_TEXTEDIT_K_TEXTEND 0x10007 // keyboard input to move cursor to end of text
7159 #define STB_TEXTEDIT_K_DELETE 0x10008 // keyboard input to delete selection or character under cursor
7160 #define STB_TEXTEDIT_K_BACKSPACE 0x10009 // keyboard input to delete selection or character left of cursor
7161 #define STB_TEXTEDIT_K_UNDO 0x1000A // keyboard input to perform undo
7162 #define STB_TEXTEDIT_K_REDO 0x1000B // keyboard input to perform redo
7163 #define STB_TEXTEDIT_K_WORDLEFT 0x1000C // keyboard input to move cursor left one word
7164 #define STB_TEXTEDIT_K_WORDRIGHT 0x1000D // keyboard input to move cursor right one word
7165 #define STB_TEXTEDIT_K_SHIFT 0x20000
7166
7167 #define STB_TEXTEDIT_IMPLEMENTATION
7168 #include "stb_textedit.h"
7169
7170 }
7171
OnKeyPressed(int key)7172 void ImGuiTextEditState::OnKeyPressed(int key)
7173 {
7174 stb_textedit_key(this, &StbState, key);
7175 CursorFollow = true;
7176 CursorAnimReset();
7177 }
7178
7179 // Public API to manipulate UTF-8 text
7180 // We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar)
7181 // FIXME: The existence of this rarely exercised code path is a bit of a nuisance.
DeleteChars(int pos,int bytes_count)7182 void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count)
7183 {
7184 IM_ASSERT(pos + bytes_count <= BufTextLen);
7185 char* dst = Buf + pos;
7186 const char* src = Buf + pos + bytes_count;
7187 while (char c = *src++)
7188 *dst++ = c;
7189 *dst = '\0';
7190
7191 if (CursorPos + bytes_count >= pos)
7192 CursorPos -= bytes_count;
7193 else if (CursorPos >= pos)
7194 CursorPos = pos;
7195 SelectionStart = SelectionEnd = CursorPos;
7196 BufDirty = true;
7197 BufTextLen -= bytes_count;
7198 }
7199
InsertChars(int pos,const char * new_text,const char * new_text_end)7200 void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)
7201 {
7202 const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
7203 if (new_text_len + BufTextLen + 1 >= BufSize)
7204 return;
7205
7206 if (BufTextLen != pos)
7207 memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos));
7208 memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char));
7209 Buf[BufTextLen + new_text_len] = '\0';
7210
7211 if (CursorPos >= pos)
7212 CursorPos += new_text_len;
7213 SelectionStart = SelectionEnd = CursorPos;
7214 BufDirty = true;
7215 BufTextLen += new_text_len;
7216 }
7217
7218 // Return false to discard a character.
InputTextFilterCharacter(unsigned int * p_char,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)7219 static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
7220 {
7221 unsigned int c = *p_char;
7222
7223 if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF)))
7224 {
7225 bool pass = false;
7226 pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline));
7227 pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput));
7228 if (!pass)
7229 return false;
7230 }
7231
7232 if (c >= 0xE000 && c <= 0xF8FF) // Filter private Unicode range. I don't imagine anybody would want to input them. GLFW on OSX seems to send private characters for special keys like arrow keys.
7233 return false;
7234
7235 if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank))
7236 {
7237 if (flags & ImGuiInputTextFlags_CharsDecimal)
7238 if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/'))
7239 return false;
7240
7241 if (flags & ImGuiInputTextFlags_CharsHexadecimal)
7242 if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F'))
7243 return false;
7244
7245 if (flags & ImGuiInputTextFlags_CharsUppercase)
7246 if (c >= 'a' && c <= 'z')
7247 *p_char = (c += (unsigned int)('A'-'a'));
7248
7249 if (flags & ImGuiInputTextFlags_CharsNoBlank)
7250 if (ImCharIsSpace(c))
7251 return false;
7252 }
7253
7254 if (flags & ImGuiInputTextFlags_CallbackCharFilter)
7255 {
7256 ImGuiTextEditCallbackData callback_data;
7257 memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
7258 callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;
7259 callback_data.EventChar = (ImWchar)c;
7260 callback_data.Flags = flags;
7261 callback_data.UserData = user_data;
7262 if (callback(&callback_data) != 0)
7263 return false;
7264 *p_char = callback_data.EventChar;
7265 if (!callback_data.EventChar)
7266 return false;
7267 }
7268
7269 return true;
7270 }
7271
7272 // Edit a string of text
7273 // NB: when active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while active has no effect.
7274 // FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188
InputTextEx(const char * label,char * buf,int buf_size,const ImVec2 & size_arg,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)7275 bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
7276 {
7277 ImGuiWindow* window = GetCurrentWindow();
7278 if (window->SkipItems)
7279 return false;
7280
7281 IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys)
7282 IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)
7283
7284 ImGuiState& g = *GImGui;
7285 const ImGuiIO& io = g.IO;
7286 const ImGuiStyle& style = g.Style;
7287
7288 const ImGuiID id = window->GetID(label);
7289 const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;
7290 const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0;
7291 const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
7292
7293 const ImVec2 label_size = CalcTextSize(label, NULL, true);
7294 ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? ImGui::GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line
7295 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
7296 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f, 0.0f));
7297
7298 ImGuiWindow* draw_window = window;
7299 if (is_multiline)
7300 {
7301 ImGui::BeginGroup();
7302 if (!ImGui::BeginChildFrame(id, frame_bb.GetSize()))
7303 {
7304 ImGui::EndChildFrame();
7305 ImGui::EndGroup();
7306 return false;
7307 }
7308 draw_window = GetCurrentWindow();
7309 size.x -= draw_window->ScrollbarSizes.x;
7310 }
7311 else
7312 {
7313 ItemSize(total_bb, style.FramePadding.y);
7314 if (!ItemAdd(total_bb, &id))
7315 return false;
7316 }
7317
7318 // Password pushes a temporary font with only a fallback glyph
7319 if (is_password)
7320 {
7321 const ImFont::Glyph* glyph = g.Font->FindGlyph('*');
7322 ImFont* password_font = &g.InputTextPasswordFont;
7323 password_font->FontSize = g.Font->FontSize;
7324 password_font->Scale = g.Font->Scale;
7325 password_font->DisplayOffset = g.Font->DisplayOffset;
7326 password_font->Ascent = g.Font->Ascent;
7327 password_font->Descent = g.Font->Descent;
7328 password_font->ContainerAtlas = g.Font->ContainerAtlas;
7329 password_font->FallbackGlyph = glyph;
7330 password_font->FallbackXAdvance = glyph->XAdvance;
7331 IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexXAdvance.empty() && password_font->IndexLookup.empty());
7332 ImGui::PushFont(password_font);
7333 }
7334
7335 // NB: we are only allowed to access 'edit_state' if we are the active widget.
7336 ImGuiTextEditState& edit_state = g.InputTextState;
7337
7338 const bool is_ctrl_down = io.KeyCtrl;
7339 const bool is_shift_down = io.KeyShift;
7340 const bool is_alt_down = io.KeyAlt;
7341 const bool is_super_down = io.KeySuper;
7342 const bool focus_requested = FocusableItemRegister(window, g.ActiveId == id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing
7343 const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent);
7344 const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code;
7345
7346 const bool hovered = IsHovered(frame_bb, id);
7347 if (hovered)
7348 {
7349 SetHoveredID(id);
7350 g.MouseCursor = ImGuiMouseCursor_TextInput;
7351 }
7352 const bool user_clicked = hovered && io.MouseClicked[0];
7353 const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetID("#SCROLLY");
7354
7355 bool select_all = (g.ActiveId != id) && (flags & ImGuiInputTextFlags_AutoSelectAll) != 0;
7356 if (focus_requested || user_clicked || user_scrolled)
7357 {
7358 if (g.ActiveId != id)
7359 {
7360 // Start edition
7361 // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)
7362 // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode)
7363 const int prev_len_w = edit_state.CurLenW;
7364 edit_state.Text.resize(buf_size+1); // wchar count <= utf-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash.
7365 edit_state.InitialText.resize(buf_size+1); // utf-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash.
7366 ImFormatString(edit_state.InitialText.Data, edit_state.InitialText.Size, "%s", buf);
7367 const char* buf_end = NULL;
7368 edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
7369 edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImFormatString() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8.
7370 edit_state.CursorAnimReset();
7371
7372 // Preserve cursor position and undo/redo stack if we come back to same widget
7373 // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar).
7374 const bool recycle_state = (edit_state.Id == id) && (prev_len_w == edit_state.CurLenW);
7375 if (recycle_state)
7376 {
7377 // Recycle existing cursor/selection/undo stack but clamp position
7378 // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
7379 edit_state.CursorClamp();
7380 }
7381 else
7382 {
7383 edit_state.Id = id;
7384 edit_state.ScrollX = 0.0f;
7385 stb_textedit_initialize_state(&edit_state.StbState, !is_multiline);
7386 if (!is_multiline && focus_requested_by_code)
7387 select_all = true;
7388 }
7389 if (flags & ImGuiInputTextFlags_AlwaysInsertMode)
7390 edit_state.StbState.insert_mode = true;
7391 if (!is_multiline && (focus_requested_by_tab || (user_clicked && is_ctrl_down)))
7392 select_all = true;
7393 }
7394 SetActiveID(id, window);
7395 FocusWindow(window);
7396 }
7397 else if (io.MouseClicked[0])
7398 {
7399 // Release focus when we click outside
7400 if (g.ActiveId == id)
7401 SetActiveID(0);
7402 }
7403
7404 bool value_changed = false;
7405 bool enter_pressed = false;
7406
7407 if (g.ActiveId == id)
7408 {
7409 if (!is_editable && !g.ActiveIdIsJustActivated)
7410 {
7411 // When read-only we always use the live data passed to the function
7412 edit_state.Text.resize(buf_size+1);
7413 const char* buf_end = NULL;
7414 edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
7415 edit_state.CurLenA = (int)(buf_end - buf);
7416 edit_state.CursorClamp();
7417 }
7418
7419 edit_state.BufSizeA = buf_size;
7420
7421 // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
7422 // Down the line we should have a cleaner library-wide concept of Selected vs Active.
7423 g.ActiveIdAllowOverlap = !io.MouseDown[0];
7424
7425 // Edit in progress
7426 const float mouse_x = (g.IO.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX;
7427 const float mouse_y = (is_multiline ? (g.IO.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f));
7428
7429 if (select_all || (hovered && !io.DoubleClickSelectsWord && io.MouseDoubleClicked[0]))
7430 {
7431 edit_state.SelectAll();
7432 edit_state.SelectedAllMouseLock = true;
7433 }
7434 else if (hovered && io.DoubleClickSelectsWord && io.MouseDoubleClicked[0])
7435 {
7436 // Select a word only, OS X style (by simulating keystrokes)
7437 edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT);
7438 edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);
7439 }
7440 else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock)
7441 {
7442 stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
7443 edit_state.CursorAnimReset();
7444 }
7445 else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f))
7446 {
7447 stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
7448 edit_state.CursorAnimReset();
7449 edit_state.CursorFollow = true;
7450 }
7451 if (edit_state.SelectedAllMouseLock && !io.MouseDown[0])
7452 edit_state.SelectedAllMouseLock = false;
7453
7454 if (g.IO.InputCharacters[0])
7455 {
7456 // Process text input (before we check for Return because using some IME will effectively send a Return?)
7457 // We ignore CTRL inputs, but need to allow CTRL+ALT as some keyboards (e.g. German) use AltGR - which is Alt+Ctrl - to input certain characters.
7458 if (!(is_ctrl_down && !is_alt_down) && is_editable)
7459 {
7460 for (int n = 0; n < IM_ARRAYSIZE(g.IO.InputCharacters) && g.IO.InputCharacters[n]; n++)
7461 if (unsigned int c = (unsigned int)g.IO.InputCharacters[n])
7462 {
7463 // Insert character if they pass filtering
7464 if (!InputTextFilterCharacter(&c, flags, callback, user_data))
7465 continue;
7466 edit_state.OnKeyPressed((int)c);
7467 }
7468 }
7469
7470 // Consume characters
7471 memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
7472 }
7473
7474 // Handle various key-presses
7475 bool cancel_edit = false;
7476 const int k_mask = (is_shift_down ? STB_TEXTEDIT_K_SHIFT : 0);
7477 const bool is_shortcutkey_only = (io.ShortcutsUseSuperKey ? (is_super_down && !is_alt_down && !is_shift_down && !is_ctrl_down) : (is_ctrl_down && !is_alt_down && !is_shift_down && !is_super_down));
7478 const bool is_wordmove_key_down = (io.WordMovementUsesAltKey ? io.KeyAlt : io.KeyCtrl);
7479
7480 if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { edit_state.OnKeyPressed(is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT | k_mask : STB_TEXTEDIT_K_LEFT | k_mask); }
7481 else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { edit_state.OnKeyPressed(is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT | k_mask : STB_TEXTEDIT_K_RIGHT | k_mask); }
7482 else if (is_multiline && IsKeyPressedMap(ImGuiKey_UpArrow)) { if (is_ctrl_down) SetWindowScrollY(draw_window, draw_window->Scroll.y - g.FontSize); else edit_state.OnKeyPressed(STB_TEXTEDIT_K_UP | k_mask); }
7483 else if (is_multiline && IsKeyPressedMap(ImGuiKey_DownArrow)) { if (is_ctrl_down) SetWindowScrollY(draw_window, draw_window->Scroll.y + g.FontSize); else edit_state.OnKeyPressed(STB_TEXTEDIT_K_DOWN| k_mask); }
7484 else if (IsKeyPressedMap(ImGuiKey_Home)) { edit_state.OnKeyPressed(is_ctrl_down ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
7485 else if (IsKeyPressedMap(ImGuiKey_End)) { edit_state.OnKeyPressed(is_ctrl_down ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
7486 else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
7487 else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); }
7488 else if (IsKeyPressedMap(ImGuiKey_Enter))
7489 {
7490 bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;
7491 if (!is_multiline || (ctrl_enter_for_new_line && !is_ctrl_down) || (!ctrl_enter_for_new_line && is_ctrl_down))
7492 {
7493 SetActiveID(0);
7494 enter_pressed = true;
7495 }
7496 else if (is_editable)
7497 {
7498 unsigned int c = '\n'; // Insert new line
7499 if (InputTextFilterCharacter(&c, flags, callback, user_data))
7500 edit_state.OnKeyPressed((int)c);
7501 }
7502 }
7503 else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !is_ctrl_down && !is_shift_down && !is_alt_down && is_editable)
7504 {
7505 unsigned int c = '\t'; // Insert TAB
7506 if (InputTextFilterCharacter(&c, flags, callback, user_data))
7507 edit_state.OnKeyPressed((int)c);
7508 }
7509 else if (IsKeyPressedMap(ImGuiKey_Escape)) { SetActiveID(0); cancel_edit = true; }
7510 else if (is_shortcutkey_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); }
7511 else if (is_shortcutkey_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); }
7512 else if (is_shortcutkey_only && IsKeyPressedMap(ImGuiKey_A)) { edit_state.SelectAll(); edit_state.CursorFollow = true; }
7513 else if (is_shortcutkey_only && !is_password && ((IsKeyPressedMap(ImGuiKey_X) && is_editable) || IsKeyPressedMap(ImGuiKey_C)) && (!is_multiline || edit_state.HasSelection()))
7514 {
7515 // Cut, Copy
7516 const bool cut = IsKeyPressedMap(ImGuiKey_X);
7517 if (cut && !edit_state.HasSelection())
7518 edit_state.SelectAll();
7519
7520 if (g.IO.SetClipboardTextFn)
7521 {
7522 const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0;
7523 const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW;
7524 edit_state.TempTextBuffer.resize((ie-ib) * 4 + 1);
7525 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data+ib, edit_state.Text.Data+ie);
7526 g.IO.SetClipboardTextFn(edit_state.TempTextBuffer.Data);
7527 }
7528
7529 if (cut)
7530 {
7531 edit_state.CursorFollow = true;
7532 stb_textedit_cut(&edit_state, &edit_state.StbState);
7533 }
7534 }
7535 else if (is_shortcutkey_only && IsKeyPressedMap(ImGuiKey_V) && is_editable)
7536 {
7537 // Paste
7538 if (g.IO.GetClipboardTextFn)
7539 {
7540 if (const char* clipboard = g.IO.GetClipboardTextFn())
7541 {
7542 // Remove new-line from pasted buffer
7543 const int clipboard_len = (int)strlen(clipboard);
7544 ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar));
7545 int clipboard_filtered_len = 0;
7546 for (const char* s = clipboard; *s; )
7547 {
7548 unsigned int c;
7549 s += ImTextCharFromUtf8(&c, s, NULL);
7550 if (c == 0)
7551 break;
7552 if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, user_data))
7553 continue;
7554 clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;
7555 }
7556 clipboard_filtered[clipboard_filtered_len] = 0;
7557 if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation
7558 {
7559 stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len);
7560 edit_state.CursorFollow = true;
7561 }
7562 ImGui::MemFree(clipboard_filtered);
7563 }
7564 }
7565 }
7566
7567 if (cancel_edit)
7568 {
7569 // Restore initial value
7570 if (is_editable)
7571 {
7572 ImFormatString(buf, buf_size, "%s", edit_state.InitialText.Data);
7573 value_changed = true;
7574 }
7575 }
7576 else
7577 {
7578 // Apply new value immediately - copy modified buffer back
7579 // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer
7580 // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect.
7581 // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks.
7582 if (is_editable)
7583 {
7584 edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4);
7585 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL);
7586 }
7587
7588 // User callback
7589 if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0)
7590 {
7591 IM_ASSERT(callback != NULL);
7592
7593 // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
7594 ImGuiInputTextFlags event_flag = 0;
7595 ImGuiKey event_key = ImGuiKey_COUNT;
7596 if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab))
7597 {
7598 event_flag = ImGuiInputTextFlags_CallbackCompletion;
7599 event_key = ImGuiKey_Tab;
7600 }
7601 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow))
7602 {
7603 event_flag = ImGuiInputTextFlags_CallbackHistory;
7604 event_key = ImGuiKey_UpArrow;
7605 }
7606 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow))
7607 {
7608 event_flag = ImGuiInputTextFlags_CallbackHistory;
7609 event_key = ImGuiKey_DownArrow;
7610 }
7611 else if (flags & ImGuiInputTextFlags_CallbackAlways)
7612 event_flag = ImGuiInputTextFlags_CallbackAlways;
7613
7614 if (event_flag)
7615 {
7616 ImGuiTextEditCallbackData callback_data;
7617 memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
7618 callback_data.EventFlag = event_flag;
7619 callback_data.Flags = flags;
7620 callback_data.UserData = user_data;
7621 callback_data.ReadOnly = !is_editable;
7622
7623 callback_data.EventKey = event_key;
7624 callback_data.Buf = edit_state.TempTextBuffer.Data;
7625 callback_data.BufTextLen = edit_state.CurLenA;
7626 callback_data.BufSize = edit_state.BufSizeA;
7627 callback_data.BufDirty = false;
7628
7629 // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188)
7630 ImWchar* text = edit_state.Text.Data;
7631 const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor);
7632 const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start);
7633 const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end);
7634
7635 // Call user code
7636 callback(&callback_data);
7637
7638 // Read back what user may have modified
7639 IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data); // Invalid to modify those fields
7640 IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA);
7641 IM_ASSERT(callback_data.Flags == flags);
7642 if (callback_data.CursorPos != utf8_cursor_pos) edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos);
7643 if (callback_data.SelectionStart != utf8_selection_start) edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart);
7644 if (callback_data.SelectionEnd != utf8_selection_end) edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd);
7645 if (callback_data.BufDirty)
7646 {
7647 IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
7648 edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, callback_data.Buf, NULL);
7649 edit_state.CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
7650 edit_state.CursorAnimReset();
7651 }
7652 }
7653 }
7654
7655 // Copy back to user buffer
7656 if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0)
7657 {
7658 ImFormatString(buf, buf_size, "%s", edit_state.TempTextBuffer.Data);
7659 value_changed = true;
7660 }
7661 }
7662 }
7663
7664 if (!is_multiline)
7665 RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
7666
7667 // Render
7668 const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size
7669 ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;
7670 ImVec2 text_size(0.f, 0.f);
7671 if (g.ActiveId == id || (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetID("#SCROLLY")))
7672 {
7673 edit_state.CursorAnim += g.IO.DeltaTime;
7674
7675 // We need to:
7676 // - Display the text (this can be more easily clipped)
7677 // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)
7678 // - Measure text height (for scrollbar)
7679 // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)
7680 const ImWchar* text_begin = edit_state.Text.Data;
7681 ImVec2 cursor_offset, select_start_offset;
7682
7683 {
7684 // Count lines + find lines numbers straddling 'cursor' and 'select_start' position.
7685 const ImWchar* searches_input_ptr[2];
7686 searches_input_ptr[0] = text_begin + edit_state.StbState.cursor;
7687 searches_input_ptr[1] = NULL;
7688 int searches_remaining = 1;
7689 int searches_result_line_number[2] = { -1, -999 };
7690 if (edit_state.StbState.select_start != edit_state.StbState.select_end)
7691 {
7692 searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
7693 searches_result_line_number[1] = -1;
7694 searches_remaining++;
7695 }
7696
7697 // Iterate all lines to find our line numbers
7698 // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter.
7699 searches_remaining += is_multiline ? 1 : 0;
7700 int line_count = 0;
7701 for (const ImWchar* s = text_begin; *s != 0; s++)
7702 if (*s == '\n')
7703 {
7704 line_count++;
7705 if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; }
7706 if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; }
7707 }
7708 line_count++;
7709 if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count;
7710 if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count;
7711
7712 // Calculate 2d position by finding the beginning of the line and measuring distance
7713 cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;
7714 cursor_offset.y = searches_result_line_number[0] * g.FontSize;
7715 if (searches_result_line_number[1] >= 0)
7716 {
7717 select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;
7718 select_start_offset.y = searches_result_line_number[1] * g.FontSize;
7719 }
7720
7721 // Calculate text height
7722 if (is_multiline)
7723 text_size = ImVec2(size.x, line_count * g.FontSize);
7724 }
7725
7726 // Scroll
7727 if (edit_state.CursorFollow)
7728 {
7729 // Horizontal scroll in chunks of quarter width
7730 if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll))
7731 {
7732 const float scroll_increment_x = size.x * 0.25f;
7733 if (cursor_offset.x < edit_state.ScrollX)
7734 edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x);
7735 else if (cursor_offset.x - size.x >= edit_state.ScrollX)
7736 edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x);
7737 }
7738 else
7739 {
7740 edit_state.ScrollX = 0.0f;
7741 }
7742
7743 // Vertical scroll
7744 if (is_multiline)
7745 {
7746 float scroll_y = draw_window->Scroll.y;
7747 if (cursor_offset.y - g.FontSize < scroll_y)
7748 scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
7749 else if (cursor_offset.y - size.y >= scroll_y)
7750 scroll_y = cursor_offset.y - size.y;
7751 draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y); // To avoid a frame of lag
7752 draw_window->Scroll.y = scroll_y;
7753 render_pos.y = draw_window->DC.CursorPos.y;
7754 }
7755 }
7756 edit_state.CursorFollow = false;
7757 const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f);
7758
7759 // Draw selection
7760 if (edit_state.StbState.select_start != edit_state.StbState.select_end)
7761 {
7762 const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
7763 const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end);
7764
7765 float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection.
7766 float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
7767 ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg);
7768 ImVec2 rect_pos = render_pos + select_start_offset - render_scroll;
7769 for (const ImWchar* p = text_selected_begin; p < text_selected_end; )
7770 {
7771 if (rect_pos.y > clip_rect.w + g.FontSize)
7772 break;
7773 if (rect_pos.y < clip_rect.y)
7774 {
7775 while (p < text_selected_end)
7776 if (*p++ == '\n')
7777 break;
7778 }
7779 else
7780 {
7781 ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true);
7782 if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((unsigned short)' ') * 0.50f); // So we can see selected empty lines
7783 ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn));
7784 rect.Clip(clip_rect);
7785 if (rect.Overlaps(clip_rect))
7786 draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
7787 }
7788 rect_pos.x = render_pos.x - render_scroll.x;
7789 rect_pos.y += g.FontSize;
7790 }
7791 }
7792
7793 draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf, buf+edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect);
7794
7795 // Draw blinking cursor
7796 ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll;
7797 bool cursor_is_visible = (g.InputTextState.CursorAnim <= 0.0f) || fmodf(g.InputTextState.CursorAnim, 1.20f) <= 0.80f;
7798 if (cursor_is_visible)
7799 draw_window->DrawList->AddLine(cursor_screen_pos + ImVec2(0.0f,-g.FontSize+0.5f), cursor_screen_pos + ImVec2(0.0f,-1.5f), GetColorU32(ImGuiCol_Text));
7800
7801 // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
7802 if (is_editable)
7803 g.OsImePosRequest = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize);
7804 }
7805 else
7806 {
7807 // Render text only
7808 const char* buf_end = NULL;
7809 if (is_multiline)
7810 text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf, &buf_end) * g.FontSize); // We don't need width
7811 draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf, buf_end, 0.0f, is_multiline ? NULL : &clip_rect);
7812 }
7813
7814 if (is_multiline)
7815 {
7816 ImGui::Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line
7817 ImGui::EndChildFrame();
7818 ImGui::EndGroup();
7819 }
7820
7821 if (is_password)
7822 ImGui::PopFont();
7823
7824 // Log as text
7825 if (g.LogEnabled && !is_password)
7826 LogRenderedText(render_pos, buf, NULL);
7827
7828 if (label_size.x > 0)
7829 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
7830
7831 if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)
7832 return enter_pressed;
7833 else
7834 return value_changed;
7835 }
7836
InputText(const char * label,char * buf,size_t buf_size,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)7837 bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
7838 {
7839 IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline()
7840 bool ret = InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data);
7841 return ret;
7842 }
7843
InputTextMultiline(const char * label,char * buf,size_t buf_size,const ImVec2 & size,ImGuiInputTextFlags flags,ImGuiTextEditCallback callback,void * user_data)7844 bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data)
7845 {
7846 bool ret = InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);
7847 return ret;
7848 }
7849
7850 // NB: scalar_format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "display_format" argument)
InputScalarEx(const char * label,ImGuiDataType data_type,void * data_ptr,void * step_ptr,void * step_fast_ptr,const char * scalar_format,ImGuiInputTextFlags extra_flags)7851 bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags)
7852 {
7853 ImGuiWindow* window = GetCurrentWindow();
7854 if (window->SkipItems)
7855 return false;
7856
7857 ImGuiState& g = *GImGui;
7858 const ImGuiStyle& style = g.Style;
7859 const ImVec2 label_size = CalcTextSize(label, NULL, true);
7860
7861 ImGui::BeginGroup();
7862 ImGui::PushID(label);
7863 const ImVec2 button_sz = ImVec2(g.FontSize, g.FontSize) + style.FramePadding*2.0f;
7864 if (step_ptr)
7865 ImGui::PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_sz.x + style.ItemInnerSpacing.x)*2));
7866
7867 char buf[64];
7868 DataTypeFormatString(data_type, data_ptr, scalar_format, buf, IM_ARRAYSIZE(buf));
7869
7870 bool value_changed = false;
7871 if (!(extra_flags & ImGuiInputTextFlags_CharsHexadecimal))
7872 extra_flags |= ImGuiInputTextFlags_CharsDecimal;
7873 extra_flags |= ImGuiInputTextFlags_AutoSelectAll;
7874 if (ImGui::InputText("", buf, IM_ARRAYSIZE(buf), extra_flags))
7875 value_changed = DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, scalar_format);
7876
7877 // Step buttons
7878 if (step_ptr)
7879 {
7880 ImGui::PopItemWidth();
7881 ImGui::SameLine(0, style.ItemInnerSpacing.x);
7882 if (ButtonEx("-", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
7883 {
7884 DataTypeApplyOp(data_type, '-', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
7885 value_changed = true;
7886 }
7887 ImGui::SameLine(0, style.ItemInnerSpacing.x);
7888 if (ButtonEx("+", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
7889 {
7890 DataTypeApplyOp(data_type, '+', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
7891 value_changed = true;
7892 }
7893 }
7894 ImGui::PopID();
7895
7896 if (label_size.x > 0)
7897 {
7898 ImGui::SameLine(0, style.ItemInnerSpacing.x);
7899 RenderText(ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + style.FramePadding.y), label);
7900 ItemSize(label_size, style.FramePadding.y);
7901 }
7902 ImGui::EndGroup();
7903
7904 return value_changed;
7905 }
7906
InputFloat(const char * label,float * v,float step,float step_fast,int decimal_precision,ImGuiInputTextFlags extra_flags)7907 bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags)
7908 {
7909 char display_format[16];
7910 if (decimal_precision < 0)
7911 strcpy(display_format, "%f"); // Ideally we'd have a minimum decimal precision of 1 to visually denote that this is a float, while hiding non-significant digits? %f doesn't have a minimum of 1
7912 else
7913 ImFormatString(display_format, 16, "%%.%df", decimal_precision);
7914 return InputScalarEx(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), display_format, extra_flags);
7915 }
7916
InputInt(const char * label,int * v,int step,int step_fast,ImGuiInputTextFlags extra_flags)7917 bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags)
7918 {
7919 // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes.
7920 const char* scalar_format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d";
7921 return InputScalarEx(label, ImGuiDataType_Int, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), scalar_format, extra_flags);
7922 }
7923
InputFloatN(const char * label,float * v,int components,int decimal_precision,ImGuiInputTextFlags extra_flags)7924 bool ImGui::InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags)
7925 {
7926 ImGuiWindow* window = GetCurrentWindow();
7927 if (window->SkipItems)
7928 return false;
7929
7930 ImGuiState& g = *GImGui;
7931 bool value_changed = false;
7932 ImGui::BeginGroup();
7933 ImGui::PushID(label);
7934 PushMultiItemsWidths(components);
7935 for (int i = 0; i < components; i++)
7936 {
7937 ImGui::PushID(i);
7938 value_changed |= ImGui::InputFloat("##v", &v[i], 0, 0, decimal_precision, extra_flags);
7939 ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
7940 ImGui::PopID();
7941 ImGui::PopItemWidth();
7942 }
7943 ImGui::PopID();
7944
7945 window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
7946 ImGui::TextUnformatted(label, FindRenderedTextEnd(label));
7947 ImGui::EndGroup();
7948
7949 return value_changed;
7950 }
7951
InputFloat2(const char * label,float v[2],int decimal_precision,ImGuiInputTextFlags extra_flags)7952 bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags)
7953 {
7954 return InputFloatN(label, v, 2, decimal_precision, extra_flags);
7955 }
7956
InputFloat3(const char * label,float v[3],int decimal_precision,ImGuiInputTextFlags extra_flags)7957 bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags)
7958 {
7959 return InputFloatN(label, v, 3, decimal_precision, extra_flags);
7960 }
7961
InputFloat4(const char * label,float v[4],int decimal_precision,ImGuiInputTextFlags extra_flags)7962 bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags)
7963 {
7964 return InputFloatN(label, v, 4, decimal_precision, extra_flags);
7965 }
7966
InputIntN(const char * label,int * v,int components,ImGuiInputTextFlags extra_flags)7967 bool ImGui::InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags)
7968 {
7969 ImGuiWindow* window = GetCurrentWindow();
7970 if (window->SkipItems)
7971 return false;
7972
7973 ImGuiState& g = *GImGui;
7974 bool value_changed = false;
7975 ImGui::BeginGroup();
7976 ImGui::PushID(label);
7977 PushMultiItemsWidths(components);
7978 for (int i = 0; i < components; i++)
7979 {
7980 ImGui::PushID(i);
7981 value_changed |= ImGui::InputInt("##v", &v[i], 0, 0, extra_flags);
7982 ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);
7983 ImGui::PopID();
7984 ImGui::PopItemWidth();
7985 }
7986 ImGui::PopID();
7987
7988 window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
7989 ImGui::TextUnformatted(label, FindRenderedTextEnd(label));
7990 ImGui::EndGroup();
7991
7992 return value_changed;
7993 }
7994
InputInt2(const char * label,int v[2],ImGuiInputTextFlags extra_flags)7995 bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags)
7996 {
7997 return InputIntN(label, v, 2, extra_flags);
7998 }
7999
InputInt3(const char * label,int v[3],ImGuiInputTextFlags extra_flags)8000 bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags)
8001 {
8002 return InputIntN(label, v, 3, extra_flags);
8003 }
8004
InputInt4(const char * label,int v[4],ImGuiInputTextFlags extra_flags)8005 bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags)
8006 {
8007 return InputIntN(label, v, 4, extra_flags);
8008 }
8009
Items_ArrayGetter(void * data,int idx,const char ** out_text)8010 static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
8011 {
8012 const char** items = (const char**)data;
8013 if (out_text)
8014 *out_text = items[idx];
8015 return true;
8016 }
8017
Items_SingleStringGetter(void * data,int idx,const char ** out_text)8018 static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
8019 {
8020 // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
8021 const char* items_separated_by_zeros = (const char*)data;
8022 int items_count = 0;
8023 const char* p = items_separated_by_zeros;
8024 while (*p)
8025 {
8026 if (idx == items_count)
8027 break;
8028 p += strlen(p) + 1;
8029 items_count++;
8030 }
8031 if (!*p)
8032 return false;
8033 if (out_text)
8034 *out_text = p;
8035 return true;
8036 }
8037
8038 // Combo box helper allowing to pass an array of strings.
Combo(const char * label,int * current_item,const char ** items,int items_count,int height_in_items)8039 bool ImGui::Combo(const char* label, int* current_item, const char** items, int items_count, int height_in_items)
8040 {
8041 const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
8042 return value_changed;
8043 }
8044
8045 // Combo box helper allowing to pass all items in a single string.
Combo(const char * label,int * current_item,const char * items_separated_by_zeros,int height_in_items)8046 bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
8047 {
8048 int items_count = 0;
8049 const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open
8050 while (*p)
8051 {
8052 p += strlen(p) + 1;
8053 items_count++;
8054 }
8055 bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
8056 return value_changed;
8057 }
8058
8059 // Combo box function.
Combo(const char * label,int * current_item,bool (* items_getter)(void *,int,const char **),void * data,int items_count,int height_in_items)8060 bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
8061 {
8062 ImGuiWindow* window = GetCurrentWindow();
8063 if (window->SkipItems)
8064 return false;
8065
8066 ImGuiState& g = *GImGui;
8067 const ImGuiStyle& style = g.Style;
8068 const ImGuiID id = window->GetID(label);
8069 const float w = CalcItemWidth();
8070
8071 const ImVec2 label_size = CalcTextSize(label, NULL, true);
8072 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
8073 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
8074 ItemSize(total_bb, style.FramePadding.y);
8075 if (!ItemAdd(total_bb, &id))
8076 return false;
8077
8078 const float arrow_size = (g.FontSize + style.FramePadding.x * 2.0f);
8079 const bool hovered = IsHovered(frame_bb, id);
8080 bool popup_opened = IsPopupOpen(id);
8081 bool popup_opened_now = false;
8082
8083 const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
8084 RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
8085 RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(popup_opened || hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding); // FIXME-ROUNDING
8086 RenderCollapseTriangle(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y) + style.FramePadding, true);
8087
8088 if (*current_item >= 0 && *current_item < items_count)
8089 {
8090 const char* item_text;
8091 if (items_getter(data, *current_item, &item_text))
8092 RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, item_text, NULL, NULL);
8093 }
8094
8095 if (label_size.x > 0)
8096 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
8097
8098 if (hovered)
8099 {
8100 SetHoveredID(id);
8101 if (g.IO.MouseClicked[0])
8102 {
8103 SetActiveID(0);
8104 if (IsPopupOpen(id))
8105 {
8106 ClosePopup(id);
8107 }
8108 else
8109 {
8110 FocusWindow(window);
8111 OpenPopup(label);
8112 popup_opened = popup_opened_now = true;
8113 }
8114 }
8115 }
8116
8117 bool value_changed = false;
8118 if (IsPopupOpen(id))
8119 {
8120 // Size default to hold ~7 items
8121 if (height_in_items < 0)
8122 height_in_items = 7;
8123
8124 float popup_height = (label_size.y + style.ItemSpacing.y) * ImMin(items_count, height_in_items) + (style.FramePadding.y * 3);
8125 float popup_y1 = frame_bb.Max.y;
8126 float popup_y2 = ImClamp(popup_y1 + popup_height, popup_y1, g.IO.DisplaySize.y - style.DisplaySafeAreaPadding.y);
8127 if ((popup_y2 - popup_y1) < ImMin(popup_height, frame_bb.Min.y - style.DisplaySafeAreaPadding.y))
8128 {
8129 // Position our combo ABOVE because there's more space to fit! (FIXME: Handle in Begin() or use a shared helper. We have similar code in Begin() for popup placement)
8130 popup_y1 = ImClamp(frame_bb.Min.y - popup_height, style.DisplaySafeAreaPadding.y, frame_bb.Min.y);
8131 popup_y2 = frame_bb.Min.y;
8132 }
8133 ImRect popup_rect(ImVec2(frame_bb.Min.x, popup_y1), ImVec2(frame_bb.Max.x, popup_y2));
8134 ImGui::SetNextWindowPos(popup_rect.Min);
8135 ImGui::SetNextWindowSize(popup_rect.GetSize());
8136 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
8137
8138 const ImGuiWindowFlags flags = ImGuiWindowFlags_ComboBox | ((window->Flags & ImGuiWindowFlags_ShowBorders) ? ImGuiWindowFlags_ShowBorders : 0);
8139 if (BeginPopupEx(label, flags))
8140 {
8141 // Display items
8142 ImGui::Spacing();
8143 for (int i = 0; i < items_count; i++)
8144 {
8145 ImGui::PushID((void*)(intptr_t)i);
8146 const bool item_selected = (i == *current_item);
8147 const char* item_text;
8148 if (!items_getter(data, i, &item_text))
8149 item_text = "*Unknown item*";
8150 if (ImGui::Selectable(item_text, item_selected))
8151 {
8152 SetActiveID(0);
8153 value_changed = true;
8154 *current_item = i;
8155 }
8156 if (item_selected && popup_opened_now)
8157 ImGui::SetScrollHere();
8158 ImGui::PopID();
8159 }
8160 ImGui::EndPopup();
8161 }
8162 ImGui::PopStyleVar();
8163 }
8164 return value_changed;
8165 }
8166
8167 // Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image.
8168 // But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID.
Selectable(const char * label,bool selected,ImGuiSelectableFlags flags,const ImVec2 & size_arg)8169 bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
8170 {
8171 ImGuiWindow* window = GetCurrentWindow();
8172 if (window->SkipItems)
8173 return false;
8174
8175 ImGuiState& g = *GImGui;
8176 const ImGuiStyle& style = g.Style;
8177
8178 if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
8179 PopClipRect();
8180
8181 ImGuiID id = window->GetID(label);
8182 ImVec2 label_size = CalcTextSize(label, NULL, true);
8183 ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
8184 ImVec2 pos = window->DC.CursorPos;
8185 pos.y += window->DC.CurrentLineTextBaseOffset;
8186 ImRect bb(pos, pos + size);
8187 ItemSize(bb);
8188
8189 // Fill horizontal space.
8190 ImVec2 window_padding = window->WindowPadding;
8191 float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? ImGui::GetWindowContentRegionMax().x : ImGui::GetContentRegionMax().x;
8192 float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x);
8193 ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y);
8194 ImRect bb_with_spacing(pos, pos + size_draw);
8195 if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth))
8196 bb_with_spacing.Max.x += window_padding.x;
8197
8198 // Selectables are tightly packed together, we extend the box to cover spacing between selectable.
8199 float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f);
8200 float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f);
8201 float spacing_R = style.ItemSpacing.x - spacing_L;
8202 float spacing_D = style.ItemSpacing.y - spacing_U;
8203 bb_with_spacing.Min.x -= spacing_L;
8204 bb_with_spacing.Min.y -= spacing_U;
8205 bb_with_spacing.Max.x += spacing_R;
8206 bb_with_spacing.Max.y += spacing_D;
8207 if (!ItemAdd(bb_with_spacing, &id))
8208 {
8209 if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
8210 PushColumnClipRect();
8211 return false;
8212 }
8213
8214 ImGuiButtonFlags button_flags = 0;
8215 if (flags & ImGuiSelectableFlags_Menu) button_flags |= ImGuiButtonFlags_PressedOnClick;
8216 if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnClick|ImGuiButtonFlags_PressedOnRelease;
8217 if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled;
8218 if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnDoubleClick;
8219 bool hovered, held;
8220 bool pressed = ButtonBehavior(bb_with_spacing, id, &hovered, &held, button_flags);
8221 if (flags & ImGuiSelectableFlags_Disabled)
8222 selected = false;
8223
8224 // Render
8225 if (hovered || selected)
8226 {
8227 const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
8228 RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, 0.0f);
8229 }
8230
8231 if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
8232 {
8233 PushColumnClipRect();
8234 bb_with_spacing.Max.x -= (ImGui::GetContentRegionMax().x - max_x);
8235 }
8236
8237 if (flags & ImGuiSelectableFlags_Disabled) ImGui::PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
8238 RenderTextClipped(bb.Min, bb_with_spacing.Max, label, NULL, &label_size);
8239 if (flags & ImGuiSelectableFlags_Disabled) ImGui::PopStyleColor();
8240
8241 // Automatically close popups
8242 if (pressed && !(flags & ImGuiSelectableFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
8243 ImGui::CloseCurrentPopup();
8244 return pressed;
8245 }
8246
Selectable(const char * label,bool * p_selected,ImGuiSelectableFlags flags,const ImVec2 & size_arg)8247 bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
8248 {
8249 if (ImGui::Selectable(label, *p_selected, flags, size_arg))
8250 {
8251 *p_selected = !*p_selected;
8252 return true;
8253 }
8254 return false;
8255 }
8256
8257 // Helper to calculate the size of a listbox and display a label on the right.
8258 // Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty"
ListBoxHeader(const char * label,const ImVec2 & size_arg)8259 bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
8260 {
8261 ImGuiWindow* window = GetCurrentWindow();
8262 if (window->SkipItems)
8263 return false;
8264
8265 const ImGuiStyle& style = ImGui::GetStyle();
8266 const ImGuiID id = ImGui::GetID(label);
8267 const ImVec2 label_size = CalcTextSize(label, NULL, true);
8268
8269 // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
8270 ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), ImGui::GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y);
8271 ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y));
8272 ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
8273 ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
8274 window->DC.LastItemRect = bb;
8275
8276 ImGui::BeginGroup();
8277 if (label_size.x > 0)
8278 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
8279
8280 ImGui::BeginChildFrame(id, frame_bb.GetSize());
8281 return true;
8282 }
8283
ListBoxHeader(const char * label,int items_count,int height_in_items)8284 bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items)
8285 {
8286 // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
8287 // However we don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size.
8288 // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution.
8289 if (height_in_items < 0)
8290 height_in_items = ImMin(items_count, 7);
8291 float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f);
8292
8293 // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild().
8294 ImVec2 size;
8295 size.x = 0.0f;
8296 size.y = ImGui::GetTextLineHeightWithSpacing() * height_in_items_f + ImGui::GetStyle().ItemSpacing.y;
8297 return ImGui::ListBoxHeader(label, size);
8298 }
8299
ListBoxFooter()8300 void ImGui::ListBoxFooter()
8301 {
8302 ImGuiWindow* parent_window = GetParentWindow();
8303 const ImRect bb = parent_window->DC.LastItemRect;
8304 const ImGuiStyle& style = ImGui::GetStyle();
8305
8306 ImGui::EndChildFrame();
8307
8308 // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect)
8309 // We call SameLine() to restore DC.CurrentLine* data
8310 ImGui::SameLine();
8311 parent_window->DC.CursorPos = bb.Min;
8312 ItemSize(bb, style.FramePadding.y);
8313 ImGui::EndGroup();
8314 }
8315
ListBox(const char * label,int * current_item,const char ** items,int items_count,int height_items)8316 bool ImGui::ListBox(const char* label, int* current_item, const char** items, int items_count, int height_items)
8317 {
8318 const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items);
8319 return value_changed;
8320 }
8321
ListBox(const char * label,int * current_item,bool (* items_getter)(void *,int,const char **),void * data,int items_count,int height_in_items)8322 bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
8323 {
8324 if (!ImGui::ListBoxHeader(label, items_count, height_in_items))
8325 return false;
8326
8327 // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper.
8328 bool value_changed = false;
8329 ImGuiListClipper clipper(items_count, ImGui::GetTextLineHeightWithSpacing());
8330 for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
8331 {
8332 const bool item_selected = (i == *current_item);
8333 const char* item_text;
8334 if (!items_getter(data, i, &item_text))
8335 item_text = "*Unknown item*";
8336
8337 ImGui::PushID(i);
8338 if (ImGui::Selectable(item_text, item_selected))
8339 {
8340 *current_item = i;
8341 value_changed = true;
8342 }
8343 ImGui::PopID();
8344 }
8345 clipper.End();
8346 ImGui::ListBoxFooter();
8347 return value_changed;
8348 }
8349
MenuItem(const char * label,const char * shortcut,bool selected,bool enabled)8350 bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)
8351 {
8352 ImGuiWindow* window = GetCurrentWindow();
8353 if (window->SkipItems)
8354 return false;
8355
8356 ImGuiState& g = *GImGui;
8357 ImVec2 pos = window->DC.CursorPos;
8358 ImVec2 label_size = CalcTextSize(label, NULL, true);
8359 ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f);
8360 float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame
8361 float extra_w = ImMax(0.0f, ImGui::GetContentRegionAvail().x - w);
8362
8363 bool pressed = ImGui::Selectable(label, false, ImGuiSelectableFlags_MenuItem | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
8364 if (shortcut_size.x > 0.0f)
8365 {
8366 ImGui::PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
8367 RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
8368 ImGui::PopStyleColor();
8369 }
8370
8371 if (selected)
8372 RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), GetColorU32(ImGuiCol_Text));
8373
8374 return pressed;
8375 }
8376
MenuItem(const char * label,const char * shortcut,bool * p_selected,bool enabled)8377 bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled)
8378 {
8379 if (ImGui::MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled))
8380 {
8381 if (p_selected)
8382 *p_selected = !*p_selected;
8383 return true;
8384 }
8385 return false;
8386 }
8387
BeginMainMenuBar()8388 bool ImGui::BeginMainMenuBar()
8389 {
8390 ImGuiState& g = *GImGui;
8391 ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f));
8392 ImGui::SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.FontBaseSize + g.Style.FramePadding.y * 2.0f));
8393 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
8394 ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0));
8395 if (!ImGui::Begin("##MainMenuBar", NULL, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_MenuBar)
8396 || !ImGui::BeginMenuBar())
8397 {
8398 ImGui::End();
8399 ImGui::PopStyleVar(2);
8400 return false;
8401 }
8402 g.CurrentWindow->DC.MenuBarOffsetX += g.Style.DisplaySafeAreaPadding.x;
8403 return true;
8404 }
8405
EndMainMenuBar()8406 void ImGui::EndMainMenuBar()
8407 {
8408 ImGui::EndMenuBar();
8409 ImGui::End();
8410 ImGui::PopStyleVar(2);
8411 }
8412
BeginMenuBar()8413 bool ImGui::BeginMenuBar()
8414 {
8415 ImGuiWindow* window = GetCurrentWindow();
8416 if (window->SkipItems)
8417 return false;
8418 if (!(window->Flags & ImGuiWindowFlags_MenuBar))
8419 return false;
8420
8421 IM_ASSERT(!window->DC.MenuBarAppending);
8422 ImGui::BeginGroup(); // Save position
8423 ImGui::PushID("##menubar");
8424 ImRect rect = window->MenuBarRect();
8425 PushClipRect(ImVec2(rect.Min.x+0.5f, rect.Min.y-0.5f+window->BorderSize), ImVec2(rect.Max.x+0.5f, rect.Max.y-0.5f), false);
8426 window->DC.CursorPos = ImVec2(rect.Min.x + window->DC.MenuBarOffsetX, rect.Min.y);// + g.Style.FramePadding.y);
8427 window->DC.LayoutType = ImGuiLayoutType_Horizontal;
8428 window->DC.MenuBarAppending = true;
8429 ImGui::AlignFirstTextHeightToWidgets();
8430 return true;
8431 }
8432
EndMenuBar()8433 void ImGui::EndMenuBar()
8434 {
8435 ImGuiWindow* window = GetCurrentWindow();
8436 if (window->SkipItems)
8437 return;
8438
8439 IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);
8440 IM_ASSERT(window->DC.MenuBarAppending);
8441 PopClipRect();
8442 ImGui::PopID();
8443 window->DC.MenuBarOffsetX = window->DC.CursorPos.x - window->MenuBarRect().Min.x;
8444 window->DC.GroupStack.back().AdvanceCursor = false;
8445 ImGui::EndGroup();
8446 window->DC.LayoutType = ImGuiLayoutType_Vertical;
8447 window->DC.MenuBarAppending = false;
8448 }
8449
BeginMenu(const char * label,bool enabled)8450 bool ImGui::BeginMenu(const char* label, bool enabled)
8451 {
8452 ImGuiWindow* window = GetCurrentWindow();
8453 if (window->SkipItems)
8454 return false;
8455
8456 ImGuiState& g = *GImGui;
8457 const ImGuiStyle& style = g.Style;
8458 const ImGuiID id = window->GetID(label);
8459
8460 ImVec2 label_size = CalcTextSize(label, NULL, true);
8461 ImGuiWindow* backed_focused_window = g.FocusedWindow;
8462
8463 bool pressed;
8464 bool opened = IsPopupOpen(id);
8465 bool menuset_opened = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenedPopupStack.Size > g.CurrentPopupStack.Size && g.OpenedPopupStack[g.CurrentPopupStack.Size].ParentMenuSet == window->GetID("##menus"));
8466 if (menuset_opened)
8467 g.FocusedWindow = window;
8468
8469 ImVec2 popup_pos, pos = window->DC.CursorPos;
8470 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
8471 {
8472 popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight());
8473 window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
8474 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
8475 float w = label_size.x;
8476 pressed = ImGui::Selectable(label, opened, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
8477 ImGui::PopStyleVar();
8478 ImGui::SameLine();
8479 window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
8480 }
8481 else
8482 {
8483 popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
8484 float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame
8485 float extra_w = ImMax(0.0f, ImGui::GetContentRegionAvail().x - w);
8486 pressed = ImGui::Selectable(label, opened, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
8487 if (!enabled) ImGui::PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
8488 RenderCollapseTriangle(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), false);
8489 if (!enabled) ImGui::PopStyleColor();
8490 }
8491
8492 bool hovered = enabled && IsHovered(window->DC.LastItemRect, id);
8493 if (menuset_opened)
8494 g.FocusedWindow = backed_focused_window;
8495
8496 bool want_open = false, want_close = false;
8497 if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
8498 {
8499 // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
8500 bool moving_within_opened_triangle = false;
8501 if (g.HoveredWindow == window && g.OpenedPopupStack.Size > g.CurrentPopupStack.Size && g.OpenedPopupStack[g.CurrentPopupStack.Size].ParentWindow == window)
8502 {
8503 if (ImGuiWindow* next_window = g.OpenedPopupStack[g.CurrentPopupStack.Size].Window)
8504 {
8505 ImRect next_window_rect = next_window->Rect();
8506 ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
8507 ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
8508 ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
8509 float extra = ImClamp(fabsf(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
8510 ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues
8511 tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus
8512 tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
8513 moving_within_opened_triangle = ImIsPointInTriangle(g.IO.MousePos, ta, tb, tc);
8514 //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? 0x80008000 : 0x80000080); window->DrawList->PopClipRect(); // Debug
8515 }
8516 }
8517
8518 want_close = (opened && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle);
8519 want_open = (!opened && hovered && !moving_within_opened_triangle) || (!opened && hovered && pressed);
8520 }
8521 else if (opened && pressed && menuset_opened) // menu-bar: click open menu to close
8522 {
8523 want_close = true;
8524 want_open = opened = false;
8525 }
8526 else if (pressed || (hovered && menuset_opened && !opened)) // menu-bar: first click to open, then hover to open others
8527 want_open = true;
8528
8529 if (want_close && IsPopupOpen(id))
8530 ClosePopupToLevel(GImGui->CurrentPopupStack.Size);
8531
8532 if (!opened && want_open && g.OpenedPopupStack.Size > g.CurrentPopupStack.Size)
8533 {
8534 // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
8535 ImGui::OpenPopup(label);
8536 return false;
8537 }
8538
8539 opened |= want_open;
8540 if (want_open)
8541 ImGui::OpenPopup(label);
8542
8543 if (opened)
8544 {
8545 ImGui::SetNextWindowPos(popup_pos, ImGuiSetCond_Always);
8546 ImGuiWindowFlags flags = ImGuiWindowFlags_ShowBorders | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu);
8547 opened = BeginPopupEx(label, flags); // opened can be 'false' when the popup is completely clipped (e.g. zero size display)
8548 }
8549
8550 return opened;
8551 }
8552
EndMenu()8553 void ImGui::EndMenu()
8554 {
8555 ImGui::EndPopup();
8556 }
8557
8558 // A little colored square. Return true when clicked.
8559 // FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip.
ColorButton(const ImVec4 & col,bool small_height,bool outline_border)8560 bool ImGui::ColorButton(const ImVec4& col, bool small_height, bool outline_border)
8561 {
8562 ImGuiWindow* window = GetCurrentWindow();
8563 if (window->SkipItems)
8564 return false;
8565
8566 ImGuiState& g = *GImGui;
8567 const ImGuiStyle& style = g.Style;
8568 const ImGuiID id = window->GetID("#colorbutton");
8569 const float square_size = g.FontSize;
8570 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(square_size + style.FramePadding.y*2, square_size + (small_height ? 0 : style.FramePadding.y*2)));
8571 ItemSize(bb, small_height ? 0.0f : style.FramePadding.y);
8572 if (!ItemAdd(bb, &id))
8573 return false;
8574
8575 bool hovered, held;
8576 bool pressed = ButtonBehavior(bb, id, &hovered, &held);
8577 RenderFrame(bb.Min, bb.Max, GetColorU32(col), outline_border, style.FrameRounding);
8578
8579 if (hovered)
8580 ImGui::SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col.x, col.y, col.z, col.w, IM_F32_TO_INT8(col.x), IM_F32_TO_INT8(col.y), IM_F32_TO_INT8(col.z), IM_F32_TO_INT8(col.z));
8581
8582 return pressed;
8583 }
8584
ColorEdit3(const char * label,float col[3])8585 bool ImGui::ColorEdit3(const char* label, float col[3])
8586 {
8587 float col4[4];
8588 col4[0] = col[0];
8589 col4[1] = col[1];
8590 col4[2] = col[2];
8591 col4[3] = 1.0f;
8592 const bool value_changed = ImGui::ColorEdit4(label, col4, false);
8593 col[0] = col4[0];
8594 col[1] = col4[1];
8595 col[2] = col4[2];
8596 return value_changed;
8597 }
8598
8599 // Edit colors components (each component in 0.0f..1.0f range
8600 // Use CTRL-Click to input value and TAB to go to next item.
ColorEdit4(const char * label,float col[4],bool alpha)8601 bool ImGui::ColorEdit4(const char* label, float col[4], bool alpha)
8602 {
8603 ImGuiWindow* window = GetCurrentWindow();
8604 if (window->SkipItems)
8605 return false;
8606
8607 ImGuiState& g = *GImGui;
8608 const ImGuiStyle& style = g.Style;
8609 const ImGuiID id = window->GetID(label);
8610 const float w_full = CalcItemWidth();
8611 const float square_sz = (g.FontSize + style.FramePadding.y * 2.0f);
8612
8613 ImGuiColorEditMode edit_mode = window->DC.ColorEditMode;
8614 if (edit_mode == ImGuiColorEditMode_UserSelect || edit_mode == ImGuiColorEditMode_UserSelectShowButton)
8615 edit_mode = g.ColorEditModeStorage.GetInt(id, 0) % 3;
8616
8617 float f[4] = { col[0], col[1], col[2], col[3] };
8618 if (edit_mode == ImGuiColorEditMode_HSV)
8619 ImGui::ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
8620
8621 int i[4] = { IM_F32_TO_INT8(f[0]), IM_F32_TO_INT8(f[1]), IM_F32_TO_INT8(f[2]), IM_F32_TO_INT8(f[3]) };
8622
8623 int components = alpha ? 4 : 3;
8624 bool value_changed = false;
8625
8626 ImGui::BeginGroup();
8627 ImGui::PushID(label);
8628
8629 const bool hsv = (edit_mode == 1);
8630 switch (edit_mode)
8631 {
8632 case ImGuiColorEditMode_RGB:
8633 case ImGuiColorEditMode_HSV:
8634 {
8635 // RGB/HSV 0..255 Sliders
8636 const float w_items_all = w_full - (square_sz + style.ItemInnerSpacing.x);
8637 const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
8638 const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
8639
8640 const bool hide_prefix = (w_item_one <= CalcTextSize("M:999").x);
8641 const char* ids[4] = { "##X", "##Y", "##Z", "##W" };
8642 const char* fmt_table[3][4] =
8643 {
8644 { "%3.0f", "%3.0f", "%3.0f", "%3.0f" },
8645 { "R:%3.0f", "G:%3.0f", "B:%3.0f", "A:%3.0f" },
8646 { "H:%3.0f", "S:%3.0f", "V:%3.0f", "A:%3.0f" }
8647 };
8648 const char** fmt = hide_prefix ? fmt_table[0] : hsv ? fmt_table[2] : fmt_table[1];
8649
8650 ImGui::PushItemWidth(w_item_one);
8651 for (int n = 0; n < components; n++)
8652 {
8653 if (n > 0)
8654 ImGui::SameLine(0, style.ItemInnerSpacing.x);
8655 if (n + 1 == components)
8656 ImGui::PushItemWidth(w_item_last);
8657 value_changed |= ImGui::DragInt(ids[n], &i[n], 1.0f, 0, 255, fmt[n]);
8658 }
8659 ImGui::PopItemWidth();
8660 ImGui::PopItemWidth();
8661 }
8662 break;
8663 case ImGuiColorEditMode_HEX:
8664 {
8665 // RGB Hexadecimal Input
8666 const float w_slider_all = w_full - square_sz;
8667 char buf[64];
8668 if (alpha)
8669 ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", i[0], i[1], i[2], i[3]);
8670 else
8671 ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", i[0], i[1], i[2]);
8672 ImGui::PushItemWidth(w_slider_all - style.ItemInnerSpacing.x);
8673 if (ImGui::InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase))
8674 {
8675 value_changed |= true;
8676 char* p = buf;
8677 while (*p == '#' || ImCharIsSpace(*p))
8678 p++;
8679 i[0] = i[1] = i[2] = i[3] = 0;
8680 if (alpha)
8681 sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned)
8682 else
8683 sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
8684 }
8685 ImGui::PopItemWidth();
8686 }
8687 break;
8688 }
8689
8690 ImGui::SameLine(0, style.ItemInnerSpacing.x);
8691
8692 const ImVec4 col_display(col[0], col[1], col[2], 1.0f);
8693 if (ImGui::ColorButton(col_display))
8694 g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away!
8695
8696 // Recreate our own tooltip over's ColorButton() one because we want to display correct alpha here
8697 if (ImGui::IsItemHovered())
8698 ImGui::SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col[0], col[1], col[2], col[3], IM_F32_TO_INT8(col[0]), IM_F32_TO_INT8(col[1]), IM_F32_TO_INT8(col[2]), IM_F32_TO_INT8(col[3]));
8699
8700 if (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton)
8701 {
8702 ImGui::SameLine(0, style.ItemInnerSpacing.x);
8703 const char* button_titles[3] = { "RGB", "HSV", "HEX" };
8704 if (ButtonEx(button_titles[edit_mode], ImVec2(0,0), ImGuiButtonFlags_DontClosePopups))
8705 g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away!
8706 }
8707
8708 const char* label_display_end = FindRenderedTextEnd(label);
8709 if (label != label_display_end)
8710 {
8711 ImGui::SameLine(0, (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton) ? -1.0f : style.ItemInnerSpacing.x);
8712 ImGui::TextUnformatted(label, label_display_end);
8713 }
8714
8715 // Convert back
8716 for (int n = 0; n < 4; n++)
8717 f[n] = i[n] / 255.0f;
8718 if (edit_mode == 1)
8719 ImGui::ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
8720
8721 if (value_changed)
8722 {
8723 col[0] = f[0];
8724 col[1] = f[1];
8725 col[2] = f[2];
8726 if (alpha)
8727 col[3] = f[3];
8728 }
8729
8730 ImGui::PopID();
8731 ImGui::EndGroup();
8732
8733 return value_changed;
8734 }
8735
ColorEditMode(ImGuiColorEditMode mode)8736 void ImGui::ColorEditMode(ImGuiColorEditMode mode)
8737 {
8738 ImGuiWindow* window = GetCurrentWindow();
8739 window->DC.ColorEditMode = mode;
8740 }
8741
8742 // Horizontal separating line.
Separator()8743 void ImGui::Separator()
8744 {
8745 ImGuiWindow* window = GetCurrentWindow();
8746 if (window->SkipItems)
8747 return;
8748
8749 if (window->DC.ColumnsCount > 1)
8750 PopClipRect();
8751
8752 float x1 = window->Pos.x;
8753 float x2 = window->Pos.x + window->Size.x;
8754 if (!window->DC.GroupStack.empty())
8755 x1 += window->DC.IndentX;
8756
8757 const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y));
8758 ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit // FIXME: Height should be 1.0f not 0.0f ?
8759 if (!ItemAdd(bb, NULL))
8760 {
8761 if (window->DC.ColumnsCount > 1)
8762 PushColumnClipRect();
8763 return;
8764 }
8765
8766 window->DrawList->AddLine(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border));
8767
8768 ImGuiState& g = *GImGui;
8769 if (g.LogEnabled)
8770 ImGui::LogText(IM_NEWLINE "--------------------------------");
8771
8772 if (window->DC.ColumnsCount > 1)
8773 {
8774 PushColumnClipRect();
8775 window->DC.ColumnsCellMinY = window->DC.CursorPos.y;
8776 }
8777 }
8778
Spacing()8779 void ImGui::Spacing()
8780 {
8781 ImGuiWindow* window = GetCurrentWindow();
8782 if (window->SkipItems)
8783 return;
8784 ItemSize(ImVec2(0,0));
8785 }
8786
Dummy(const ImVec2 & size)8787 void ImGui::Dummy(const ImVec2& size)
8788 {
8789 ImGuiWindow* window = GetCurrentWindow();
8790 if (window->SkipItems)
8791 return;
8792
8793 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
8794 ItemSize(bb);
8795 ItemAdd(bb, NULL);
8796 }
8797
IsRectVisible(const ImVec2 & size)8798 bool ImGui::IsRectVisible(const ImVec2& size)
8799 {
8800 ImGuiWindow* window = GetCurrentWindowRead();
8801 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
8802 }
8803
BeginGroup()8804 void ImGui::BeginGroup()
8805 {
8806 ImGuiWindow* window = GetCurrentWindow();
8807
8808 window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
8809 ImGuiGroupData& group_data = window->DC.GroupStack.back();
8810 group_data.BackupCursorPos = window->DC.CursorPos;
8811 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
8812 group_data.BackupIndentX = window->DC.IndentX;
8813 group_data.BackupCurrentLineHeight = window->DC.CurrentLineHeight;
8814 group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
8815 group_data.BackupLogLinePosY = window->DC.LogLinePosY;
8816 group_data.AdvanceCursor = true;
8817
8818 window->DC.IndentX = window->DC.CursorPos.x - window->Pos.x;
8819 window->DC.CursorMaxPos = window->DC.CursorPos;
8820 window->DC.CurrentLineHeight = 0.0f;
8821 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
8822 }
8823
EndGroup()8824 void ImGui::EndGroup()
8825 {
8826 ImGuiWindow* window = GetCurrentWindow();
8827 ImGuiStyle& style = ImGui::GetStyle();
8828
8829 IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls
8830
8831 ImGuiGroupData& group_data = window->DC.GroupStack.back();
8832
8833 ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
8834 group_bb.Max.y -= style.ItemSpacing.y; // Cancel out last vertical spacing because we are adding one ourselves.
8835 group_bb.Max = ImMax(group_bb.Min, group_bb.Max);
8836
8837 window->DC.CursorPos = group_data.BackupCursorPos;
8838 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
8839 window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight;
8840 window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
8841 window->DC.IndentX = group_data.BackupIndentX;
8842 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
8843
8844 if (group_data.AdvanceCursor)
8845 {
8846 window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrentLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.
8847 ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset);
8848 ItemAdd(group_bb, NULL);
8849 }
8850
8851 window->DC.GroupStack.pop_back();
8852
8853 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, 0xFFFF00FF); // Debug
8854 }
8855
8856 // Gets back to previous line and continue with horizontal layout
8857 // pos_x == 0 : follow on previous item
8858 // pos_x != 0 : align to specified column
8859 // spacing_w < 0 : use default spacing if column_x==0, no spacing if column_x!=0
8860 // spacing_w >= 0 : enforce spacing
SameLine(float pos_x,float spacing_w)8861 void ImGui::SameLine(float pos_x, float spacing_w)
8862 {
8863 ImGuiWindow* window = GetCurrentWindow();
8864 if (window->SkipItems)
8865 return;
8866
8867 ImGuiState& g = *GImGui;
8868 if (pos_x != 0.0f)
8869 {
8870 if (spacing_w < 0.0f) spacing_w = 0.0f;
8871 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w;
8872 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
8873 }
8874 else
8875 {
8876 if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
8877 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
8878 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
8879 }
8880 window->DC.CurrentLineHeight = window->DC.PrevLineHeight;
8881 window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
8882 }
8883
NextColumn()8884 void ImGui::NextColumn()
8885 {
8886 ImGuiWindow* window = GetCurrentWindow();
8887 if (window->SkipItems)
8888 return;
8889
8890 ImGuiState& g = *GImGui;
8891 if (window->DC.ColumnsCount > 1)
8892 {
8893 ImGui::PopItemWidth();
8894 PopClipRect();
8895
8896 window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
8897 if (++window->DC.ColumnsCurrent < window->DC.ColumnsCount)
8898 {
8899 // Columns 1+ cancel out IndentX
8900 window->DC.ColumnsOffsetX = ImGui::GetColumnOffset(window->DC.ColumnsCurrent) - window->DC.IndentX + g.Style.ItemSpacing.x;
8901 window->DrawList->ChannelsSetCurrent(window->DC.ColumnsCurrent);
8902 }
8903 else
8904 {
8905 window->DC.ColumnsCurrent = 0;
8906 window->DC.ColumnsOffsetX = 0.0f;
8907 window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY;
8908 window->DrawList->ChannelsSetCurrent(0);
8909 }
8910 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
8911 window->DC.CursorPos.y = window->DC.ColumnsCellMinY;
8912 window->DC.CurrentLineHeight = 0.0f;
8913 window->DC.CurrentLineTextBaseOffset = 0.0f;
8914
8915 PushColumnClipRect();
8916 ImGui::PushItemWidth(ImGui::GetColumnWidth() * 0.65f); // FIXME: Move on columns setup
8917 }
8918 }
8919
GetColumnIndex()8920 int ImGui::GetColumnIndex()
8921 {
8922 ImGuiWindow* window = GetCurrentWindowRead();
8923 return window->DC.ColumnsCurrent;
8924 }
8925
GetColumnsCount()8926 int ImGui::GetColumnsCount()
8927 {
8928 ImGuiWindow* window = GetCurrentWindowRead();
8929 return window->DC.ColumnsCount;
8930 }
8931
GetDraggedColumnOffset(int column_index)8932 static float GetDraggedColumnOffset(int column_index)
8933 {
8934 // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
8935 // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
8936 ImGuiState& g = *GImGui;
8937 ImGuiWindow* window = ImGui::GetCurrentWindowRead();
8938 IM_ASSERT(column_index > 0); // We cannot drag column 0. If you get this assert you may have a conflict between the ID of your columns and another widgets.
8939 IM_ASSERT(g.ActiveId == window->DC.ColumnsSetID + ImGuiID(column_index));
8940
8941 float x = g.IO.MousePos.x + g.ActiveClickDeltaToCenter.x - window->Pos.x;
8942 x = ImClamp(x, ImGui::GetColumnOffset(column_index-1)+g.Style.ColumnsMinSpacing, ImGui::GetColumnOffset(column_index+1)-g.Style.ColumnsMinSpacing);
8943
8944 return (float)(int)x;
8945 }
8946
GetColumnOffset(int column_index)8947 float ImGui::GetColumnOffset(int column_index)
8948 {
8949 ImGuiState& g = *GImGui;
8950 ImGuiWindow* window = GetCurrentWindowRead();
8951 if (column_index < 0)
8952 column_index = window->DC.ColumnsCurrent;
8953
8954 if (g.ActiveId)
8955 {
8956 const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(column_index);
8957 if (g.ActiveId == column_id)
8958 return GetDraggedColumnOffset(column_index);
8959 }
8960
8961 IM_ASSERT(column_index < window->DC.ColumnsData.Size);
8962 const float t = window->DC.ColumnsData[column_index].OffsetNorm;
8963 const float x_offset = window->DC.ColumnsMinX + t * (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
8964 return (float)(int)x_offset;
8965 }
8966
SetColumnOffset(int column_index,float offset)8967 void ImGui::SetColumnOffset(int column_index, float offset)
8968 {
8969 ImGuiWindow* window = GetCurrentWindow();
8970 if (column_index < 0)
8971 column_index = window->DC.ColumnsCurrent;
8972
8973 IM_ASSERT(column_index < window->DC.ColumnsData.Size);
8974 const float t = (offset - window->DC.ColumnsMinX) / (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
8975 window->DC.ColumnsData[column_index].OffsetNorm = t;
8976
8977 const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(column_index);
8978 window->DC.StateStorage->SetFloat(column_id, t);
8979 }
8980
GetColumnWidth(int column_index)8981 float ImGui::GetColumnWidth(int column_index)
8982 {
8983 ImGuiWindow* window = GetCurrentWindowRead();
8984 if (column_index < 0)
8985 column_index = window->DC.ColumnsCurrent;
8986
8987 const float w = GetColumnOffset(column_index+1) - GetColumnOffset(column_index);
8988 return w;
8989 }
8990
PushColumnClipRect(int column_index)8991 static void PushColumnClipRect(int column_index)
8992 {
8993 ImGuiWindow* window = ImGui::GetCurrentWindow();
8994 if (column_index < 0)
8995 column_index = window->DC.ColumnsCurrent;
8996
8997 const float x1 = window->Pos.x + ImGui::GetColumnOffset(column_index) - 1;
8998 const float x2 = window->Pos.x + ImGui::GetColumnOffset(column_index+1) - 1;
8999 ImGui::PushClipRect(ImVec2(x1,-FLT_MAX), ImVec2(x2,+FLT_MAX), true);
9000 }
9001
Columns(int columns_count,const char * id,bool border)9002 void ImGui::Columns(int columns_count, const char* id, bool border)
9003 {
9004 ImGuiState& g = *GImGui;
9005 ImGuiWindow* window = GetCurrentWindow();
9006 IM_ASSERT(columns_count >= 1);
9007
9008 if (window->DC.ColumnsCount != 1)
9009 {
9010 if (window->DC.ColumnsCurrent != 0)
9011 ItemSize(ImVec2(0,0)); // Advance to column 0
9012 ImGui::PopItemWidth();
9013 PopClipRect();
9014 window->DrawList->ChannelsMerge();
9015
9016 window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
9017 window->DC.CursorPos.y = window->DC.ColumnsCellMaxY;
9018 }
9019
9020 // Draw columns borders and handle resize at the time of "closing" a columns set
9021 if (window->DC.ColumnsCount != columns_count && window->DC.ColumnsCount != 1 && window->DC.ColumnsShowBorders && !window->SkipItems)
9022 {
9023 const float y1 = window->DC.ColumnsStartPosY;
9024 const float y2 = window->DC.CursorPos.y;
9025 for (int i = 1; i < window->DC.ColumnsCount; i++)
9026 {
9027 float x = window->Pos.x + GetColumnOffset(i);
9028 const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(i);
9029 const ImRect column_rect(ImVec2(x-4,y1),ImVec2(x+4,y2));
9030 if (IsClippedEx(column_rect, &column_id, false))
9031 continue;
9032
9033 bool hovered, held;
9034 ButtonBehavior(column_rect, column_id, &hovered, &held, true);
9035 if (hovered || held)
9036 g.MouseCursor = ImGuiMouseCursor_ResizeEW;
9037
9038 // Draw before resize so our items positioning are in sync with the line being drawn
9039 const ImU32 col = GetColorU32(held ? ImGuiCol_ColumnActive : hovered ? ImGuiCol_ColumnHovered : ImGuiCol_Column);
9040 const float xi = (float)(int)x;
9041 window->DrawList->AddLine(ImVec2(xi, y1+1.0f), ImVec2(xi, y2), col);
9042
9043 if (held)
9044 {
9045 if (g.ActiveIdIsJustActivated)
9046 g.ActiveClickDeltaToCenter.x = x - g.IO.MousePos.x;
9047
9048 x = GetDraggedColumnOffset(i);
9049 SetColumnOffset(i, x);
9050 }
9051 }
9052 }
9053
9054 // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
9055 // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
9056 ImGui::PushID(0x11223347 + (id ? 0 : columns_count));
9057 window->DC.ColumnsSetID = window->GetID(id ? id : "columns");
9058 ImGui::PopID();
9059
9060 // Set state for first column
9061 window->DC.ColumnsCurrent = 0;
9062 window->DC.ColumnsCount = columns_count;
9063 window->DC.ColumnsShowBorders = border;
9064
9065 const float content_region_width = window->SizeContentsExplicit.x ? window->SizeContentsExplicit.x : window->Size.x;
9066 window->DC.ColumnsMinX = window->DC.IndentX; // Lock our horizontal range
9067 window->DC.ColumnsMaxX = content_region_width - window->Scroll.x - ((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x;
9068 window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
9069 window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.CursorPos.y;
9070 window->DC.ColumnsOffsetX = 0.0f;
9071 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
9072
9073 if (window->DC.ColumnsCount != 1)
9074 {
9075 // Cache column offsets
9076 window->DC.ColumnsData.resize(columns_count + 1);
9077 for (int column_index = 0; column_index < columns_count + 1; column_index++)
9078 {
9079 const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(column_index);
9080 KeepAliveID(column_id);
9081 const float default_t = column_index / (float)window->DC.ColumnsCount;
9082 const float t = window->DC.StateStorage->GetFloat(column_id, default_t); // Cheaply store our floating point value inside the integer (could store an union into the map?)
9083 window->DC.ColumnsData[column_index].OffsetNorm = t;
9084 }
9085 window->DrawList->ChannelsSplit(window->DC.ColumnsCount);
9086 PushColumnClipRect();
9087 ImGui::PushItemWidth(ImGui::GetColumnWidth() * 0.65f);
9088 }
9089 else
9090 {
9091 window->DC.ColumnsData.resize(0);
9092 }
9093 }
9094
Indent()9095 void ImGui::Indent()
9096 {
9097 ImGuiState& g = *GImGui;
9098 ImGuiWindow* window = GetCurrentWindow();
9099 window->DC.IndentX += g.Style.IndentSpacing;
9100 window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
9101 }
9102
Unindent()9103 void ImGui::Unindent()
9104 {
9105 ImGuiState& g = *GImGui;
9106 ImGuiWindow* window = GetCurrentWindow();
9107 window->DC.IndentX -= g.Style.IndentSpacing;
9108 window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
9109 }
9110
TreePush(const char * str_id)9111 void ImGui::TreePush(const char* str_id)
9112 {
9113 ImGuiWindow* window = GetCurrentWindow();
9114 ImGui::Indent();
9115 window->DC.TreeDepth++;
9116 PushID(str_id ? str_id : "#TreePush");
9117 }
9118
TreePush(const void * ptr_id)9119 void ImGui::TreePush(const void* ptr_id)
9120 {
9121 ImGuiWindow* window = GetCurrentWindow();
9122 ImGui::Indent();
9123 window->DC.TreeDepth++;
9124 PushID(ptr_id ? ptr_id : (const void*)"#TreePush");
9125 }
9126
TreePop()9127 void ImGui::TreePop()
9128 {
9129 ImGuiWindow* window = GetCurrentWindow();
9130 ImGui::Unindent();
9131 window->DC.TreeDepth--;
9132 PopID();
9133 }
9134
Value(const char * prefix,bool b)9135 void ImGui::Value(const char* prefix, bool b)
9136 {
9137 ImGui::Text("%s: %s", prefix, (b ? "true" : "false"));
9138 }
9139
Value(const char * prefix,int v)9140 void ImGui::Value(const char* prefix, int v)
9141 {
9142 ImGui::Text("%s: %d", prefix, v);
9143 }
9144
Value(const char * prefix,unsigned int v)9145 void ImGui::Value(const char* prefix, unsigned int v)
9146 {
9147 ImGui::Text("%s: %d", prefix, v);
9148 }
9149
Value(const char * prefix,float v,const char * float_format)9150 void ImGui::Value(const char* prefix, float v, const char* float_format)
9151 {
9152 if (float_format)
9153 {
9154 char fmt[64];
9155 ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format);
9156 ImGui::Text(fmt, prefix, v);
9157 }
9158 else
9159 {
9160 ImGui::Text("%s: %.3f", prefix, v);
9161 }
9162 }
9163
9164 // FIXME: May want to remove those helpers?
ValueColor(const char * prefix,const ImVec4 & v)9165 void ImGui::ValueColor(const char* prefix, const ImVec4& v)
9166 {
9167 ImGui::Text("%s: (%.2f,%.2f,%.2f,%.2f)", prefix, v.x, v.y, v.z, v.w);
9168 ImGui::SameLine();
9169 ImGui::ColorButton(v, true);
9170 }
9171
ValueColor(const char * prefix,unsigned int v)9172 void ImGui::ValueColor(const char* prefix, unsigned int v)
9173 {
9174 ImGui::Text("%s: %08X", prefix, v);
9175 ImGui::SameLine();
9176
9177 ImVec4 col;
9178 col.x = (float)((v >> 0) & 0xFF) / 255.0f;
9179 col.y = (float)((v >> 8) & 0xFF) / 255.0f;
9180 col.z = (float)((v >> 16) & 0xFF) / 255.0f;
9181 col.w = (float)((v >> 24) & 0xFF) / 255.0f;
9182 ImGui::ColorButton(col, true);
9183 }
9184
9185 //-----------------------------------------------------------------------------
9186 // PLATFORM DEPENDANT HELPERS
9187 //-----------------------------------------------------------------------------
9188
9189 #if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS))
9190 #undef WIN32_LEAN_AND_MEAN
9191 #define WIN32_LEAN_AND_MEAN
9192 #include <windows.h>
9193 #endif
9194
9195 // Win32 API clipboard implementation
9196 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS)
9197
9198 #ifdef _MSC_VER
9199 #pragma comment(lib, "user32")
9200 #endif
9201
GetClipboardTextFn_DefaultImpl()9202 static const char* GetClipboardTextFn_DefaultImpl()
9203 {
9204 static char* buf_local = NULL;
9205 if (buf_local)
9206 {
9207 ImGui::MemFree(buf_local);
9208 buf_local = NULL;
9209 }
9210 if (!OpenClipboard(NULL))
9211 return NULL;
9212 HANDLE wbuf_handle = GetClipboardData(CF_UNICODETEXT);
9213 if (wbuf_handle == NULL)
9214 return NULL;
9215 if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle))
9216 {
9217 int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
9218 buf_local = (char*)ImGui::MemAlloc(buf_len * sizeof(char));
9219 ImTextStrToUtf8(buf_local, buf_len, wbuf_global, NULL);
9220 }
9221 GlobalUnlock(wbuf_handle);
9222 CloseClipboard();
9223 return buf_local;
9224 }
9225
SetClipboardTextFn_DefaultImpl(const char * text)9226 static void SetClipboardTextFn_DefaultImpl(const char* text)
9227 {
9228 if (!OpenClipboard(NULL))
9229 return;
9230
9231 const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
9232 HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
9233 if (wbuf_handle == NULL)
9234 return;
9235 ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle);
9236 ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
9237 GlobalUnlock(wbuf_handle);
9238 EmptyClipboard();
9239 SetClipboardData(CF_UNICODETEXT, wbuf_handle);
9240 CloseClipboard();
9241 }
9242
9243 #else
9244
9245 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
GetClipboardTextFn_DefaultImpl()9246 static const char* GetClipboardTextFn_DefaultImpl()
9247 {
9248 return GImGui->PrivateClipboard;
9249 }
9250
9251 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
SetClipboardTextFn_DefaultImpl(const char * text)9252 static void SetClipboardTextFn_DefaultImpl(const char* text)
9253 {
9254 ImGuiState& g = *GImGui;
9255 if (g.PrivateClipboard)
9256 {
9257 ImGui::MemFree(g.PrivateClipboard);
9258 g.PrivateClipboard = NULL;
9259 }
9260 const char* text_end = text + strlen(text);
9261 g.PrivateClipboard = (char*)ImGui::MemAlloc((size_t)(text_end - text) + 1);
9262 memcpy(g.PrivateClipboard, text, (size_t)(text_end - text));
9263 g.PrivateClipboard[(int)(text_end - text)] = 0;
9264 }
9265
9266 #endif
9267
9268 // Win32 API IME support (for Asian languages, etc.)
9269 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS)
9270
9271 #include <imm.h>
9272 #ifdef _MSC_VER
9273 #pragma comment(lib, "imm32")
9274 #endif
9275
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)9276 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
9277 {
9278 // Notify OS Input Method Editor of text input position
9279 if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle)
9280 if (HIMC himc = ImmGetContext(hwnd))
9281 {
9282 COMPOSITIONFORM cf;
9283 cf.ptCurrentPos.x = x;
9284 cf.ptCurrentPos.y = y;
9285 cf.dwStyle = CFS_FORCE_POSITION;
9286 ImmSetCompositionWindow(himc, &cf);
9287 }
9288 }
9289
9290 #else
9291
ImeSetInputScreenPosFn_DefaultImpl(int,int)9292 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
9293
9294 #endif
9295
9296 //-----------------------------------------------------------------------------
9297 // HELP
9298 //-----------------------------------------------------------------------------
9299
ShowMetricsWindow(bool * opened)9300 void ImGui::ShowMetricsWindow(bool* opened)
9301 {
9302 if (ImGui::Begin("ImGui Metrics", opened))
9303 {
9304 ImGui::Text("ImGui %s", ImGui::GetVersion());
9305 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
9306 ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3);
9307 ImGui::Text("%d allocations", ImGui::GetIO().MetricsAllocs);
9308 static bool show_clip_rects = true;
9309 ImGui::Checkbox("Show clipping rectangles when hovering a ImDrawCmd", &show_clip_rects);
9310 ImGui::Separator();
9311
9312 struct Funcs
9313 {
9314 static void NodeDrawList(ImDrawList* draw_list, const char* label)
9315 {
9316 bool node_opened = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size);
9317 if (draw_list == ImGui::GetWindowDrawList())
9318 {
9319 ImGui::SameLine();
9320 ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
9321 if (node_opened) ImGui::TreePop();
9322 return;
9323 }
9324 if (!node_opened)
9325 return;
9326
9327 ImDrawList* overlay_draw_list = &GImGui->OverlayDrawList; // Render additional visuals into the top-most draw list
9328 overlay_draw_list->PushClipRectFullScreen();
9329 int elem_offset = 0;
9330 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
9331 {
9332 if (pcmd->UserCallback)
9333 {
9334 ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
9335 continue;
9336 }
9337 bool draw_opened = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %-4d %s vtx, tex = %p, clip_rect = (%.0f,%.0f)..(%.0f,%.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
9338 if (show_clip_rects && ImGui::IsItemHovered())
9339 {
9340 ImRect clip_rect = pcmd->ClipRect;
9341 ImRect vtxs_rect;
9342 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
9343 for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
9344 vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
9345 clip_rect.Round(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, ImColor(255,255,0));
9346 vtxs_rect.Round(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, ImColor(255,0,255));
9347 }
9348 if (!draw_opened)
9349 continue;
9350 for (int i = elem_offset; i+2 < elem_offset + (int)pcmd->ElemCount; i += 3)
9351 {
9352 ImVec2 triangles_pos[3];
9353 char buf[300], *buf_p = buf;
9354 for (int n = 0; n < 3; n++)
9355 {
9356 ImDrawVert& v = draw_list->VtxBuffer[(draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data[i+n] : i+n];
9357 triangles_pos[n] = v.pos;
9358 buf_p += sprintf(buf_p, "vtx %04d { pos = (%8.2f,%8.2f), uv = (%.6f,%.6f), col = %08X }\n", i+n, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
9359 }
9360 ImGui::Selectable(buf, false);
9361 if (ImGui::IsItemHovered())
9362 overlay_draw_list->AddPolyline(triangles_pos, 3, ImColor(255,255,0), true, 1.0f, false); // Add triangle without AA, more readable for large-thin triangle
9363 }
9364 ImGui::TreePop();
9365 }
9366 overlay_draw_list->PopClipRect();
9367 ImGui::TreePop();
9368 }
9369
9370 static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
9371 {
9372 if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
9373 return;
9374 for (int i = 0; i < windows.Size; i++)
9375 Funcs::NodeWindow(windows[i], "Window");
9376 ImGui::TreePop();
9377 }
9378
9379 static void NodeWindow(ImGuiWindow* window, const char* label)
9380 {
9381 if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
9382 return;
9383 NodeDrawList(window->DrawList, "DrawList");
9384 if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
9385 if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
9386 ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
9387 ImGui::TreePop();
9388 }
9389 };
9390
9391 ImGuiState& g = *GImGui; // Access private state
9392 Funcs::NodeWindows(g.Windows, "Windows");
9393 if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.RenderDrawLists[0].Size))
9394 {
9395 for (int i = 0; i < g.RenderDrawLists[0].Size; i++)
9396 Funcs::NodeDrawList(g.RenderDrawLists[0][i], "DrawList");
9397 ImGui::TreePop();
9398 }
9399 if (ImGui::TreeNode("Popups", "Opened Popups Stack (%d)", g.OpenedPopupStack.Size))
9400 {
9401 for (int i = 0; i < g.OpenedPopupStack.Size; i++)
9402 {
9403 ImGuiWindow* window = g.OpenedPopupStack[i].Window;
9404 ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenedPopupStack[i].PopupID, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : "");
9405 }
9406 ImGui::TreePop();
9407 }
9408 if (ImGui::TreeNode("Basic state"))
9409 {
9410 ImGui::Text("FocusedWindow: '%s'", g.FocusedWindow ? g.FocusedWindow->Name : "NULL");
9411 ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
9412 ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
9413 ImGui::Text("HoveredID: 0x%08X/0x%08X", g.HoveredId, g.HoveredIdPreviousFrame); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
9414 ImGui::Text("ActiveID: 0x%08X/0x%08X", g.ActiveId, g.ActiveIdPreviousFrame);
9415 ImGui::TreePop();
9416 }
9417 }
9418 ImGui::End();
9419 }
9420
9421 //-----------------------------------------------------------------------------
9422
9423 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
9424 // Prefer just including imgui_internal.h from your code rather than using this define. If a declaration is missing from imgui_internal.h add it or request it on the github.
9425 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
9426 #include "imgui_user.inl"
9427 #endif
9428
9429 //-----------------------------------------------------------------------------
9430