1 // dear imgui, v1.74 WIP
2 // (main code and documentation)
3
4 // Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code.
5 // Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
6 // Get latest version at https://github.com/ocornut/imgui
7 // Releases change-log at https://github.com/ocornut/imgui/releases
8 // Technical Support for Getting Started https://github.com/ocornut/imgui/wiki
9 // Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/2847
10
11 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
12 // See LICENSE.txt for copyright and licensing details (standard MIT License).
13 // This library is free but I need your support to sustain development and maintenance.
14 // Businesses: you can support continued maintenance and development via support contracts or sponsoring, see docs/README.
15 // Individuals: you can support continued maintenance and development via donations or Patreon https://www.patreon.com/imgui.
16
17 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
18 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
19 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
20 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
21 // to a better solution or official support for them.
22
23 /*
24
25 Index of this file:
26
27 DOCUMENTATION
28
29 - MISSION STATEMENT
30 - END-USER GUIDE
31 - PROGRAMMER GUIDE
32 - Read first.
33 - How to update to a newer version of Dear ImGui.
34 - Getting started with integrating Dear ImGui in your code/engine.
35 - This is how a simple application may look like (2 variations).
36 - This is how a simple rendering function may look like.
37 - Using gamepad/keyboard navigation controls.
38 - API BREAKING CHANGES (read me when you update!)
39 - FREQUENTLY ASKED QUESTIONS (FAQ)
40 - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
41
42 CODE
43 (search for "[SECTION]" in the code to find them)
44
45 // [SECTION] FORWARD DECLARATIONS
46 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
47 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
48 // [SECTION] MISC HELPERS/UTILITIES (Maths, String, Format, Hash, File functions)
49 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
50 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
51 // [SECTION] ImGuiStorage
52 // [SECTION] ImGuiTextFilter
53 // [SECTION] ImGuiTextBuffer
54 // [SECTION] ImGuiListClipper
55 // [SECTION] RENDER HELPERS
56 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
57 // [SECTION] SCROLLING
58 // [SECTION] TOOLTIPS
59 // [SECTION] POPUPS
60 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
61 // [SECTION] DRAG AND DROP
62 // [SECTION] LOGGING/CAPTURING
63 // [SECTION] SETTINGS
64 // [SECTION] PLATFORM DEPENDENT HELPERS
65 // [SECTION] METRICS/DEBUG WINDOW
66
67 */
68
69 //-----------------------------------------------------------------------------
70 // DOCUMENTATION
71 //-----------------------------------------------------------------------------
72
73 /*
74
75 MISSION STATEMENT
76 =================
77
78 - Easy to use to create code-driven and data-driven tools.
79 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
80 - Easy to hack and improve.
81 - Minimize screen real-estate usage.
82 - Minimize setup and maintenance.
83 - Minimize state storage on user side.
84 - Portable, minimize dependencies, run on target (consoles, phones, etc.).
85 - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,.
86 opening a tree node for the first time, etc. but a typical frame should not allocate anything).
87
88 Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
89 - Doesn't look fancy, doesn't animate.
90 - Limited layout features, intricate layouts are typically crafted in code.
91
92
93 END-USER GUIDE
94 ==============
95
96 - Double-click on title bar to collapse window.
97 - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
98 - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
99 - Click and drag on any empty space to move window.
100 - TAB/SHIFT+TAB to cycle through keyboard editable fields.
101 - CTRL+Click on a slider or drag box to input value as text.
102 - Use mouse wheel to scroll.
103 - Text editor:
104 - Hold SHIFT or use mouse to select text.
105 - CTRL+Left/Right to word jump.
106 - CTRL+Shift+Left/Right to select words.
107 - CTRL+A our Double-Click to select all.
108 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
109 - CTRL+Z,CTRL+Y to undo/redo.
110 - ESCAPE to revert text to its original value.
111 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
112 - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
113 - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
114 - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW
115
116
117 PROGRAMMER GUIDE
118 ================
119
120 READ FIRST:
121
122 - Remember to read the FAQ (https://www.dearimgui.org/faq)
123 - Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction
124 or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs.
125 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
126 - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
127 - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
128 You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links docs/README.md.
129 - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
130 For every application frame your UI code will be called only once. This is in contrast to e.g. Unity's own implementation of an IMGUI,
131 where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
132 - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
133 - This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
134 - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
135 If you get an assert, read the messages and comments around the assert.
136 - C++: this is a very C-ish codebase: we don't rely on C++11, we don't include any C++ headers, and ImGui:: is a namespace.
137 - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
138 See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
139 However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
140 - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
141
142 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI:
143
144 - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
145 - Or maintain your own branch where you have imconfig.h modified.
146 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
147 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
148 from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
149 likely be a comment about it. Please report any issue to the GitHub page!
150 - Try to keep your copy of dear imgui reasonably up to date.
151
152 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE:
153
154 - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
155 - Add the Dear ImGui source files to your projects or using your preferred build system.
156 It is recommended you build and statically link the .cpp files as part of your project and not as shared library (DLL).
157 - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating Dear ImGui types with your own maths types.
158 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
159 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
160 Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
161 phases of your own application. All rendering information are stored into command-lists that you will retrieve after calling ImGui::Render().
162 - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
163 - If you are running over a standard OS with a common graphics API, you should be able to use unmodified imgui_impl_*** files from the examples/ folder.
164
165 HOW A SIMPLE APPLICATION MAY LOOK LIKE:
166 EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder).
167
168 // Application init: create a dear imgui context, setup some options, load fonts
169 ImGui::CreateContext();
170 ImGuiIO& io = ImGui::GetIO();
171 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
172 // TODO: Fill optional fields of the io structure later.
173 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
174
175 // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32 and imgui_impl_dx11)
176 ImGui_ImplWin32_Init(hwnd);
177 ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
178
179 // Application main loop
180 while (true)
181 {
182 // Feed inputs to dear imgui, start new frame
183 ImGui_ImplDX11_NewFrame();
184 ImGui_ImplWin32_NewFrame();
185 ImGui::NewFrame();
186
187 // Any application code here
188 ImGui::Text("Hello, world!");
189
190 // Render dear imgui into screen
191 ImGui::Render();
192 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
193 g_pSwapChain->Present(1, 0);
194 }
195
196 // Shutdown
197 ImGui_ImplDX11_Shutdown();
198 ImGui_ImplWin32_Shutdown();
199 ImGui::DestroyContext();
200
201 HOW A SIMPLE APPLICATION MAY LOOK LIKE:
202 EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE.
203
204 // Application init: create a dear imgui context, setup some options, load fonts
205 ImGui::CreateContext();
206 ImGuiIO& io = ImGui::GetIO();
207 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
208 // TODO: Fill optional fields of the io structure later.
209 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
210
211 // Build and load the texture atlas into a texture
212 // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
213 int width, height;
214 unsigned char* pixels = NULL;
215 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
216
217 // At this point you've got the texture data and you need to upload that your your graphic system:
218 // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
219 // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
220 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
221 io.Fonts->TexID = (void*)texture;
222
223 // Application main loop
224 while (true)
225 {
226 // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
227 // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings)
228 io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds)
229 io.DisplaySize.x = 1920.0f; // set the current display width
230 io.DisplaySize.y = 1280.0f; // set the current display height here
231 io.MousePos = my_mouse_pos; // set the mouse position
232 io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states
233 io.MouseDown[1] = my_mouse_buttons[1];
234
235 // Call NewFrame(), after this point you can use ImGui::* functions anytime
236 // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use Dear ImGui everywhere)
237 ImGui::NewFrame();
238
239 // Most of your application code here
240 ImGui::Text("Hello, world!");
241 MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
242 MyGameRender(); // may use any Dear ImGui functions as well!
243
244 // Render dear imgui, swap buffers
245 // (You want to try calling EndFrame/Render as late as you can, to be able to use Dear ImGui in your own game rendering code)
246 ImGui::EndFrame();
247 ImGui::Render();
248 ImDrawData* draw_data = ImGui::GetDrawData();
249 MyImGuiRenderFunction(draw_data);
250 SwapBuffers();
251 }
252
253 // Shutdown
254 ImGui::DestroyContext();
255
256 HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE:
257
258 void void MyImGuiRenderFunction(ImDrawData* draw_data)
259 {
260 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
261 // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
262 // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
263 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
264 for (int n = 0; n < draw_data->CmdListsCount; n++)
265 {
266 const ImDrawList* cmd_list = draw_data->CmdLists[n];
267 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by Dear ImGui
268 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by Dear ImGui
269 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
270 {
271 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
272 if (pcmd->UserCallback)
273 {
274 pcmd->UserCallback(cmd_list, pcmd);
275 }
276 else
277 {
278 // The texture for the draw call is specified by pcmd->TextureId.
279 // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
280 MyEngineBindTexture((MyTexture*)pcmd->TextureId);
281
282 // We are using scissoring to clip some objects. All low-level graphics API should supports it.
283 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
284 // (some elements visible outside their bounds) but you can fix that once everything else works!
285 // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
286 // In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
287 // However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
288 // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
289 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
290 ImVec2 pos = draw_data->DisplayPos;
291 MyEngineScissor((int)(pcmd->ClipRect.x - pos.x), (int)(pcmd->ClipRect.y - pos.y), (int)(pcmd->ClipRect.z - pos.x), (int)(pcmd->ClipRect.w - pos.y));
292
293 // Render 'pcmd->ElemCount/3' indexed triangles.
294 // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits in imconfig.h if your engine doesn't support 16-bits indices.
295 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
296 }
297 idx_buffer += pcmd->ElemCount;
298 }
299 }
300 }
301
302 - The examples/ folders contains many actual implementation of the pseudo-codes above.
303 - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
304 They tell you if Dear ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs from the
305 rest of your application. In every cases you need to pass on the inputs to Dear ImGui.
306 - Refer to the FAQ for more information. Amusingly, it is called a FAQ because people frequently run into the same issues!
307
308 USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
309
310 - The gamepad/keyboard navigation is fairly functional and keeps being improved.
311 - Gamepad support is particularly useful to use dear imgui on a console system (e.g. PS4, Switch, XB1) without a mouse!
312 - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
313 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
314 - Gamepad:
315 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
316 - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
317 Note that io.NavInputs[] is cleared by EndFrame().
318 - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
319 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
320 - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
321 Your code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.).
322 - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW.
323 - If you need to share inputs between your game and the imgui parts, the easiest approach is to go all-or-nothing, with a buttons combo
324 to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
325 - Keyboard:
326 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
327 NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
328 - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
329 will be set. For more advanced uses, you may want to read from:
330 - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
331 - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
332 - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
333 Please reach out if you think the game vs navigation input sharing could be improved.
334 - Mouse:
335 - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
336 - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + uSynergy.c (on your console/tablet/phone app) to share your PC mouse/keyboard.
337 - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavEnableSetMousePos flag.
338 Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
339 When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved.
340 When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that.
341 (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse as moving back and forth!)
342 (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want
343 to set a boolean to ignore your other external mouse positions until the external source is moved again.)
344
345
346 API BREAKING CHANGES
347 ====================
348
349 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
350 Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code.
351 When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
352 You can read releases logs https://github.com/ocornut/imgui/releases for more details.
353
354 - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017): Begin() (5 arguments signature), IsRootWindowOrAnyChildHovered(), AlignFirstTextHeightToWidgets(), SetNextWindowPosCenter(), ImFont::Glyph. Grep this log for details and new names, or see how they were implemented until 1.73.
355 - 2019/10/14 (1.74) - inputs: Fixed a miscalculation in the keyboard/mouse "typematic" repeat delay/rate calculation, used by keys and e.g. repeating mouse buttons as well as the GetKeyPressedAmount() function.
356 if you were using a non-default value for io.KeyRepeatRate (previous default was 0.250), you can add +io.KeyRepeatDelay to it to compensate for the fix.
357 The function was triggering on: 0.0 and (delay+rate*N) where (N>=1). Fixed formula responds to (N>=0). Effectively it made io.KeyRepeatRate behave like it was set to (io.KeyRepeatRate + io.KeyRepeatDelay).
358 If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
359 - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
360 - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
361 - 2019/06/14 (1.72) - removed redirecting functions/enums names that were marked obsolete in 1.51 (June 2017): ImGuiCol_Column*, ImGuiSetCond_*, IsItemHoveredRect(), IsPosHoveringAnyWindow(), IsMouseHoveringAnyWindow(), IsMouseHoveringWindow(), IMGUI_ONCE_UPON_A_FRAME. Grep this log for details and new names, or see how they were implemented until 1.71.
362 - 2019/06/07 (1.71) - rendering of child window outer decorations (bg color, border, scrollbars) is now performed as part of the parent window. If you have
363 overlapping child windows in a same parent, and relied on their relative z-order to be mapped to their submission order, this will affect your rendering.
364 This optimization is disabled if the parent window has no visual output, because it appears to be the most common situation leading to the creation of overlapping child windows.
365 Please reach out if you are affected.
366 - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
367 - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
368 - 2019/04/29 (1.70) - improved ImDrawList thick strokes (>1.0f) preserving correct thickness up to 90 degrees angles (e.g. rectangles). If you have custom rendering using thick lines, they will appear thicker now.
369 - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
370 - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
371 - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
372 - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with a dummy small value!
373 - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
374 - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
375 - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
376 - 2018/12/20 (1.67) - made it illegal to call Begin("") with an empty string. This somehow half-worked before but had various undesirable side-effects.
377 - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
378 - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
379 - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
380 - 2018/09/06 (1.65) - renamed stb_truetype.h to imstb_truetype.h, stb_textedit.h to imstb_textedit.h, and stb_rect_pack.h to imstb_rectpack.h.
381 If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
382 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
383 - 2018/08/31 (1.64) - added imgui_widgets.cpp file, extracted and moved widgets code out of imgui.cpp into imgui_widgets.cpp. Re-ordered some of the code remaining in imgui.cpp.
384 NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
385 Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
386 - 2018/08/22 (1.63) - renamed IsItemDeactivatedAfterChange() to IsItemDeactivatedAfterEdit() for consistency with new IsItemEdited() API. Kept redirection function (will obsolete soonish as IsItemDeactivatedAfterChange() is very recent).
387 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
388 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
389 - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges [update 1.67 renamed to ConfigWindowsResizeFromEdges] to enable the feature.
390 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
391 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
392 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
393 - 2018/06/08 (1.62) - examples: the imgui_impl_xxx files have been split to separate platform (Win32, Glfw, SDL2, etc.) from renderer (DX11, OpenGL, Vulkan, etc.).
394 old bindings will still work as is, however prefer using the separated bindings as they will be updated to support multi-viewports.
395 when adopting new bindings follow the main.cpp code of your preferred examples/ folder to know which functions to call.
396 in particular, note that old bindings called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
397 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
398 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
399 - 2018/05/03 (1.61) - DragInt(): the default compile-time format string has been changed from "%.0f" to "%d", as we are not using integers internally any more.
400 If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
401 To honor backward-compatibility, the DragInt() code will currently parse and modify format strings to replace %*f with %d, giving time to users to upgrade their code.
402 If you have IMGUI_DISABLE_OBSOLETE_FUNCTIONS enabled, the code will instead assert! You may run a reg-exp search on your codebase for e.g. "DragInt.*%f" to help you find them.
403 - 2018/04/28 (1.61) - obsoleted InputFloat() functions taking an optional "int decimal_precision" in favor of an equivalent and more flexible "const char* format",
404 consistent with other functions. Kept redirection functions (will obsolete).
405 - 2018/04/09 (1.61) - IM_DELETE() helper function added in 1.60 doesn't clear the input _pointer_ reference, more consistent with expectation and allows passing r-value.
406 - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some binding ahead of merging the Nav branch).
407 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
408 - 2018/03/08 (1.60) - changed ImFont::DisplayOffset.y to default to 0 instead of +1. Fixed rounding of Ascent/Descent to match TrueType renderer. If you were adding or subtracting to ImFont::DisplayOffset check if your fonts are correctly aligned vertically.
409 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
410 - 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is not really usable in many situations at the moment.
411 - 2018/02/16 (1.60) - obsoleted the io.RenderDrawListsFn callback, you can call your graphics engine render function after ImGui::Render(). Use ImGui::GetDrawData() to retrieve the ImDrawData* to display.
412 - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
413 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
414 - removed Shutdown() function, as DestroyContext() serve this purpose.
415 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
416 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
417 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
418 - 2018/01/31 (1.60) - moved sample TTF files from extra_fonts/ to misc/fonts/. If you loaded files directly from the imgui repo you may need to update your paths.
419 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
420 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
421 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
422 - 2017/12/29 (1.60) - removed CalcItemRectClosestPoint() which was weird and not really used by anyone except demo code. If you need it it's easy to replicate on your side.
423 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
424 - 2017/12/21 (1.53) - ImDrawList: renamed style.AntiAliasedShapes to style.AntiAliasedFill for consistency and as a way to explicitly break code that manipulate those flag at runtime. You can now manipulate ImDrawList::Flags
425 - 2017/12/21 (1.53) - ImDrawList: removed 'bool anti_aliased = true' final parameter of ImDrawList::AddPolyline() and ImDrawList::AddConvexPolyFilled(). Prefer manipulating ImDrawList::Flags if you need to toggle them during the frame.
426 - 2017/12/14 (1.53) - using the ImGuiWindowFlags_NoScrollWithMouse flag on a child window forwards the mouse wheel event to the parent window, unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set.
427 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
428 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
429 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
430 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
431 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
432 - 2017/11/27 (1.53) - renamed ImGuiTextBuffer::append() helper to appendf(), appendv() to appendfv(). If you copied the 'Log' demo in your code, it uses appendv() so that needs to be renamed.
433 - 2017/11/18 (1.53) - Style, Begin: removed ImGuiWindowFlags_ShowBorders window flag. Borders are now fully set up in the ImGuiStyle structure (see e.g. style.FrameBorderSize, style.WindowBorderSize). Use ImGui::ShowStyleEditor() to look them up.
434 Please note that the style system will keep evolving (hopefully stabilizing in Q1 2018), and so custom styles will probably subtly break over time. It is recommended you use the StyleColorsClassic(), StyleColorsDark(), StyleColorsLight() functions.
435 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
436 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
437 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
438 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
439 - 2017/10/24 (1.52) - renamed IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS to IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS for consistency.
440 - 2017/10/20 (1.52) - changed IsWindowHovered() default parameters behavior to return false if an item is active in another window (e.g. click-dragging item from another window to this window). You can use the newly introduced IsWindowHovered() flags to requests this specific behavior if you need it.
441 - 2017/10/20 (1.52) - marked IsItemHoveredRect()/IsMouseHoveringWindow() as obsolete, in favor of using the newly introduced flags for IsItemHovered() and IsWindowHovered(). See https://github.com/ocornut/imgui/issues/1382 for details.
442 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
443 IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
444 IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
445 IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
446 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
447 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
448 - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
449 - 2017/09/25 (1.52) - removed SetNextWindowPosCenter() because SetNextWindowPos() now has the optional pivot information to do the same and more. Kept redirection function (will obsolete).
450 - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your binding if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
451 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
452 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
453 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
454 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
455 - 2017/08/20 (1.51) - added PushStyleColor(ImGuiCol idx, ImU32 col) overload, which _might_ cause an "ambiguous call" compilation error if you are using ImColor() with implicit cast. Cast to ImU32 or ImVec4 explicily to fix.
456 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
457 - 2017/08/15 (1.51) - changed parameter order for BeginPopupContextWindow() from (const char*,int buttons,bool also_over_items) to (const char*,int buttons,bool also_over_items). Note that most calls relied on default parameters completely.
458 - 2017/08/13 (1.51) - renamed ImGuiCol_Column to ImGuiCol_Separator, ImGuiCol_ColumnHovered to ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive to ImGuiCol_SeparatorActive. Kept redirection enums (will obsolete).
459 - 2017/08/11 (1.51) - renamed ImGuiSetCond_Always to ImGuiCond_Always, ImGuiSetCond_Once to ImGuiCond_Once, ImGuiSetCond_FirstUseEver to ImGuiCond_FirstUseEver, ImGuiSetCond_Appearing to ImGuiCond_Appearing. Kept redirection enums (will obsolete).
460 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
461 - 2017/08/08 (1.51) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to the various Color*() functions. The SetColorEditOptions() allows to initialize default but the user can still change them with right-click context menu.
462 - changed prototype of 'ColorEdit4(const char* label, float col[4], bool show_alpha = true)' to 'ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)', where passing flags = 0x01 is a safe no-op (hello dodgy backward compatibility!). - check and run the demo window, under "Color/Picker Widgets", to understand the various new options.
463 - changed prototype of rarely used 'ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)' to 'ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0,0))'
464 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
465 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
466 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
467 - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetId() and use it instead of passing string to BeginChild().
468 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
469 - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc.
470 - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully breakage should be minimal.
471 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
472 If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you.
473 If your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
474 This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color.
475 ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
476 {
477 float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
478 return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a);
479 }
480 If this is confusing, pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color.
481 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
482 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
483 - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen).
484 - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDrawList::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer.
485 - 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).
486 - 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)
487 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
488 - 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.
489 - 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.
490 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
491 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
492 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
493 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.
494 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!
495 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
496 - 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.
497 - 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
498 - 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.
499 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
500 - 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.
501 this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
502 - 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.
503 - the signature of the io.RenderDrawListsFn handler has changed!
504 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
505 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
506 parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
507 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
508 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
509 - 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.
510 - 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!
511 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
512 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
513 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
514 - 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.
515 - 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
516 - 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!
517 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
518 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
519 - 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.
520 - 2015/05/11 (1.40) - changed BeginPopup() API, takes a string identifier instead of a bool. ImGui needs to manage the open/closed state of popups. Call OpenPopup() to actually set the "open" state of a popup. BeginPopup() returns true if the popup is opened.
521 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
522 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
523 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
524 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
525 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
526 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
527 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
528 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
529 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
530 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
531 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
532 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
533 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
534 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
535 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
536 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
537 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
538 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
539 font init: { const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..>; }
540 became: { unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier; }
541 you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
542 it is now recommended that you sample the font texture with bilinear interpolation.
543 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
544 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
545 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
546 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
547 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
548 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
549 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
550 - 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)
551 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
552 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
553 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
554 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
555 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
556 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
557
558
559 FREQUENTLY ASKED QUESTIONS (FAQ)
560 ================================
561
562 Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
563 Some answers are copied down here to facilitate searching in code.
564
565 Q&A: Basics
566 ===========
567
568 Q: Where is the documentation?
569 A: This library is poorly documented at the moment and expects of the user to be acquainted with C/C++.
570 - Run the examples/ and explore them.
571 - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
572 - The demo covers most features of Dear ImGui, so you can read the code and see its output.
573 - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
574 - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the examples/
575 folder to explain how to integrate Dear ImGui with your own engine/application.
576 - Your programming IDE is your friend, find the type or function declaration to find comments
577 associated to it.
578
579 Q: Which version should I get?
580 Q: Why the names "Dear ImGui" vs "ImGui"?
581 >> See https://www.dearimgui.org/faq
582
583 Q&A: Concerns
584 =============
585
586 Q: Who uses Dear ImGui?
587 Q: Can you create elaborate/serious tools with Dear ImGui?
588 Q: Can you reskin the look of Dear ImGui?
589 Q: Why using C++ (as opposed to C)?
590 >> See https://www.dearimgui.org/faq
591
592 Q&A: Integration
593 ================
594
595 Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or to my application?
596 A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure (e.g. if (ImGui::GetIO().WantCaptureMouse) { ... } )
597 - When 'io.WantCaptureMouse' is set, imgui wants to use your mouse state, and you may want to discard/hide the inputs from the rest of your application.
598 - When 'io.WantCaptureKeyboard' is set, imgui wants to use your keyboard state, and you may want to discard/hide the inputs from the rest of your application.
599 - When 'io.WantTextInput' is set to may want to notify your OS to popup an on-screen keyboard, if available (e.g. on a mobile phone, or console OS).
600 Note: you should always pass your mouse/keyboard inputs to imgui, even when the io.WantCaptureXXX flag are set false.
601 This is because imgui needs to detect that you clicked in the void to unfocus its own windows.
602 Note: The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!).
603 It handle mouse dragging correctly (both dragging that started over your application or over an imgui window) and handle e.g. modal windows blocking inputs.
604 Those flags are updated by ImGui::NewFrame(). Preferably read the flags after calling NewFrame() if you can afford it, but reading them before is also
605 perfectly fine, as the bool toggle fairly rarely. If you have on a touch device, you might find use for an early call to UpdateHoveredWindowAndCaptureFlags().
606 Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically
607 have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
608 were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
609
610 Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
611 Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
612 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
613 >> See https://www.dearimgui.org/faq
614
615 Q&A: Usage
616 ----------
617
618 Q: Why are multiple widgets reacting when I interact with a single one?
619 Q: How can I have multiple widgets with the same label or with an empty label?
620 A: A primer on labels and the ID Stack...
621
622 Dear ImGui internally need to uniquely identify UI elements.
623 Elements that are typically not clickable (such as calls to the Text functions) don't need an ID.
624 Interactive widgets (such as calls to Button buttons) need a unique ID.
625 Unique ID are used internally to track active widgets and occasionally associate state to widgets.
626 Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element.
627
628 - Unique ID are often derived from a string label:
629
630 Button("OK"); // Label = "OK", ID = hash of (..., "OK")
631 Button("Cancel"); // Label = "Cancel", ID = hash of (..., "Cancel")
632
633 - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having
634 two buttons labeled "OK" in different windows or different tree locations is fine.
635 We used "..." above to signify whatever was already pushed to the ID stack previously:
636
637 Begin("MyWindow");
638 Button("OK"); // Label = "OK", ID = hash of ("MyWindow", "OK")
639 End();
640 Begin("MyOtherWindow");
641 Button("OK"); // Label = "OK", ID = hash of ("MyOtherWindow", "OK")
642 End();
643
644 - If you have a same ID twice in the same location, you'll have a conflict:
645
646 Button("OK");
647 Button("OK"); // ID collision! Interacting with either button will trigger the first one.
648
649 Fear not! this is easy to solve and there are many ways to solve it!
650
651 - Solving ID conflict in a simple/local context:
652 When passing a label you can optionally specify extra ID information within string itself.
653 Use "##" to pass a complement to the ID that won't be visible to the end-user.
654 This helps solving the simple collision cases when you know e.g. at compilation time which items
655 are going to be created:
656
657 Begin("MyWindow");
658 Button("Play"); // Label = "Play", ID = hash of ("MyWindow", "Play")
659 Button("Play##foo1"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo1") // Different from above
660 Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo2") // Different from above
661 End();
662
663 - If you want to completely hide the label, but still need an ID:
664
665 Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label, just a checkbox!
666
667 - Occasionally/rarely you might want change a label while preserving a constant ID. This allows
668 you to animate labels. For example you may want to include varying information in a window title bar,
669 but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID:
670
671 Button("Hello###ID"); // Label = "Hello", ID = hash of (..., "###ID")
672 Button("World###ID"); // Label = "World", ID = hash of (..., "###ID") // Same as above, even though the label looks different
673
674 sprintf(buf, "My game (%f FPS)###MyGame", fps);
675 Begin(buf); // Variable title, ID = hash of "MyGame"
676
677 - Solving ID conflict in a more general manner:
678 Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts
679 within the same window. This is the most convenient way of distinguishing ID when iterating and
680 creating many UI elements programmatically.
681 You can push a pointer, a string or an integer value into the ID stack.
682 Remember that ID are formed from the concatenation of _everything_ pushed into the ID stack.
683 At each level of the stack we store the seed used for items at this level of the ID stack.
684
685 Begin("Window");
686 for (int i = 0; i < 100; i++)
687 {
688 PushID(i); // Push i to the id tack
689 Button("Click"); // Label = "Click", ID = hash of ("Window", i, "Click")
690 PopID();
691 }
692 for (int i = 0; i < 100; i++)
693 {
694 MyObject* obj = Objects[i];
695 PushID(obj);
696 Button("Click"); // Label = "Click", ID = hash of ("Window", obj pointer, "Click")
697 PopID();
698 }
699 for (int i = 0; i < 100; i++)
700 {
701 MyObject* obj = Objects[i];
702 PushID(obj->Name);
703 Button("Click"); // Label = "Click", ID = hash of ("Window", obj->Name, "Click")
704 PopID();
705 }
706 End();
707
708 - You can stack multiple prefixes into the ID stack:
709
710 Button("Click"); // Label = "Click", ID = hash of (..., "Click")
711 PushID("node");
712 Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
713 PushID(my_ptr);
714 Button("Click"); // Label = "Click", ID = hash of (..., "node", my_ptr, "Click")
715 PopID();
716 PopID();
717
718 - Tree nodes implicitly creates a scope for you by calling PushID().
719
720 Button("Click"); // Label = "Click", ID = hash of (..., "Click")
721 if (TreeNode("node")) // <-- this function call will do a PushID() for you (unless instructed not to, with a special flag)
722 {
723 Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
724 TreePop();
725 }
726
727 - When working with trees, ID are used to preserve the open/close state of each tree node.
728 Depending on your use cases you may want to use strings, indices or pointers as ID.
729 e.g. when following a single pointer that may change over time, using a static string as ID
730 will preserve your node open/closed state when the targeted object change.
731 e.g. when displaying a list of objects, using indices or pointers as ID will preserve the
732 node open/closed state differently. See what makes more sense in your situation!
733
734 Q: How can I display an image? What is ImTextureID, how does it works?
735 >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
736
737 Q: How can I use my own math types instead of ImVec2/ImVec4?
738 Q: How can I interact with standard C++ types (such as std::string and std::vector)?
739 Q: How can I display custom shapes? (using low-level ImDrawList API)
740 >> See https://www.dearimgui.org/faq
741
742 Q&A: Fonts, Text
743 ================
744
745 Q: How can I load a different font than the default?
746 Q: How can I easily use icons in my application?
747 Q: How can I load multiple fonts?
748 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
749 >> See https://www.dearimgui.org/faq and misc/fonts/README.txt
750
751 Q&A: Community
752 ==============
753
754 Q: How can I help?
755 A: - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt
756 and see how you want to help and can help!
757 - Businesses: convince your company to fund development via support contracts/sponsoring! This is among the most useful thing you can do for dear imgui.
758 - Individuals: you can also become a Patron (http://www.patreon.com/imgui) or donate on PayPal! See README.
759 - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
760 You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/2847). Visuals are ideal as they inspire other programmers.
761 But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
762 - If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues (on github or privately).
763
764 */
765
766 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
767 #define _CRT_SECURE_NO_WARNINGS
768 #endif
769
770 #include "imgui.h"
771 #ifndef IMGUI_DEFINE_MATH_OPERATORS
772 #define IMGUI_DEFINE_MATH_OPERATORS
773 #endif
774 #include "imgui_internal.h"
775
776 #include <ctype.h> // toupper
777 #include <stdio.h> // vsnprintf, sscanf, printf
778 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
779 #include <stddef.h> // intptr_t
780 #else
781 #include <stdint.h> // intptr_t
782 #endif
783
784 // Debug options
785 #define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
786 #define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window
787 #define IMGUI_DEBUG_INI_SETTINGS 0 // Save additional comments in .ini file
788
789 // Visual Studio warnings
790 #ifdef _MSC_VER
791 #pragma warning (disable: 4127) // condition expression is constant
792 #pragma warning (disable: 4201) // nonstandard extension used: nameless struct/union
793 #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
794 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
795 #endif
796
797 // Clang/GCC warnings with -Weverything
798 #if defined(__clang__)
799 #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning : unknown warning group '-Wformat-pedantic *' // not all warnings are known by all clang versions.. so ignoring warnings triggers new warnings on some configuration. great!
800 #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
801 #pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
802 #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.
803 #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.
804 #pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference is.
805 #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
806 #pragma clang diagnostic ignored "-Wformat-pedantic" // warning : format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic.
807 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int'
808 #if __has_warning("-Wzero-as-null-pointer-constant")
809 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0
810 #endif
811 #if __has_warning("-Wdouble-promotion")
812 #pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
813 #endif
814 #elif defined(__GNUC__)
815 // We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association.
816 #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
817 #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
818 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
819 #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
820 #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
821 #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
822 #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
823 #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
824 #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
825 #endif
826
827 // When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch.
828 static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in
829 static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
830
831 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end)
832 static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow().
833 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
834 static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 2.00f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certaint time, unless mouse moved.
835
836 //-------------------------------------------------------------------------
837 // [SECTION] FORWARD DECLARATIONS
838 //-------------------------------------------------------------------------
839
840 static void SetCurrentWindow(ImGuiWindow* window);
841 static void FindHoveredWindow();
842 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
843 static void CheckStacksSize(ImGuiWindow* window, bool write);
844 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
845
846 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
847 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
848
849 static ImRect GetViewportRect();
850
851 // Settings
852 static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
853 static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
854 static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
855
856 // Platform Dependents default implementation for IO functions
857 static const char* GetClipboardTextFn_DefaultImpl(void* user_data);
858 static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
859 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
860
861 namespace ImGui
862 {
863 static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags);
864
865 // Navigation
866 static void NavUpdate();
867 static void NavUpdateWindowing();
868 static void NavUpdateWindowingOverlay();
869 static void NavUpdateMoveResult();
870 static float NavUpdatePageUpPageDown();
871 static inline void NavUpdateAnyRequestFlag();
872 static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand);
873 static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
874 static ImVec2 NavCalcPreferredRefPos();
875 static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
876 static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
877 static int FindWindowFocusIndex(ImGuiWindow* window);
878
879 // Misc
880 static void UpdateMouseInputs();
881 static void UpdateMouseWheel();
882 static bool UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
883 static void UpdateDebugToolItemPicker();
884 static void RenderWindowOuterBorders(ImGuiWindow* window);
885 static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);
886 static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
887
888 }
889
890 //-----------------------------------------------------------------------------
891 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
892 //-----------------------------------------------------------------------------
893
894 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
895 // ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
896 // 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call
897 // SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading.
898 // In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into.
899 // 2) Important: Dear ImGui functions are not thread-safe because of this pointer.
900 // If you want thread-safety to allow N threads to access N different contexts, you can:
901 // - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h:
902 // struct ImGuiContext;
903 // extern thread_local ImGuiContext* MyImGuiTLS;
904 // #define GImGui MyImGuiTLS
905 // And then define MyImGuiTLS in one of your cpp file. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword.
906 // - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
907 // - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace.
908 #ifndef GImGui
909 ImGuiContext* GImGui = NULL;
910 #endif
911
912 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
913 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
914 // Otherwise, you probably don't want to modify them mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction.
915 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)916 static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); }
FreeWrapper(void * ptr,void * user_data)917 static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); }
918 #else
MallocWrapper(size_t size,void * user_data)919 static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; }
FreeWrapper(void * ptr,void * user_data)920 static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
921 #endif
922
923 static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
924 static void (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
925 static void* GImAllocatorUserData = NULL;
926
927 //-----------------------------------------------------------------------------
928 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
929 //-----------------------------------------------------------------------------
930
ImGuiStyle()931 ImGuiStyle::ImGuiStyle()
932 {
933 Alpha = 1.0f; // Global alpha applies to everything in ImGui
934 WindowPadding = ImVec2(8,8); // Padding within a window
935 WindowRounding = 7.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows
936 WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
937 WindowMinSize = ImVec2(32,32); // Minimum window size
938 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
939 WindowMenuButtonPosition= ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
940 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
941 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
942 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
943 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
944 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
945 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
946 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
947 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
948 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
949 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!
950 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
951 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
952 ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
953 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
954 GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
955 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
956 TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
957 TabBorderSize = 0.0f; // Thickness of border around tabs.
958 ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
959 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
960 SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text when button is larger than text.
961 DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows.
962 DisplaySafeAreaPadding = ImVec2(3,3); // 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.
963 MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
964 AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
965 AntiAliasedFill = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
966 CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
967
968 // Default theme
969 ImGui::StyleColorsDark(this);
970 }
971
972 // To scale your entire UI (e.g. if you want your app to use High DPI or generally be DPI aware) you may use this helper function. Scaling the fonts is done separately and is up to you.
973 // Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times.
ScaleAllSizes(float scale_factor)974 void ImGuiStyle::ScaleAllSizes(float scale_factor)
975 {
976 WindowPadding = ImFloor(WindowPadding * scale_factor);
977 WindowRounding = ImFloor(WindowRounding * scale_factor);
978 WindowMinSize = ImFloor(WindowMinSize * scale_factor);
979 ChildRounding = ImFloor(ChildRounding * scale_factor);
980 PopupRounding = ImFloor(PopupRounding * scale_factor);
981 FramePadding = ImFloor(FramePadding * scale_factor);
982 FrameRounding = ImFloor(FrameRounding * scale_factor);
983 ItemSpacing = ImFloor(ItemSpacing * scale_factor);
984 ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
985 TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
986 IndentSpacing = ImFloor(IndentSpacing * scale_factor);
987 ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
988 ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
989 ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
990 GrabMinSize = ImFloor(GrabMinSize * scale_factor);
991 GrabRounding = ImFloor(GrabRounding * scale_factor);
992 TabRounding = ImFloor(TabRounding * scale_factor);
993 DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
994 DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
995 MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
996 }
997
ImGuiIO()998 ImGuiIO::ImGuiIO()
999 {
1000 // Most fields are initialized with zero
1001 memset(this, 0, sizeof(*this));
1002
1003 // Settings
1004 ConfigFlags = ImGuiConfigFlags_None;
1005 BackendFlags = ImGuiBackendFlags_None;
1006 DisplaySize = ImVec2(-1.0f, -1.0f);
1007 DeltaTime = 1.0f/60.0f;
1008 IniSavingRate = 5.0f;
1009 IniFilename = "imgui.ini";
1010 LogFilename = "imgui_log.txt";
1011 MouseDoubleClickTime = 0.30f;
1012 MouseDoubleClickMaxDist = 6.0f;
1013 for (int i = 0; i < ImGuiKey_COUNT; i++)
1014 KeyMap[i] = -1;
1015 KeyRepeatDelay = 0.275f;
1016 KeyRepeatRate = 0.050f;
1017 UserData = NULL;
1018
1019 Fonts = NULL;
1020 FontGlobalScale = 1.0f;
1021 FontDefault = NULL;
1022 FontAllowUserScaling = false;
1023 DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1024
1025 // Miscellaneous options
1026 MouseDrawCursor = false;
1027 #ifdef __APPLE__
1028 ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
1029 #else
1030 ConfigMacOSXBehaviors = false;
1031 #endif
1032 ConfigInputTextCursorBlink = true;
1033 ConfigWindowsResizeFromEdges = true;
1034 ConfigWindowsMoveFromTitleBarOnly = false;
1035 ConfigWindowsMemoryCompactTimer = 60.0f;
1036
1037 // Platform Functions
1038 BackendPlatformName = BackendRendererName = NULL;
1039 BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1040 GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
1041 SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1042 ClipboardUserData = NULL;
1043 ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1044 ImeWindowHandle = NULL;
1045
1046 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1047 RenderDrawListsFn = NULL;
1048 #endif
1049
1050 // Input (NB: we already have memset zero the entire structure!)
1051 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1052 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1053 MouseDragThreshold = 6.0f;
1054 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1055 for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
1056 for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1057 }
1058
1059 // Pass in translated ASCII characters for text input.
1060 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1061 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(unsigned int c)1062 void ImGuiIO::AddInputCharacter(unsigned int c)
1063 {
1064 if (c > 0 && c < 0x10000)
1065 InputQueueCharacters.push_back((ImWchar)c);
1066 }
1067
AddInputCharactersUTF8(const char * utf8_chars)1068 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1069 {
1070 while (*utf8_chars != 0)
1071 {
1072 unsigned int c = 0;
1073 utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1074 if (c > 0 && c < 0x10000)
1075 InputQueueCharacters.push_back((ImWchar)c);
1076 }
1077 }
1078
ClearInputCharacters()1079 void ImGuiIO::ClearInputCharacters()
1080 {
1081 InputQueueCharacters.resize(0);
1082 }
1083
1084 //-----------------------------------------------------------------------------
1085 // [SECTION] MISC HELPERS/UTILITIES (Maths, String, Format, Hash, File functions)
1086 //-----------------------------------------------------------------------------
1087
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1088 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1089 {
1090 ImVec2 ap = p - a;
1091 ImVec2 ab_dir = b - a;
1092 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1093 if (dot < 0.0f)
1094 return a;
1095 float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1096 if (dot > ab_len_sqr)
1097 return b;
1098 return a + ab_dir * dot / ab_len_sqr;
1099 }
1100
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1101 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1102 {
1103 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1104 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1105 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1106 return ((b1 == b2) && (b2 == b3));
1107 }
1108
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1109 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1110 {
1111 ImVec2 v0 = b - a;
1112 ImVec2 v1 = c - a;
1113 ImVec2 v2 = p - a;
1114 const float denom = v0.x * v1.y - v1.x * v0.y;
1115 out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1116 out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1117 out_u = 1.0f - out_v - out_w;
1118 }
1119
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1120 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1121 {
1122 ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1123 ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1124 ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1125 float dist2_ab = ImLengthSqr(p - proj_ab);
1126 float dist2_bc = ImLengthSqr(p - proj_bc);
1127 float dist2_ca = ImLengthSqr(p - proj_ca);
1128 float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1129 if (m == dist2_ab)
1130 return proj_ab;
1131 if (m == dist2_bc)
1132 return proj_bc;
1133 return proj_ca;
1134 }
1135
1136 // Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.
ImStricmp(const char * str1,const char * str2)1137 int ImStricmp(const char* str1, const char* str2)
1138 {
1139 int d;
1140 while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1141 return d;
1142 }
1143
ImStrnicmp(const char * str1,const char * str2,size_t count)1144 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1145 {
1146 int d = 0;
1147 while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1148 return d;
1149 }
1150
ImStrncpy(char * dst,const char * src,size_t count)1151 void ImStrncpy(char* dst, const char* src, size_t count)
1152 {
1153 if (count < 1)
1154 return;
1155 if (count > 1)
1156 strncpy(dst, src, count - 1);
1157 dst[count - 1] = 0;
1158 }
1159
ImStrdup(const char * str)1160 char* ImStrdup(const char* str)
1161 {
1162 size_t len = strlen(str);
1163 void* buf = IM_ALLOC(len + 1);
1164 return (char*)memcpy(buf, (const void*)str, len + 1);
1165 }
1166
ImStrdupcpy(char * dst,size_t * p_dst_size,const char * src)1167 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1168 {
1169 size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1170 size_t src_size = strlen(src) + 1;
1171 if (dst_buf_size < src_size)
1172 {
1173 IM_FREE(dst);
1174 dst = (char*)IM_ALLOC(src_size);
1175 if (p_dst_size)
1176 *p_dst_size = src_size;
1177 }
1178 return (char*)memcpy(dst, (const void*)src, src_size);
1179 }
1180
ImStrchrRange(const char * str,const char * str_end,char c)1181 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1182 {
1183 const char* p = (const char*)memchr(str, (int)c, str_end - str);
1184 return p;
1185 }
1186
ImStrlenW(const ImWchar * str)1187 int ImStrlenW(const ImWchar* str)
1188 {
1189 //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bits
1190 int n = 0;
1191 while (*str++) n++;
1192 return n;
1193 }
1194
1195 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1196 const char* ImStreolRange(const char* str, const char* str_end)
1197 {
1198 const char* p = (const char*)memchr(str, '\n', str_end - str);
1199 return p ? p : str_end;
1200 }
1201
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1202 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1203 {
1204 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1205 buf_mid_line--;
1206 return buf_mid_line;
1207 }
1208
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1209 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1210 {
1211 if (!needle_end)
1212 needle_end = needle + strlen(needle);
1213
1214 const char un0 = (char)toupper(*needle);
1215 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1216 {
1217 if (toupper(*haystack) == un0)
1218 {
1219 const char* b = needle + 1;
1220 for (const char* a = haystack + 1; b < needle_end; a++, b++)
1221 if (toupper(*a) != toupper(*b))
1222 break;
1223 if (b == needle_end)
1224 return haystack;
1225 }
1226 haystack++;
1227 }
1228 return NULL;
1229 }
1230
1231 // Trim str by offsetting contents when there's leading data + writing a \0 at the trailing position. We use this in situation where the cost is negligible.
ImStrTrimBlanks(char * buf)1232 void ImStrTrimBlanks(char* buf)
1233 {
1234 char* p = buf;
1235 while (p[0] == ' ' || p[0] == '\t') // Leading blanks
1236 p++;
1237 char* p_start = p;
1238 while (*p != 0) // Find end of string
1239 p++;
1240 while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks
1241 p--;
1242 if (p_start != buf) // Copy memory if we had leading blanks
1243 memmove(buf, p_start, p - p_start);
1244 buf[p - p_start] = 0; // Zero terminate
1245 }
1246
1247 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1248 // Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm.
1249 // B) When buf==NULL vsnprintf() will return the output size.
1250 #ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1251
1252 //#define IMGUI_USE_STB_SPRINTF
1253 #ifdef IMGUI_USE_STB_SPRINTF
1254 #define STB_SPRINTF_IMPLEMENTATION
1255 #include "imstb_sprintf.h"
1256 #endif
1257
1258 #if defined(_MSC_VER) && !defined(vsnprintf)
1259 #define vsnprintf _vsnprintf
1260 #endif
1261
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1262 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1263 {
1264 va_list args;
1265 va_start(args, fmt);
1266 #ifdef IMGUI_USE_STB_SPRINTF
1267 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1268 #else
1269 int w = vsnprintf(buf, buf_size, fmt, args);
1270 #endif
1271 va_end(args);
1272 if (buf == NULL)
1273 return w;
1274 if (w == -1 || w >= (int)buf_size)
1275 w = (int)buf_size - 1;
1276 buf[w] = 0;
1277 return w;
1278 }
1279
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1280 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1281 {
1282 #ifdef IMGUI_USE_STB_SPRINTF
1283 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1284 #else
1285 int w = vsnprintf(buf, buf_size, fmt, args);
1286 #endif
1287 if (buf == NULL)
1288 return w;
1289 if (w == -1 || w >= (int)buf_size)
1290 w = (int)buf_size - 1;
1291 buf[w] = 0;
1292 return w;
1293 }
1294 #endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1295
1296 // CRC32 needs a 1KB lookup table (not cache friendly)
1297 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1298 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1299 static const ImU32 GCrc32LookupTable[256] =
1300 {
1301 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1302 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1303 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1304 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1305 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1306 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1307 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1308 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1309 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1310 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1311 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1312 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1313 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1314 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1315 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1316 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1317 };
1318
1319 // Known size hash
1320 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1321 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImHashData(const void * data_p,size_t data_size,ImU32 seed)1322 ImU32 ImHashData(const void* data_p, size_t data_size, ImU32 seed)
1323 {
1324 ImU32 crc = ~seed;
1325 const unsigned char* data = (const unsigned char*)data_p;
1326 const ImU32* crc32_lut = GCrc32LookupTable;
1327 while (data_size-- != 0)
1328 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1329 return ~crc;
1330 }
1331
1332 // Zero-terminated string hash, with support for ### to reset back to seed value
1333 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1334 // Because this syntax is rarely used we are optimizing for the common case.
1335 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1336 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1337 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImHashStr(const char * data_p,size_t data_size,ImU32 seed)1338 ImU32 ImHashStr(const char* data_p, size_t data_size, ImU32 seed)
1339 {
1340 seed = ~seed;
1341 ImU32 crc = seed;
1342 const unsigned char* data = (const unsigned char*)data_p;
1343 const ImU32* crc32_lut = GCrc32LookupTable;
1344 if (data_size != 0)
1345 {
1346 while (data_size-- != 0)
1347 {
1348 unsigned char c = *data++;
1349 if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
1350 crc = seed;
1351 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1352 }
1353 }
1354 else
1355 {
1356 while (unsigned char c = *data++)
1357 {
1358 if (c == '#' && data[0] == '#' && data[1] == '#')
1359 crc = seed;
1360 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1361 }
1362 }
1363 return ~crc;
1364 }
1365
ImFileOpen(const char * filename,const char * mode)1366 FILE* ImFileOpen(const char* filename, const char* mode)
1367 {
1368 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
1369 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can)
1370 const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1371 const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1372 ImVector<ImWchar> buf;
1373 buf.resize(filename_wsize + mode_wsize);
1374 ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1375 ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1376 return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1377 #else
1378 return fopen(filename, mode);
1379 #endif
1380 }
1381
1382 // Load file content into memory
1383 // Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
ImFileLoadToMemory(const char * filename,const char * file_open_mode,size_t * out_file_size,int padding_bytes)1384 void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size, int padding_bytes)
1385 {
1386 IM_ASSERT(filename && file_open_mode);
1387 if (out_file_size)
1388 *out_file_size = 0;
1389
1390 FILE* f;
1391 if ((f = ImFileOpen(filename, file_open_mode)) == NULL)
1392 return NULL;
1393
1394 long file_size_signed;
1395 if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
1396 {
1397 fclose(f);
1398 return NULL;
1399 }
1400
1401 size_t file_size = (size_t)file_size_signed;
1402 void* file_data = IM_ALLOC(file_size + padding_bytes);
1403 if (file_data == NULL)
1404 {
1405 fclose(f);
1406 return NULL;
1407 }
1408 if (fread(file_data, 1, file_size, f) != file_size)
1409 {
1410 fclose(f);
1411 IM_FREE(file_data);
1412 return NULL;
1413 }
1414 if (padding_bytes > 0)
1415 memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1416
1417 fclose(f);
1418 if (out_file_size)
1419 *out_file_size = file_size;
1420
1421 return file_data;
1422 }
1423
1424 //-----------------------------------------------------------------------------
1425 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1426 //-----------------------------------------------------------------------------
1427
1428 // Convert UTF-8 to 32-bits character, process single character input.
1429 // Based on stb_from_utf8() from github.com/nothings/stb/
1430 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1431 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1432 {
1433 unsigned int c = (unsigned int)-1;
1434 const unsigned char* str = (const unsigned char*)in_text;
1435 if (!(*str & 0x80))
1436 {
1437 c = (unsigned int)(*str++);
1438 *out_char = c;
1439 return 1;
1440 }
1441 if ((*str & 0xe0) == 0xc0)
1442 {
1443 *out_char = 0xFFFD; // will be invalid but not end of string
1444 if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1445 if (*str < 0xc2) return 2;
1446 c = (unsigned int)((*str++ & 0x1f) << 6);
1447 if ((*str & 0xc0) != 0x80) return 2;
1448 c += (*str++ & 0x3f);
1449 *out_char = c;
1450 return 2;
1451 }
1452 if ((*str & 0xf0) == 0xe0)
1453 {
1454 *out_char = 0xFFFD; // will be invalid but not end of string
1455 if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1456 if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1457 if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1458 c = (unsigned int)((*str++ & 0x0f) << 12);
1459 if ((*str & 0xc0) != 0x80) return 3;
1460 c += (unsigned int)((*str++ & 0x3f) << 6);
1461 if ((*str & 0xc0) != 0x80) return 3;
1462 c += (*str++ & 0x3f);
1463 *out_char = c;
1464 return 3;
1465 }
1466 if ((*str & 0xf8) == 0xf0)
1467 {
1468 *out_char = 0xFFFD; // will be invalid but not end of string
1469 if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1470 if (*str > 0xf4) return 4;
1471 if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1472 if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1473 c = (unsigned int)((*str++ & 0x07) << 18);
1474 if ((*str & 0xc0) != 0x80) return 4;
1475 c += (unsigned int)((*str++ & 0x3f) << 12);
1476 if ((*str & 0xc0) != 0x80) return 4;
1477 c += (unsigned int)((*str++ & 0x3f) << 6);
1478 if ((*str & 0xc0) != 0x80) return 4;
1479 c += (*str++ & 0x3f);
1480 // utf-8 encodings of values used in surrogate pairs are invalid
1481 if ((c & 0xFFFFF800) == 0xD800) return 4;
1482 *out_char = c;
1483 return 4;
1484 }
1485 *out_char = 0;
1486 return 0;
1487 }
1488
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1489 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1490 {
1491 ImWchar* buf_out = buf;
1492 ImWchar* buf_end = buf + buf_size;
1493 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1494 {
1495 unsigned int c;
1496 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1497 if (c == 0)
1498 break;
1499 if (c < 0x10000) // FIXME: Losing characters that don't fit in 2 bytes
1500 *buf_out++ = (ImWchar)c;
1501 }
1502 *buf_out = 0;
1503 if (in_text_remaining)
1504 *in_text_remaining = in_text;
1505 return (int)(buf_out - buf);
1506 }
1507
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1508 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1509 {
1510 int char_count = 0;
1511 while ((!in_text_end || in_text < in_text_end) && *in_text)
1512 {
1513 unsigned int c;
1514 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1515 if (c == 0)
1516 break;
1517 if (c < 0x10000)
1518 char_count++;
1519 }
1520 return char_count;
1521 }
1522
1523 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1524 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1525 {
1526 if (c < 0x80)
1527 {
1528 buf[0] = (char)c;
1529 return 1;
1530 }
1531 if (c < 0x800)
1532 {
1533 if (buf_size < 2) return 0;
1534 buf[0] = (char)(0xc0 + (c >> 6));
1535 buf[1] = (char)(0x80 + (c & 0x3f));
1536 return 2;
1537 }
1538 if (c >= 0xdc00 && c < 0xe000)
1539 {
1540 return 0;
1541 }
1542 if (c >= 0xd800 && c < 0xdc00)
1543 {
1544 if (buf_size < 4) return 0;
1545 buf[0] = (char)(0xf0 + (c >> 18));
1546 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1547 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1548 buf[3] = (char)(0x80 + ((c ) & 0x3f));
1549 return 4;
1550 }
1551 //else if (c < 0x10000)
1552 {
1553 if (buf_size < 3) return 0;
1554 buf[0] = (char)(0xe0 + (c >> 12));
1555 buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1556 buf[2] = (char)(0x80 + ((c ) & 0x3f));
1557 return 3;
1558 }
1559 }
1560
1561 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1562 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1563 {
1564 unsigned int dummy = 0;
1565 return ImTextCharFromUtf8(&dummy, in_text, in_text_end);
1566 }
1567
ImTextCountUtf8BytesFromChar(unsigned int c)1568 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1569 {
1570 if (c < 0x80) return 1;
1571 if (c < 0x800) return 2;
1572 if (c >= 0xdc00 && c < 0xe000) return 0;
1573 if (c >= 0xd800 && c < 0xdc00) return 4;
1574 return 3;
1575 }
1576
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1577 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1578 {
1579 char* buf_out = buf;
1580 const char* buf_end = buf + buf_size;
1581 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1582 {
1583 unsigned int c = (unsigned int)(*in_text++);
1584 if (c < 0x80)
1585 *buf_out++ = (char)c;
1586 else
1587 buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1588 }
1589 *buf_out = 0;
1590 return (int)(buf_out - buf);
1591 }
1592
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1593 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1594 {
1595 int bytes_count = 0;
1596 while ((!in_text_end || in_text < in_text_end) && *in_text)
1597 {
1598 unsigned int c = (unsigned int)(*in_text++);
1599 if (c < 0x80)
1600 bytes_count++;
1601 else
1602 bytes_count += ImTextCountUtf8BytesFromChar(c);
1603 }
1604 return bytes_count;
1605 }
1606
1607 //-----------------------------------------------------------------------------
1608 // [SECTION] MISC HELPERS/UTILTIES (Color functions)
1609 // Note: The Convert functions are early design which are not consistent with other API.
1610 //-----------------------------------------------------------------------------
1611
ColorConvertU32ToFloat4(ImU32 in)1612 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1613 {
1614 float s = 1.0f/255.0f;
1615 return ImVec4(
1616 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1617 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1618 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1619 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1620 }
1621
ColorConvertFloat4ToU32(const ImVec4 & in)1622 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1623 {
1624 ImU32 out;
1625 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1626 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1627 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1628 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1629 return out;
1630 }
1631
1632 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1633 // 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)1634 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1635 {
1636 float K = 0.f;
1637 if (g < b)
1638 {
1639 ImSwap(g, b);
1640 K = -1.f;
1641 }
1642 if (r < g)
1643 {
1644 ImSwap(r, g);
1645 K = -2.f / 6.f - K;
1646 }
1647
1648 const float chroma = r - (g < b ? g : b);
1649 out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1650 out_s = chroma / (r + 1e-20f);
1651 out_v = r;
1652 }
1653
1654 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1655 // 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)1656 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1657 {
1658 if (s == 0.0f)
1659 {
1660 // gray
1661 out_r = out_g = out_b = v;
1662 return;
1663 }
1664
1665 h = ImFmod(h, 1.0f) / (60.0f/360.0f);
1666 int i = (int)h;
1667 float f = h - (float)i;
1668 float p = v * (1.0f - s);
1669 float q = v * (1.0f - s * f);
1670 float t = v * (1.0f - s * (1.0f - f));
1671
1672 switch (i)
1673 {
1674 case 0: out_r = v; out_g = t; out_b = p; break;
1675 case 1: out_r = q; out_g = v; out_b = p; break;
1676 case 2: out_r = p; out_g = v; out_b = t; break;
1677 case 3: out_r = p; out_g = q; out_b = v; break;
1678 case 4: out_r = t; out_g = p; out_b = v; break;
1679 case 5: default: out_r = v; out_g = p; out_b = q; break;
1680 }
1681 }
1682
GetColorU32(ImGuiCol idx,float alpha_mul)1683 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1684 {
1685 ImGuiStyle& style = GImGui->Style;
1686 ImVec4 c = style.Colors[idx];
1687 c.w *= style.Alpha * alpha_mul;
1688 return ColorConvertFloat4ToU32(c);
1689 }
1690
GetColorU32(const ImVec4 & col)1691 ImU32 ImGui::GetColorU32(const ImVec4& col)
1692 {
1693 ImGuiStyle& style = GImGui->Style;
1694 ImVec4 c = col;
1695 c.w *= style.Alpha;
1696 return ColorConvertFloat4ToU32(c);
1697 }
1698
GetStyleColorVec4(ImGuiCol idx)1699 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
1700 {
1701 ImGuiStyle& style = GImGui->Style;
1702 return style.Colors[idx];
1703 }
1704
GetColorU32(ImU32 col)1705 ImU32 ImGui::GetColorU32(ImU32 col)
1706 {
1707 float style_alpha = GImGui->Style.Alpha;
1708 if (style_alpha >= 1.0f)
1709 return col;
1710 ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
1711 a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
1712 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
1713 }
1714
1715 //-----------------------------------------------------------------------------
1716 // [SECTION] ImGuiStorage
1717 // Helper: Key->value storage
1718 //-----------------------------------------------------------------------------
1719
1720 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair> & data,ImGuiID key)1721 static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key)
1722 {
1723 ImGuiStorage::ImGuiStoragePair* first = data.Data;
1724 ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size;
1725 size_t count = (size_t)(last - first);
1726 while (count > 0)
1727 {
1728 size_t count2 = count >> 1;
1729 ImGuiStorage::ImGuiStoragePair* mid = first + count2;
1730 if (mid->key < key)
1731 {
1732 first = ++mid;
1733 count -= count2 + 1;
1734 }
1735 else
1736 {
1737 count = count2;
1738 }
1739 }
1740 return first;
1741 }
1742
1743 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1744 void ImGuiStorage::BuildSortByKey()
1745 {
1746 struct StaticFunc
1747 {
1748 static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1749 {
1750 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1751 if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1;
1752 if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1;
1753 return 0;
1754 }
1755 };
1756 if (Data.Size > 1)
1757 ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID);
1758 }
1759
GetInt(ImGuiID key,int default_val) const1760 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1761 {
1762 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1763 if (it == Data.end() || it->key != key)
1764 return default_val;
1765 return it->val_i;
1766 }
1767
GetBool(ImGuiID key,bool default_val) const1768 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1769 {
1770 return GetInt(key, default_val ? 1 : 0) != 0;
1771 }
1772
GetFloat(ImGuiID key,float default_val) const1773 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1774 {
1775 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1776 if (it == Data.end() || it->key != key)
1777 return default_val;
1778 return it->val_f;
1779 }
1780
GetVoidPtr(ImGuiID key) const1781 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1782 {
1783 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1784 if (it == Data.end() || it->key != key)
1785 return NULL;
1786 return it->val_p;
1787 }
1788
1789 // 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)1790 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1791 {
1792 ImGuiStoragePair* it = LowerBound(Data, key);
1793 if (it == Data.end() || it->key != key)
1794 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1795 return &it->val_i;
1796 }
1797
GetBoolRef(ImGuiID key,bool default_val)1798 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1799 {
1800 return (bool*)GetIntRef(key, default_val ? 1 : 0);
1801 }
1802
GetFloatRef(ImGuiID key,float default_val)1803 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1804 {
1805 ImGuiStoragePair* it = LowerBound(Data, key);
1806 if (it == Data.end() || it->key != key)
1807 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1808 return &it->val_f;
1809 }
1810
GetVoidPtrRef(ImGuiID key,void * default_val)1811 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1812 {
1813 ImGuiStoragePair* it = LowerBound(Data, key);
1814 if (it == Data.end() || it->key != key)
1815 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1816 return &it->val_p;
1817 }
1818
1819 // FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame)
SetInt(ImGuiID key,int val)1820 void ImGuiStorage::SetInt(ImGuiID key, int val)
1821 {
1822 ImGuiStoragePair* it = LowerBound(Data, key);
1823 if (it == Data.end() || it->key != key)
1824 {
1825 Data.insert(it, ImGuiStoragePair(key, val));
1826 return;
1827 }
1828 it->val_i = val;
1829 }
1830
SetBool(ImGuiID key,bool val)1831 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1832 {
1833 SetInt(key, val ? 1 : 0);
1834 }
1835
SetFloat(ImGuiID key,float val)1836 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1837 {
1838 ImGuiStoragePair* it = LowerBound(Data, key);
1839 if (it == Data.end() || it->key != key)
1840 {
1841 Data.insert(it, ImGuiStoragePair(key, val));
1842 return;
1843 }
1844 it->val_f = val;
1845 }
1846
SetVoidPtr(ImGuiID key,void * val)1847 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1848 {
1849 ImGuiStoragePair* it = LowerBound(Data, key);
1850 if (it == Data.end() || it->key != key)
1851 {
1852 Data.insert(it, ImGuiStoragePair(key, val));
1853 return;
1854 }
1855 it->val_p = val;
1856 }
1857
SetAllInt(int v)1858 void ImGuiStorage::SetAllInt(int v)
1859 {
1860 for (int i = 0; i < Data.Size; i++)
1861 Data[i].val_i = v;
1862 }
1863
1864 //-----------------------------------------------------------------------------
1865 // [SECTION] ImGuiTextFilter
1866 //-----------------------------------------------------------------------------
1867
1868 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1869 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1870 {
1871 if (default_filter)
1872 {
1873 ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1874 Build();
1875 }
1876 else
1877 {
1878 InputBuf[0] = 0;
1879 CountGrep = 0;
1880 }
1881 }
1882
Draw(const char * label,float width)1883 bool ImGuiTextFilter::Draw(const char* label, float width)
1884 {
1885 if (width != 0.0f)
1886 ImGui::SetNextItemWidth(width);
1887 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1888 if (value_changed)
1889 Build();
1890 return value_changed;
1891 }
1892
split(char separator,ImVector<ImGuiTextRange> * out) const1893 void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
1894 {
1895 out->resize(0);
1896 const char* wb = b;
1897 const char* we = wb;
1898 while (we < e)
1899 {
1900 if (*we == separator)
1901 {
1902 out->push_back(ImGuiTextRange(wb, we));
1903 wb = we + 1;
1904 }
1905 we++;
1906 }
1907 if (wb != we)
1908 out->push_back(ImGuiTextRange(wb, we));
1909 }
1910
Build()1911 void ImGuiTextFilter::Build()
1912 {
1913 Filters.resize(0);
1914 ImGuiTextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
1915 input_range.split(',', &Filters);
1916
1917 CountGrep = 0;
1918 for (int i = 0; i != Filters.Size; i++)
1919 {
1920 ImGuiTextRange& f = Filters[i];
1921 while (f.b < f.e && ImCharIsBlankA(f.b[0]))
1922 f.b++;
1923 while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
1924 f.e--;
1925 if (f.empty())
1926 continue;
1927 if (Filters[i].b[0] != '-')
1928 CountGrep += 1;
1929 }
1930 }
1931
PassFilter(const char * text,const char * text_end) const1932 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
1933 {
1934 if (Filters.empty())
1935 return true;
1936
1937 if (text == NULL)
1938 text = "";
1939
1940 for (int i = 0; i != Filters.Size; i++)
1941 {
1942 const ImGuiTextRange& f = Filters[i];
1943 if (f.empty())
1944 continue;
1945 if (f.b[0] == '-')
1946 {
1947 // Subtract
1948 if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
1949 return false;
1950 }
1951 else
1952 {
1953 // Grep
1954 if (ImStristr(text, text_end, f.b, f.e) != NULL)
1955 return true;
1956 }
1957 }
1958
1959 // Implicit * grep
1960 if (CountGrep == 0)
1961 return true;
1962
1963 return false;
1964 }
1965
1966 //-----------------------------------------------------------------------------
1967 // [SECTION] ImGuiTextBuffer
1968 //-----------------------------------------------------------------------------
1969
1970 // On some platform vsnprintf() takes va_list by reference and modifies it.
1971 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1972 #ifndef va_copy
1973 #if defined(__GNUC__) || defined(__clang__)
1974 #define va_copy(dest, src) __builtin_va_copy(dest, src)
1975 #else
1976 #define va_copy(dest, src) (dest = src)
1977 #endif
1978 #endif
1979
1980 char ImGuiTextBuffer::EmptyString[1] = { 0 };
1981
append(const char * str,const char * str_end)1982 void ImGuiTextBuffer::append(const char* str, const char* str_end)
1983 {
1984 int len = str_end ? (int)(str_end - str) : (int)strlen(str);
1985
1986 // Add zero-terminator the first time
1987 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
1988 const int needed_sz = write_off + len;
1989 if (write_off + len >= Buf.Capacity)
1990 {
1991 int new_capacity = Buf.Capacity * 2;
1992 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
1993 }
1994
1995 Buf.resize(needed_sz);
1996 memcpy(&Buf[write_off - 1], str, (size_t)len);
1997 Buf[write_off - 1 + len] = 0;
1998 }
1999
appendf(const char * fmt,...)2000 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2001 {
2002 va_list args;
2003 va_start(args, fmt);
2004 appendfv(fmt, args);
2005 va_end(args);
2006 }
2007
2008 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)2009 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2010 {
2011 va_list args_copy;
2012 va_copy(args_copy, args);
2013
2014 int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2015 if (len <= 0)
2016 {
2017 va_end(args_copy);
2018 return;
2019 }
2020
2021 // Add zero-terminator the first time
2022 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2023 const int needed_sz = write_off + len;
2024 if (write_off + len >= Buf.Capacity)
2025 {
2026 int new_capacity = Buf.Capacity * 2;
2027 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2028 }
2029
2030 Buf.resize(needed_sz);
2031 ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2032 va_end(args_copy);
2033 }
2034
2035 //-----------------------------------------------------------------------------
2036 // [SECTION] ImGuiListClipper
2037 // This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed
2038 // the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO)
2039 //-----------------------------------------------------------------------------
2040
2041 // Helper to calculate coarse clipping of large list of evenly sized items.
2042 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
2043 // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX
CalcListClipping(int items_count,float items_height,int * out_items_display_start,int * out_items_display_end)2044 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
2045 {
2046 ImGuiContext& g = *GImGui;
2047 ImGuiWindow* window = g.CurrentWindow;
2048 if (g.LogEnabled)
2049 {
2050 // If logging is active, do not perform any clipping
2051 *out_items_display_start = 0;
2052 *out_items_display_end = items_count;
2053 return;
2054 }
2055 if (window->SkipItems)
2056 {
2057 *out_items_display_start = *out_items_display_end = 0;
2058 return;
2059 }
2060
2061 // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
2062 ImRect unclipped_rect = window->ClipRect;
2063 if (g.NavMoveRequest)
2064 unclipped_rect.Add(g.NavScoringRectScreen);
2065
2066 const ImVec2 pos = window->DC.CursorPos;
2067 int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
2068 int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
2069
2070 // When performing a navigation request, ensure we have one item extra in the direction we are moving to
2071 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
2072 start--;
2073 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
2074 end++;
2075
2076 start = ImClamp(start, 0, items_count);
2077 end = ImClamp(end + 1, start, items_count);
2078 *out_items_display_start = start;
2079 *out_items_display_end = end;
2080 }
2081
SetCursorPosYAndSetupDummyPrevLine(float pos_y,float line_height)2082 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
2083 {
2084 // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2085 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2086 // The clipper should probably have a 4th step to display the last item in a regular manner.
2087 ImGuiContext& g = *GImGui;
2088 ImGuiWindow* window = g.CurrentWindow;
2089 window->DC.CursorPos.y = pos_y;
2090 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y);
2091 window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage.
2092 window->DC.PrevLineSize.y = (line_height - g.Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list.
2093 if (ImGuiColumns* columns = window->DC.CurrentColumns)
2094 columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
2095 }
2096
2097 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
2098 // Use case B: Begin() called from constructor with items_height>0
2099 // FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style.
Begin(int count,float items_height)2100 void ImGuiListClipper::Begin(int count, float items_height)
2101 {
2102 ImGuiContext& g = *GImGui;
2103 ImGuiWindow* window = g.CurrentWindow;
2104
2105 StartPosY = window->DC.CursorPos.y;
2106 ItemsHeight = items_height;
2107 ItemsCount = count;
2108 StepNo = 0;
2109 DisplayEnd = DisplayStart = -1;
2110 if (ItemsHeight > 0.0f)
2111 {
2112 ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
2113 if (DisplayStart > 0)
2114 SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
2115 StepNo = 2;
2116 }
2117 }
2118
End()2119 void ImGuiListClipper::End()
2120 {
2121 if (ItemsCount < 0)
2122 return;
2123 // In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user.
2124 if (ItemsCount < INT_MAX)
2125 SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
2126 ItemsCount = -1;
2127 StepNo = 3;
2128 }
2129
Step()2130 bool ImGuiListClipper::Step()
2131 {
2132 ImGuiContext& g = *GImGui;
2133 ImGuiWindow* window = g.CurrentWindow;
2134
2135 if (ItemsCount == 0 || window->SkipItems)
2136 {
2137 ItemsCount = -1;
2138 return false;
2139 }
2140 if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height.
2141 {
2142 DisplayStart = 0;
2143 DisplayEnd = 1;
2144 StartPosY = window->DC.CursorPos.y;
2145 StepNo = 1;
2146 return true;
2147 }
2148 if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element.
2149 {
2150 if (ItemsCount == 1) { ItemsCount = -1; return false; }
2151 float items_height = window->DC.CursorPos.y - StartPosY;
2152 IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically
2153 Begin(ItemsCount - 1, items_height);
2154 DisplayStart++;
2155 DisplayEnd++;
2156 StepNo = 3;
2157 return true;
2158 }
2159 if (StepNo == 2) // Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3.
2160 {
2161 IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
2162 StepNo = 3;
2163 return true;
2164 }
2165 if (StepNo == 3) // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop.
2166 End();
2167 return false;
2168 }
2169
2170 //-----------------------------------------------------------------------------
2171 // [SECTION] RENDER HELPERS
2172 // Those (internal) functions are currently quite a legacy mess - their signature and behavior will change.
2173 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: state.
2174 //-----------------------------------------------------------------------------
2175
FindRenderedTextEnd(const char * text,const char * text_end)2176 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2177 {
2178 const char* text_display_end = text;
2179 if (!text_end)
2180 text_end = (const char*)-1;
2181
2182 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2183 text_display_end++;
2184 return text_display_end;
2185 }
2186
2187 // Internal ImGui functions to render text
2188 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2189 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2190 {
2191 ImGuiContext& g = *GImGui;
2192 ImGuiWindow* window = g.CurrentWindow;
2193
2194 // Hide anything after a '##' string
2195 const char* text_display_end;
2196 if (hide_text_after_hash)
2197 {
2198 text_display_end = FindRenderedTextEnd(text, text_end);
2199 }
2200 else
2201 {
2202 if (!text_end)
2203 text_end = text + strlen(text); // FIXME-OPT
2204 text_display_end = text_end;
2205 }
2206
2207 if (text != text_display_end)
2208 {
2209 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2210 if (g.LogEnabled)
2211 LogRenderedText(&pos, text, text_display_end);
2212 }
2213 }
2214
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2215 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2216 {
2217 ImGuiContext& g = *GImGui;
2218 ImGuiWindow* window = g.CurrentWindow;
2219
2220 if (!text_end)
2221 text_end = text + strlen(text); // FIXME-OPT
2222
2223 if (text != text_end)
2224 {
2225 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2226 if (g.LogEnabled)
2227 LogRenderedText(&pos, text, text_end);
2228 }
2229 }
2230
2231 // Default clip_rect uses (pos_min,pos_max)
2232 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
RenderTextClippedEx(ImDrawList * draw_list,const ImVec2 & pos_min,const ImVec2 & pos_max,const char * text,const char * text_display_end,const ImVec2 * text_size_if_known,const ImVec2 & align,const ImRect * clip_rect)2233 void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
2234 {
2235 // Perform CPU side clipping for single clipped element to avoid using scissor state
2236 ImVec2 pos = pos_min;
2237 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2238
2239 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2240 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2241 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2242 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2243 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2244
2245 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2246 if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2247 if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2248
2249 // Render
2250 if (need_clipping)
2251 {
2252 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2253 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2254 }
2255 else
2256 {
2257 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2258 }
2259 }
2260
RenderTextClipped(const ImVec2 & pos_min,const ImVec2 & pos_max,const char * text,const char * text_end,const ImVec2 * text_size_if_known,const ImVec2 & align,const ImRect * clip_rect)2261 void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
2262 {
2263 // Hide anything after a '##' string
2264 const char* text_display_end = FindRenderedTextEnd(text, text_end);
2265 const int text_len = (int)(text_display_end - text);
2266 if (text_len == 0)
2267 return;
2268
2269 ImGuiContext& g = *GImGui;
2270 ImGuiWindow* window = g.CurrentWindow;
2271 RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2272 if (g.LogEnabled)
2273 LogRenderedText(&pos_min, text, text_display_end);
2274 }
2275
2276
2277 // Another overly complex function until we reorganize everything into a nice all-in-one helper.
2278 // This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) which define _where_ the ellipsis is, from actual clipping of text and limit of the ellipsis display.
2279 // This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move.
RenderTextEllipsis(ImDrawList * draw_list,const ImVec2 & pos_min,const ImVec2 & pos_max,float clip_max_x,float ellipsis_max_x,const char * text,const char * text_end_full,const ImVec2 * text_size_if_known)2280 void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known)
2281 {
2282 ImGuiContext& g = *GImGui;
2283 if (text_end_full == NULL)
2284 text_end_full = FindRenderedTextEnd(text);
2285 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
2286
2287 //draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 4), IM_COL32(0, 0, 255, 255));
2288 //draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y-2), ImVec2(ellipsis_max_x, pos_max.y+2), IM_COL32(0, 255, 0, 255));
2289 //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
2290 // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
2291 if (text_size.x > pos_max.x - pos_min.x)
2292 {
2293 // Hello wo...
2294 // | | |
2295 // min max ellipsis_max
2296 // <-> this is generally some padding value
2297
2298 const ImFont* font = draw_list->_Data->Font;
2299 const float font_size = draw_list->_Data->FontSize;
2300 const char* text_end_ellipsis = NULL;
2301
2302 ImWchar ellipsis_char = font->EllipsisChar;
2303 int ellipsis_char_count = 1;
2304 if (ellipsis_char == (ImWchar)-1)
2305 {
2306 ellipsis_char = (ImWchar)'.';
2307 ellipsis_char_count = 3;
2308 }
2309 const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char);
2310
2311 float ellipsis_glyph_width = glyph->X1; // Width of the glyph with no padding on either side
2312 float ellipsis_total_width = ellipsis_glyph_width; // Full width of entire ellipsis
2313
2314 if (ellipsis_char_count > 1)
2315 {
2316 // Full ellipsis size without free spacing after it.
2317 const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize);
2318 ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots;
2319 ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots;
2320 }
2321
2322 // We can now claim the space between pos_max.x and ellipsis_max.x
2323 const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f);
2324 float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
2325 if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
2326 {
2327 // Always display at least 1 character if there's no room for character + ellipsis
2328 text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
2329 text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
2330 }
2331 while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
2332 {
2333 // Trim trailing space before ellipsis (FIXME: Supporting non-ascii blanks would be nice, for this we need a function to backtrack in UTF-8 text)
2334 text_end_ellipsis--;
2335 text_size_clipped_x -= font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text_end_ellipsis, text_end_ellipsis + 1).x; // Ascii blanks are always 1 byte
2336 }
2337
2338 // Render text, render ellipsis
2339 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
2340 float ellipsis_x = pos_min.x + text_size_clipped_x;
2341 if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x)
2342 for (int i = 0; i < ellipsis_char_count; i++)
2343 {
2344 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char);
2345 ellipsis_x += ellipsis_glyph_width;
2346 }
2347 }
2348 else
2349 {
2350 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
2351 }
2352
2353 if (g.LogEnabled)
2354 LogRenderedText(&pos_min, text, text_end_full);
2355 }
2356
2357 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2358 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2359 {
2360 ImGuiContext& g = *GImGui;
2361 ImGuiWindow* window = g.CurrentWindow;
2362 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2363 const float border_size = g.Style.FrameBorderSize;
2364 if (border && border_size > 0.0f)
2365 {
2366 window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2367 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2368 }
2369 }
2370
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2371 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2372 {
2373 ImGuiContext& g = *GImGui;
2374 ImGuiWindow* window = g.CurrentWindow;
2375 const float border_size = g.Style.FrameBorderSize;
2376 if (border_size > 0.0f)
2377 {
2378 window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2379 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2380 }
2381 }
2382
2383 // Render an arrow aimed to be aligned with text (p_min is a position in the same space text would be positioned). To e.g. denote expanded/collapsed state
RenderArrow(ImDrawList * draw_list,ImVec2 pos,ImU32 col,ImGuiDir dir,float scale)2384 void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale)
2385 {
2386 const float h = draw_list->_Data->FontSize * 1.00f;
2387 float r = h * 0.40f * scale;
2388 ImVec2 center = pos + ImVec2(h * 0.50f, h * 0.50f * scale);
2389
2390 ImVec2 a, b, c;
2391 switch (dir)
2392 {
2393 case ImGuiDir_Up:
2394 case ImGuiDir_Down:
2395 if (dir == ImGuiDir_Up) r = -r;
2396 a = ImVec2(+0.000f,+0.750f) * r;
2397 b = ImVec2(-0.866f,-0.750f) * r;
2398 c = ImVec2(+0.866f,-0.750f) * r;
2399 break;
2400 case ImGuiDir_Left:
2401 case ImGuiDir_Right:
2402 if (dir == ImGuiDir_Left) r = -r;
2403 a = ImVec2(+0.750f,+0.000f) * r;
2404 b = ImVec2(-0.750f,+0.866f) * r;
2405 c = ImVec2(-0.750f,-0.866f) * r;
2406 break;
2407 case ImGuiDir_None:
2408 case ImGuiDir_COUNT:
2409 IM_ASSERT(0);
2410 break;
2411 }
2412 draw_list->AddTriangleFilled(center + a, center + b, center + c, col);
2413 }
2414
RenderBullet(ImDrawList * draw_list,ImVec2 pos,ImU32 col)2415 void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col)
2416 {
2417 draw_list->AddCircleFilled(pos, draw_list->_Data->FontSize * 0.20f, col, 8);
2418 }
2419
RenderCheckMark(ImVec2 pos,ImU32 col,float sz)2420 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
2421 {
2422 ImGuiContext& g = *GImGui;
2423 ImGuiWindow* window = g.CurrentWindow;
2424
2425 float thickness = ImMax(sz / 5.0f, 1.0f);
2426 sz -= thickness*0.5f;
2427 pos += ImVec2(thickness*0.25f, thickness*0.25f);
2428
2429 float third = sz / 3.0f;
2430 float bx = pos.x + third;
2431 float by = pos.y + sz - third*0.5f;
2432 window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
2433 window->DrawList->PathLineTo(ImVec2(bx, by));
2434 window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2));
2435 window->DrawList->PathStroke(col, false, thickness);
2436 }
2437
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2438 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2439 {
2440 ImGuiContext& g = *GImGui;
2441 if (id != g.NavId)
2442 return;
2443 if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2444 return;
2445 ImGuiWindow* window = g.CurrentWindow;
2446 if (window->DC.NavHideHighlightOneFrame)
2447 return;
2448
2449 float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2450 ImRect display_rect = bb;
2451 display_rect.ClipWith(window->ClipRect);
2452 if (flags & ImGuiNavHighlightFlags_TypeDefault)
2453 {
2454 const float THICKNESS = 2.0f;
2455 const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2456 display_rect.Expand(ImVec2(DISTANCE,DISTANCE));
2457 bool fully_visible = window->ClipRect.Contains(display_rect);
2458 if (!fully_visible)
2459 window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2460 window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS);
2461 if (!fully_visible)
2462 window->DrawList->PopClipRect();
2463 }
2464 if (flags & ImGuiNavHighlightFlags_TypeThin)
2465 {
2466 window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2467 }
2468 }
2469
2470 //-----------------------------------------------------------------------------
2471 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2472 //-----------------------------------------------------------------------------
2473
2474 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2475 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
2476 : DrawListInst(&context->DrawListSharedData)
2477 {
2478 Name = ImStrdup(name);
2479 ID = ImHashStr(name);
2480 IDStack.push_back(ID);
2481 Flags = ImGuiWindowFlags_None;
2482 Pos = ImVec2(0.0f, 0.0f);
2483 Size = SizeFull = ImVec2(0.0f, 0.0f);
2484 ContentSize = ContentSizeExplicit = ImVec2(0.0f, 0.0f);
2485 WindowPadding = ImVec2(0.0f, 0.0f);
2486 WindowRounding = 0.0f;
2487 WindowBorderSize = 0.0f;
2488 NameBufLen = (int)strlen(name) + 1;
2489 MoveId = GetID("#MOVE");
2490 ChildId = 0;
2491 Scroll = ImVec2(0.0f, 0.0f);
2492 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2493 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2494 ScrollbarSizes = ImVec2(0.0f, 0.0f);
2495 ScrollbarX = ScrollbarY = false;
2496 Active = WasActive = false;
2497 WriteAccessed = false;
2498 Collapsed = false;
2499 WantCollapseToggle = false;
2500 SkipItems = false;
2501 Appearing = false;
2502 Hidden = false;
2503 HasCloseButton = false;
2504 ResizeBorderHeld = -1;
2505 BeginCount = 0;
2506 BeginOrderWithinParent = -1;
2507 BeginOrderWithinContext = -1;
2508 PopupId = 0;
2509 AutoFitFramesX = AutoFitFramesY = -1;
2510 AutoFitChildAxises = 0x00;
2511 AutoFitOnlyGrows = false;
2512 AutoPosLastDirection = ImGuiDir_None;
2513 HiddenFramesCanSkipItems = HiddenFramesCannotSkipItems = 0;
2514 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2515 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2516
2517 InnerRect = ImRect(0.0f, 0.0f, 0.0f, 0.0f); // Clear so the InnerRect.GetSize() code in Begin() doesn't lead to overflow even if the result isn't used.
2518
2519 LastFrameActive = -1;
2520 LastTimeActive = -1.0f;
2521 ItemWidthDefault = 0.0f;
2522 FontWindowScale = 1.0f;
2523 SettingsIdx = -1;
2524
2525 DrawList = &DrawListInst;
2526 DrawList->_OwnerName = Name;
2527 ParentWindow = NULL;
2528 RootWindow = NULL;
2529 RootWindowForTitleBarHighlight = NULL;
2530 RootWindowForNav = NULL;
2531
2532 NavLastIds[0] = NavLastIds[1] = 0;
2533 NavRectRel[0] = NavRectRel[1] = ImRect();
2534 NavLastChildNavWindow = NULL;
2535
2536 MemoryCompacted = false;
2537 MemoryDrawListIdxCapacity = MemoryDrawListVtxCapacity = 0;
2538 }
2539
~ImGuiWindow()2540 ImGuiWindow::~ImGuiWindow()
2541 {
2542 IM_ASSERT(DrawList == &DrawListInst);
2543 IM_DELETE(Name);
2544 for (int i = 0; i != ColumnsStorage.Size; i++)
2545 ColumnsStorage[i].~ImGuiColumns();
2546 }
2547
GetID(const char * str,const char * str_end)2548 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2549 {
2550 ImGuiID seed = IDStack.back();
2551 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2552 ImGui::KeepAliveID(id);
2553 return id;
2554 }
2555
GetID(const void * ptr)2556 ImGuiID ImGuiWindow::GetID(const void* ptr)
2557 {
2558 ImGuiID seed = IDStack.back();
2559 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2560 ImGui::KeepAliveID(id);
2561 return id;
2562 }
2563
GetID(int n)2564 ImGuiID ImGuiWindow::GetID(int n)
2565 {
2566 ImGuiID seed = IDStack.back();
2567 ImGuiID id = ImHashData(&n, sizeof(n), seed);
2568 ImGui::KeepAliveID(id);
2569 return id;
2570 }
2571
GetIDNoKeepAlive(const char * str,const char * str_end)2572 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2573 {
2574 ImGuiID seed = IDStack.back();
2575 return ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2576 }
2577
GetIDNoKeepAlive(const void * ptr)2578 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2579 {
2580 ImGuiID seed = IDStack.back();
2581 return ImHashData(&ptr, sizeof(void*), seed);
2582 }
2583
GetIDNoKeepAlive(int n)2584 ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
2585 {
2586 ImGuiID seed = IDStack.back();
2587 return ImHashData(&n, sizeof(n), seed);
2588 }
2589
2590 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2591 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2592 {
2593 ImGuiID seed = IDStack.back();
2594 const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) };
2595 ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
2596 ImGui::KeepAliveID(id);
2597 return id;
2598 }
2599
SetCurrentWindow(ImGuiWindow * window)2600 static void SetCurrentWindow(ImGuiWindow* window)
2601 {
2602 ImGuiContext& g = *GImGui;
2603 g.CurrentWindow = window;
2604 if (window)
2605 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2606 }
2607
2608 // Free up/compact internal window buffers, we can use this when a window becomes unused.
2609 // This is currently unused by the library, but you may call this yourself for easy GC.
2610 // Not freed:
2611 // - ImGuiWindow, ImGuiWindowSettings, Name
2612 // - StateStorage, ColumnsStorage (may hold useful data)
2613 // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
GcCompactTransientWindowBuffers(ImGuiWindow * window)2614 void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
2615 {
2616 window->MemoryCompacted = true;
2617 window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
2618 window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
2619 window->IDStack.clear();
2620 window->DrawList->ClearFreeMemory();
2621 window->DC.ChildWindows.clear();
2622 window->DC.ItemFlagsStack.clear();
2623 window->DC.ItemWidthStack.clear();
2624 window->DC.TextWrapPosStack.clear();
2625 window->DC.GroupStack.clear();
2626 }
2627
GcAwakeTransientWindowBuffers(ImGuiWindow * window)2628 void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
2629 {
2630 // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
2631 // The other buffers tends to amortize much faster.
2632 window->MemoryCompacted = false;
2633 window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
2634 window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
2635 window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
2636 }
2637
SetNavID(ImGuiID id,int nav_layer)2638 void ImGui::SetNavID(ImGuiID id, int nav_layer)
2639 {
2640 ImGuiContext& g = *GImGui;
2641 IM_ASSERT(g.NavWindow);
2642 IM_ASSERT(nav_layer == 0 || nav_layer == 1);
2643 g.NavId = id;
2644 g.NavWindow->NavLastIds[nav_layer] = id;
2645 }
2646
SetNavIDWithRectRel(ImGuiID id,int nav_layer,const ImRect & rect_rel)2647 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel)
2648 {
2649 ImGuiContext& g = *GImGui;
2650 SetNavID(id, nav_layer);
2651 g.NavWindow->NavRectRel[nav_layer] = rect_rel;
2652 g.NavMousePosDirty = true;
2653 g.NavDisableHighlight = false;
2654 g.NavDisableMouseHover = true;
2655 }
2656
SetActiveID(ImGuiID id,ImGuiWindow * window)2657 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2658 {
2659 ImGuiContext& g = *GImGui;
2660 g.ActiveIdIsJustActivated = (g.ActiveId != id);
2661 if (g.ActiveIdIsJustActivated)
2662 {
2663 g.ActiveIdTimer = 0.0f;
2664 g.ActiveIdHasBeenPressedBefore = false;
2665 g.ActiveIdHasBeenEditedBefore = false;
2666 if (id != 0)
2667 {
2668 g.LastActiveId = id;
2669 g.LastActiveIdTimer = 0.0f;
2670 }
2671 }
2672 g.ActiveId = id;
2673 g.ActiveIdAllowOverlap = false;
2674 g.ActiveIdWindow = window;
2675 g.ActiveIdHasBeenEditedThisFrame = false;
2676 if (id)
2677 {
2678 g.ActiveIdIsAlive = id;
2679 g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
2680 }
2681
2682 // Clear declaration of inputs claimed by the widget
2683 // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
2684 g.ActiveIdUsingNavDirMask = 0x00;
2685 g.ActiveIdUsingNavInputMask = 0x00;
2686 g.ActiveIdUsingKeyInputMask = 0x00;
2687 }
2688
2689 // FIXME-NAV: The existence of SetNavID/SetNavIDWithRectRel/SetFocusID is incredibly messy and confusing and needs some explanation or refactoring.
SetFocusID(ImGuiID id,ImGuiWindow * window)2690 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
2691 {
2692 ImGuiContext& g = *GImGui;
2693 IM_ASSERT(id != 0);
2694
2695 // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it.
2696 const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
2697 if (g.NavWindow != window)
2698 g.NavInitRequest = false;
2699 g.NavId = id;
2700 g.NavWindow = window;
2701 g.NavLayer = nav_layer;
2702 window->NavLastIds[nav_layer] = id;
2703 if (window->DC.LastItemId == id)
2704 window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
2705
2706 if (g.ActiveIdSource == ImGuiInputSource_Nav)
2707 g.NavDisableMouseHover = true;
2708 else
2709 g.NavDisableHighlight = true;
2710 }
2711
ClearActiveID()2712 void ImGui::ClearActiveID()
2713 {
2714 SetActiveID(0, NULL);
2715 }
2716
SetHoveredID(ImGuiID id)2717 void ImGui::SetHoveredID(ImGuiID id)
2718 {
2719 ImGuiContext& g = *GImGui;
2720 g.HoveredId = id;
2721 g.HoveredIdAllowOverlap = false;
2722 if (id != 0 && g.HoveredIdPreviousFrame != id)
2723 g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
2724 }
2725
GetHoveredID()2726 ImGuiID ImGui::GetHoveredID()
2727 {
2728 ImGuiContext& g = *GImGui;
2729 return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
2730 }
2731
KeepAliveID(ImGuiID id)2732 void ImGui::KeepAliveID(ImGuiID id)
2733 {
2734 ImGuiContext& g = *GImGui;
2735 if (g.ActiveId == id)
2736 g.ActiveIdIsAlive = id;
2737 if (g.ActiveIdPreviousFrame == id)
2738 g.ActiveIdPreviousFrameIsAlive = true;
2739 }
2740
MarkItemEdited(ImGuiID id)2741 void ImGui::MarkItemEdited(ImGuiID id)
2742 {
2743 // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
2744 // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need need to fill the data.
2745 ImGuiContext& g = *GImGui;
2746 IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
2747 IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out.
2748 //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
2749 g.ActiveIdHasBeenEditedThisFrame = true;
2750 g.ActiveIdHasBeenEditedBefore = true;
2751 g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
2752 }
2753
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)2754 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
2755 {
2756 // An active popup disable hovering on other windows (apart from its own children)
2757 // FIXME-OPT: This could be cached/stored within the window.
2758 ImGuiContext& g = *GImGui;
2759 if (g.NavWindow)
2760 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
2761 if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
2762 {
2763 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
2764 // NB: The order of those two tests is important because Modal windows are also Popups.
2765 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
2766 return false;
2767 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
2768 return false;
2769 }
2770
2771 return true;
2772 }
2773
2774 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_baseline_y)2775 void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
2776 {
2777 ImGuiContext& g = *GImGui;
2778 ImGuiWindow* window = g.CurrentWindow;
2779 if (window->SkipItems)
2780 return;
2781
2782 // We increase the height in this function to accommodate for baseline offset.
2783 // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
2784 // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
2785 const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
2786 const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
2787
2788 // Always align ourselves on pixel boundaries
2789 //if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG]
2790 window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
2791 window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
2792 window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line
2793 window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y); // Next line
2794 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
2795 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
2796 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
2797
2798 window->DC.PrevLineSize.y = line_height;
2799 window->DC.CurrLineSize.y = 0.0f;
2800 window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
2801 window->DC.CurrLineTextBaseOffset = 0.0f;
2802
2803 // Horizontal layout mode
2804 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
2805 SameLine();
2806 }
2807
ItemSize(const ImRect & bb,float text_baseline_y)2808 void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
2809 {
2810 ItemSize(bb.GetSize(), text_baseline_y);
2811 }
2812
2813 // Declare item bounding box for clipping and interaction.
2814 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
2815 // declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd().
ItemAdd(const ImRect & bb,ImGuiID id,const ImRect * nav_bb_arg)2816 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
2817 {
2818 ImGuiContext& g = *GImGui;
2819 ImGuiWindow* window = g.CurrentWindow;
2820
2821 if (id != 0)
2822 {
2823 // Navigation processing runs prior to clipping early-out
2824 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
2825 // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
2826 // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
2827 // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
2828 // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
2829 // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
2830 // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
2831 // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
2832 window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
2833 if (g.NavId == id || g.NavAnyRequest)
2834 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
2835 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
2836 NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
2837
2838 // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd()
2839 #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
2840 if (id == g.DebugItemPickerBreakID)
2841 {
2842 IM_DEBUG_BREAK();
2843 g.DebugItemPickerBreakID = 0;
2844 }
2845 #endif
2846 }
2847
2848 window->DC.LastItemId = id;
2849 window->DC.LastItemRect = bb;
2850 window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
2851 g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
2852
2853 #ifdef IMGUI_ENABLE_TEST_ENGINE
2854 if (id != 0)
2855 IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id);
2856 #endif
2857
2858 // Clipping test
2859 const bool is_clipped = IsClippedEx(bb, id, false);
2860 if (is_clipped)
2861 return false;
2862 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2863
2864 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2865 if (IsMouseHoveringRect(bb.Min, bb.Max))
2866 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
2867 return true;
2868 }
2869
2870 // This is roughly matching the behavior of internal-facing ItemHoverable()
2871 // - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered()
2872 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)2873 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2874 {
2875 ImGuiContext& g = *GImGui;
2876 ImGuiWindow* window = g.CurrentWindow;
2877 if (g.NavDisableMouseHover && !g.NavDisableHighlight)
2878 return IsItemFocused();
2879
2880 // Test for bounding box overlap, as updated as ItemAdd()
2881 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
2882 return false;
2883 IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function
2884
2885 // Test if we are hovering the right window (our window could be behind another window)
2886 // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable to use IsItemHovered() after EndChild() itself.
2887 // Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while.
2888 //if (g.HoveredWindow != window)
2889 // return false;
2890 if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2891 return false;
2892
2893 // Test if another item is active (e.g. being dragged)
2894 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2895 if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2896 return false;
2897
2898 // Test if interactions on this window are blocked by an active popup or modal.
2899 // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
2900 if (!IsWindowContentHoverable(window, flags))
2901 return false;
2902
2903 // Test if the item is disabled
2904 if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
2905 return false;
2906
2907 // Special handling for the dummy item after Begin() which represent the title bar or tab.
2908 // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
2909 if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2910 return false;
2911 return true;
2912 }
2913
2914 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)2915 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2916 {
2917 ImGuiContext& g = *GImGui;
2918 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
2919 return false;
2920
2921 ImGuiWindow* window = g.CurrentWindow;
2922 if (g.HoveredWindow != window)
2923 return false;
2924 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
2925 return false;
2926 if (!IsMouseHoveringRect(bb.Min, bb.Max))
2927 return false;
2928 if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
2929 return false;
2930 if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2931 return false;
2932
2933 SetHoveredID(id);
2934
2935 // [DEBUG] Item Picker tool!
2936 // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
2937 // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
2938 // items if we perform the test in ItemAdd(), but that would incur a small runtime cost.
2939 // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd().
2940 if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
2941 GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
2942 if (g.DebugItemPickerBreakID == id)
2943 IM_DEBUG_BREAK();
2944
2945 return true;
2946 }
2947
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)2948 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
2949 {
2950 ImGuiContext& g = *GImGui;
2951 ImGuiWindow* window = g.CurrentWindow;
2952 if (!bb.Overlaps(window->ClipRect))
2953 if (id == 0 || id != g.ActiveId)
2954 if (clip_even_when_logged || !g.LogEnabled)
2955 return true;
2956 return false;
2957 }
2958
2959 // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
FocusableItemRegister(ImGuiWindow * window,ImGuiID id)2960 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id)
2961 {
2962 ImGuiContext& g = *GImGui;
2963
2964 // Increment counters
2965 const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
2966 window->DC.FocusCounterAll++;
2967 if (is_tab_stop)
2968 window->DC.FocusCounterTab++;
2969
2970 // Process TAB/Shift-TAB to tab *OUT* of the currently focused item.
2971 // (Note that we can always TAB out of a widget that doesn't allow tabbing in)
2972 if (g.ActiveId == id && g.FocusTabPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.FocusRequestNextWindow == NULL)
2973 {
2974 g.FocusRequestNextWindow = window;
2975 g.FocusRequestNextCounterTab = window->DC.FocusCounterTab + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items.
2976 }
2977
2978 // Handle focus requests
2979 if (g.FocusRequestCurrWindow == window)
2980 {
2981 if (window->DC.FocusCounterAll == g.FocusRequestCurrCounterAll)
2982 return true;
2983 if (is_tab_stop && window->DC.FocusCounterTab == g.FocusRequestCurrCounterTab)
2984 {
2985 g.NavJustTabbedId = id;
2986 return true;
2987 }
2988
2989 // If another item is about to be focused, we clear our own active id
2990 if (g.ActiveId == id)
2991 ClearActiveID();
2992 }
2993
2994 return false;
2995 }
2996
FocusableItemUnregister(ImGuiWindow * window)2997 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
2998 {
2999 window->DC.FocusCounterAll--;
3000 window->DC.FocusCounterTab--;
3001 }
3002
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)3003 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
3004 {
3005 if (wrap_pos_x < 0.0f)
3006 return 0.0f;
3007
3008 ImGuiWindow* window = GImGui->CurrentWindow;
3009 if (wrap_pos_x == 0.0f)
3010 wrap_pos_x = window->WorkRect.Max.x;
3011 else if (wrap_pos_x > 0.0f)
3012 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
3013
3014 return ImMax(wrap_pos_x - pos.x, 1.0f);
3015 }
3016
3017 // IM_ALLOC() == ImGui::MemAlloc()
MemAlloc(size_t size)3018 void* ImGui::MemAlloc(size_t size)
3019 {
3020 if (ImGuiContext* ctx = GImGui)
3021 ctx->IO.MetricsActiveAllocations++;
3022 return GImAllocatorAllocFunc(size, GImAllocatorUserData);
3023 }
3024
3025 // IM_FREE() == ImGui::MemFree()
MemFree(void * ptr)3026 void ImGui::MemFree(void* ptr)
3027 {
3028 if (ptr)
3029 if (ImGuiContext* ctx = GImGui)
3030 ctx->IO.MetricsActiveAllocations--;
3031 return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
3032 }
3033
GetClipboardText()3034 const char* ImGui::GetClipboardText()
3035 {
3036 ImGuiContext& g = *GImGui;
3037 return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : "";
3038 }
3039
SetClipboardText(const char * text)3040 void ImGui::SetClipboardText(const char* text)
3041 {
3042 ImGuiContext& g = *GImGui;
3043 if (g.IO.SetClipboardTextFn)
3044 g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);
3045 }
3046
GetVersion()3047 const char* ImGui::GetVersion()
3048 {
3049 return IMGUI_VERSION;
3050 }
3051
3052 // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3053 // Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module
GetCurrentContext()3054 ImGuiContext* ImGui::GetCurrentContext()
3055 {
3056 return GImGui;
3057 }
3058
SetCurrentContext(ImGuiContext * ctx)3059 void ImGui::SetCurrentContext(ImGuiContext* ctx)
3060 {
3061 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3062 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3063 #else
3064 GImGui = ctx;
3065 #endif
3066 }
3067
3068 // Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.
3069 // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
3070 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code
3071 // may see different structures than what imgui.cpp sees, which is problematic.
3072 // We usually require settings to be in imconfig.h to make sure that they are accessible to all compilation units involved with Dear ImGui.
DebugCheckVersionAndDataLayout(const char * version,size_t sz_io,size_t sz_style,size_t sz_vec2,size_t sz_vec4,size_t sz_vert,size_t sz_idx)3073 bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx)
3074 {
3075 bool error = false;
3076 if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatched version string!"); }
3077 if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
3078 if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
3079 if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
3080 if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
3081 if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
3082 if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
3083 return !error;
3084 }
3085
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)3086 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
3087 {
3088 GImAllocatorAllocFunc = alloc_func;
3089 GImAllocatorFreeFunc = free_func;
3090 GImAllocatorUserData = user_data;
3091 }
3092
CreateContext(ImFontAtlas * shared_font_atlas)3093 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3094 {
3095 ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3096 if (GImGui == NULL)
3097 SetCurrentContext(ctx);
3098 Initialize(ctx);
3099 return ctx;
3100 }
3101
DestroyContext(ImGuiContext * ctx)3102 void ImGui::DestroyContext(ImGuiContext* ctx)
3103 {
3104 if (ctx == NULL)
3105 ctx = GImGui;
3106 Shutdown(ctx);
3107 if (GImGui == ctx)
3108 SetCurrentContext(NULL);
3109 IM_DELETE(ctx);
3110 }
3111
GetIO()3112 ImGuiIO& ImGui::GetIO()
3113 {
3114 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3115 return GImGui->IO;
3116 }
3117
GetStyle()3118 ImGuiStyle& ImGui::GetStyle()
3119 {
3120 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3121 return GImGui->Style;
3122 }
3123
3124 // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
GetDrawData()3125 ImDrawData* ImGui::GetDrawData()
3126 {
3127 ImGuiContext& g = *GImGui;
3128 return g.DrawData.Valid ? &g.DrawData : NULL;
3129 }
3130
GetTime()3131 double ImGui::GetTime()
3132 {
3133 return GImGui->Time;
3134 }
3135
GetFrameCount()3136 int ImGui::GetFrameCount()
3137 {
3138 return GImGui->FrameCount;
3139 }
3140
GetBackgroundDrawList()3141 ImDrawList* ImGui::GetBackgroundDrawList()
3142 {
3143 return &GImGui->BackgroundDrawList;
3144 }
3145
GetForegroundDrawList()3146 ImDrawList* ImGui::GetForegroundDrawList()
3147 {
3148 return &GImGui->ForegroundDrawList;
3149 }
3150
GetDrawListSharedData()3151 ImDrawListSharedData* ImGui::GetDrawListSharedData()
3152 {
3153 return &GImGui->DrawListSharedData;
3154 }
3155
StartMouseMovingWindow(ImGuiWindow * window)3156 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3157 {
3158 // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3159 // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3160 // This is because we want ActiveId to be set even when the window is not permitted to move.
3161 ImGuiContext& g = *GImGui;
3162 FocusWindow(window);
3163 SetActiveID(window->MoveId, window);
3164 g.NavDisableHighlight = true;
3165 g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
3166
3167 bool can_move_window = true;
3168 if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3169 can_move_window = false;
3170 if (can_move_window)
3171 g.MovingWindow = window;
3172 }
3173
3174 // Handle mouse moving window
3175 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
UpdateMouseMovingWindowNewFrame()3176 void ImGui::UpdateMouseMovingWindowNewFrame()
3177 {
3178 ImGuiContext& g = *GImGui;
3179 if (g.MovingWindow != NULL)
3180 {
3181 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3182 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3183 KeepAliveID(g.ActiveId);
3184 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3185 ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3186 if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3187 {
3188 ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3189 if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3190 {
3191 MarkIniSettingsDirty(moving_window);
3192 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3193 }
3194 FocusWindow(g.MovingWindow);
3195 }
3196 else
3197 {
3198 ClearActiveID();
3199 g.MovingWindow = NULL;
3200 }
3201 }
3202 else
3203 {
3204 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3205 if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3206 {
3207 KeepAliveID(g.ActiveId);
3208 if (!g.IO.MouseDown[0])
3209 ClearActiveID();
3210 }
3211 }
3212 }
3213
3214 // Initiate moving window, handle left-click and right-click focus
UpdateMouseMovingWindowEndFrame()3215 void ImGui::UpdateMouseMovingWindowEndFrame()
3216 {
3217 // Initiate moving window
3218 ImGuiContext& g = *GImGui;
3219 if (g.ActiveId != 0 || g.HoveredId != 0)
3220 return;
3221
3222 // Unless we just made a window/popup appear
3223 if (g.NavWindow && g.NavWindow->Appearing)
3224 return;
3225
3226 // Click to focus window and start moving (after we're done with all our widgets)
3227 if (g.IO.MouseClicked[0])
3228 {
3229 if (g.HoveredRootWindow != NULL)
3230 {
3231 StartMouseMovingWindow(g.HoveredWindow);
3232 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar))
3233 if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3234 g.MovingWindow = NULL;
3235 }
3236 else if (g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
3237 {
3238 // Clicking on void disable focus
3239 FocusWindow(NULL);
3240 }
3241 }
3242
3243 // With right mouse button we close popups without changing focus based on where the mouse is aimed
3244 // Instead, focus will be restored to the window under the bottom-most closed popup.
3245 // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
3246 if (g.IO.MouseClicked[1])
3247 {
3248 // Find the top-most window between HoveredWindow and the top-most Modal Window.
3249 // This is where we can trim the popup stack.
3250 ImGuiWindow* modal = GetTopMostPopupModal();
3251 bool hovered_window_above_modal = false;
3252 if (modal == NULL)
3253 hovered_window_above_modal = true;
3254 for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
3255 {
3256 ImGuiWindow* window = g.Windows[i];
3257 if (window == modal)
3258 break;
3259 if (window == g.HoveredWindow)
3260 hovered_window_above_modal = true;
3261 }
3262 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
3263 }
3264 }
3265
IsWindowActiveAndVisible(ImGuiWindow * window)3266 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3267 {
3268 return (window->Active) && (!window->Hidden);
3269 }
3270
UpdateMouseInputs()3271 static void ImGui::UpdateMouseInputs()
3272 {
3273 ImGuiContext& g = *GImGui;
3274
3275 // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3276 if (IsMousePosValid(&g.IO.MousePos))
3277 g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3278
3279 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3280 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3281 g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3282 else
3283 g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3284 if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3285 g.NavDisableMouseHover = false;
3286
3287 g.IO.MousePosPrev = g.IO.MousePos;
3288 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3289 {
3290 g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3291 g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3292 g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3293 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;
3294 g.IO.MouseDoubleClicked[i] = false;
3295 if (g.IO.MouseClicked[i])
3296 {
3297 if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3298 {
3299 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3300 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3301 g.IO.MouseDoubleClicked[i] = true;
3302 g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click
3303 }
3304 else
3305 {
3306 g.IO.MouseClickedTime[i] = g.Time;
3307 }
3308 g.IO.MouseClickedPos[i] = g.IO.MousePos;
3309 g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i];
3310 g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3311 g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3312 }
3313 else if (g.IO.MouseDown[i])
3314 {
3315 // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3316 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3317 g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3318 g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x);
3319 g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y);
3320 }
3321 if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i])
3322 g.IO.MouseDownWasDoubleClick[i] = false;
3323 if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3324 g.NavDisableMouseHover = false;
3325 }
3326 }
3327
StartLockWheelingWindow(ImGuiWindow * window)3328 static void StartLockWheelingWindow(ImGuiWindow* window)
3329 {
3330 ImGuiContext& g = *GImGui;
3331 if (g.WheelingWindow == window)
3332 return;
3333 g.WheelingWindow = window;
3334 g.WheelingWindowRefMousePos = g.IO.MousePos;
3335 g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER;
3336 }
3337
UpdateMouseWheel()3338 void ImGui::UpdateMouseWheel()
3339 {
3340 ImGuiContext& g = *GImGui;
3341
3342 // Reset the locked window if we move the mouse or after the timer elapses
3343 if (g.WheelingWindow != NULL)
3344 {
3345 g.WheelingWindowTimer -= g.IO.DeltaTime;
3346 if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
3347 g.WheelingWindowTimer = 0.0f;
3348 if (g.WheelingWindowTimer <= 0.0f)
3349 {
3350 g.WheelingWindow = NULL;
3351 g.WheelingWindowTimer = 0.0f;
3352 }
3353 }
3354
3355 if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3356 return;
3357
3358 ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
3359 if (!window || window->Collapsed)
3360 return;
3361
3362 // Zoom / Scale window
3363 // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
3364 if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3365 {
3366 StartLockWheelingWindow(window);
3367 const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3368 const float scale = new_font_scale / window->FontWindowScale;
3369 window->FontWindowScale = new_font_scale;
3370 if (!(window->Flags & ImGuiWindowFlags_ChildWindow))
3371 {
3372 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3373 SetWindowPos(window, window->Pos + offset, 0);
3374 window->Size = ImFloor(window->Size * scale);
3375 window->SizeFull = ImFloor(window->SizeFull * scale);
3376 }
3377 return;
3378 }
3379
3380 // Mouse wheel scrolling
3381 // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent
3382
3383 // Vertical Mouse Wheel scrolling
3384 const float wheel_y = (g.IO.MouseWheel != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
3385 if (wheel_y != 0.0f && !g.IO.KeyCtrl)
3386 {
3387 StartLockWheelingWindow(window);
3388 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3389 window = window->ParentWindow;
3390 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3391 {
3392 float max_step = window->InnerRect.GetHeight() * 0.67f;
3393 float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
3394 SetScrollY(window, window->Scroll.y - wheel_y * scroll_step);
3395 }
3396 }
3397
3398 // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held
3399 const float wheel_x = (g.IO.MouseWheelH != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheelH : (g.IO.MouseWheel != 0.0f && g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
3400 if (wheel_x != 0.0f && !g.IO.KeyCtrl)
3401 {
3402 StartLockWheelingWindow(window);
3403 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3404 window = window->ParentWindow;
3405 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3406 {
3407 float max_step = window->InnerRect.GetWidth() * 0.67f;
3408 float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
3409 SetScrollX(window, window->Scroll.x - wheel_x * scroll_step);
3410 }
3411 }
3412 }
3413
3414 // The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app)
UpdateHoveredWindowAndCaptureFlags()3415 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3416 {
3417 ImGuiContext& g = *GImGui;
3418
3419 // Find the window hovered by mouse:
3420 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3421 // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame.
3422 // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms.
3423 FindHoveredWindow();
3424
3425 // Modal windows prevents cursor from hovering behind them.
3426 ImGuiWindow* modal_window = GetTopMostPopupModal();
3427 if (modal_window)
3428 if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3429 g.HoveredRootWindow = g.HoveredWindow = NULL;
3430
3431 // Disabled mouse?
3432 if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3433 g.HoveredWindow = g.HoveredRootWindow = NULL;
3434
3435 // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward.
3436 int mouse_earliest_button_down = -1;
3437 bool mouse_any_down = false;
3438 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3439 {
3440 if (g.IO.MouseClicked[i])
3441 g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
3442 mouse_any_down |= g.IO.MouseDown[i];
3443 if (g.IO.MouseDown[i])
3444 if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3445 mouse_earliest_button_down = i;
3446 }
3447 const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3448
3449 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3450 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3451 const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3452 if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3453 g.HoveredWindow = g.HoveredRootWindow = NULL;
3454
3455 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app)
3456 if (g.WantCaptureMouseNextFrame != -1)
3457 g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3458 else
3459 g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
3460
3461 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
3462 if (g.WantCaptureKeyboardNextFrame != -1)
3463 g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3464 else
3465 g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3466 if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3467 g.IO.WantCaptureKeyboard = true;
3468
3469 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3470 g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3471 }
3472
NewFrameSanityChecks()3473 static void NewFrameSanityChecks()
3474 {
3475 ImGuiContext& g = *GImGui;
3476
3477 // Check user data
3478 // (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument)
3479 IM_ASSERT(g.Initialized);
3480 IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!");
3481 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
3482 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!");
3483 IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3484 IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3485 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!");
3486 IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)!");
3487 IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
3488 IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
3489 for (int n = 0; n < ImGuiKey_COUNT; n++)
3490 IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)");
3491
3492 // Perform simple check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only recently added in 1.60 WIP)
3493 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
3494 IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
3495
3496 // Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
3497 if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
3498 g.IO.ConfigWindowsResizeFromEdges = false;
3499 }
3500
NewFrame()3501 void ImGui::NewFrame()
3502 {
3503 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3504 ImGuiContext& g = *GImGui;
3505
3506 #ifdef IMGUI_ENABLE_TEST_ENGINE
3507 ImGuiTestEngineHook_PreNewFrame(&g);
3508 #endif
3509
3510 // Check and assert for various common IO and Configuration mistakes
3511 NewFrameSanityChecks();
3512
3513 // Load settings on first frame (if not explicitly loaded manually before)
3514 if (!g.SettingsLoaded)
3515 {
3516 IM_ASSERT(g.SettingsWindows.empty());
3517 if (g.IO.IniFilename)
3518 LoadIniSettingsFromDisk(g.IO.IniFilename);
3519 g.SettingsLoaded = true;
3520 }
3521
3522 // Save settings (with a delay after the last modification, so we don't spam disk too much)
3523 if (g.SettingsDirtyTimer > 0.0f)
3524 {
3525 g.SettingsDirtyTimer -= g.IO.DeltaTime;
3526 if (g.SettingsDirtyTimer <= 0.0f)
3527 {
3528 if (g.IO.IniFilename != NULL)
3529 SaveIniSettingsToDisk(g.IO.IniFilename);
3530 else
3531 g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
3532 g.SettingsDirtyTimer = 0.0f;
3533 }
3534 }
3535
3536 g.Time += g.IO.DeltaTime;
3537 g.FrameScopeActive = true;
3538 g.FrameCount += 1;
3539 g.TooltipOverrideCount = 0;
3540 g.WindowsActiveCount = 0;
3541
3542 // Setup current font and draw list shared data
3543 g.IO.Fonts->Locked = true;
3544 SetCurrentFont(GetDefaultFont());
3545 IM_ASSERT(g.Font->IsLoaded());
3546 g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3547 g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3548 g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
3549 if (g.Style.AntiAliasedLines)
3550 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
3551 if (g.Style.AntiAliasedFill)
3552 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
3553 if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
3554 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
3555
3556 g.BackgroundDrawList.Clear();
3557 g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3558 g.BackgroundDrawList.PushClipRectFullScreen();
3559
3560 g.ForegroundDrawList.Clear();
3561 g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3562 g.ForegroundDrawList.PushClipRectFullScreen();
3563
3564 // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
3565 g.DrawData.Clear();
3566
3567 // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3568 if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3569 KeepAliveID(g.DragDropPayload.SourceId);
3570
3571 // Clear reference to active widget if the widget isn't alive anymore
3572 if (!g.HoveredIdPreviousFrame)
3573 g.HoveredIdTimer = 0.0f;
3574 if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
3575 g.HoveredIdNotActiveTimer = 0.0f;
3576 if (g.HoveredId)
3577 g.HoveredIdTimer += g.IO.DeltaTime;
3578 if (g.HoveredId && g.ActiveId != g.HoveredId)
3579 g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
3580 g.HoveredIdPreviousFrame = g.HoveredId;
3581 g.HoveredId = 0;
3582 g.HoveredIdAllowOverlap = false;
3583 if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3584 ClearActiveID();
3585 if (g.ActiveId)
3586 g.ActiveIdTimer += g.IO.DeltaTime;
3587 g.LastActiveIdTimer += g.IO.DeltaTime;
3588 g.ActiveIdPreviousFrame = g.ActiveId;
3589 g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3590 g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
3591 g.ActiveIdIsAlive = 0;
3592 g.ActiveIdHasBeenEditedThisFrame = false;
3593 g.ActiveIdPreviousFrameIsAlive = false;
3594 g.ActiveIdIsJustActivated = false;
3595 if (g.TempInputTextId != 0 && g.ActiveId != g.TempInputTextId)
3596 g.TempInputTextId = 0;
3597 if (g.ActiveId == 0)
3598 {
3599 g.ActiveIdUsingNavDirMask = g.ActiveIdUsingNavInputMask = 0;
3600 g.ActiveIdUsingKeyInputMask = 0;
3601 }
3602
3603 // Drag and drop
3604 g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3605 g.DragDropAcceptIdCurr = 0;
3606 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3607 g.DragDropWithinSourceOrTarget = false;
3608
3609 // Update keyboard input state
3610 memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3611 for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3612 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;
3613
3614 // Update gamepad/keyboard directional navigation
3615 NavUpdate();
3616
3617 // Update mouse input state
3618 UpdateMouseInputs();
3619
3620 // Calculate frame-rate for the user, as a purely luxurious feature
3621 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3622 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3623 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3624 g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3625
3626 // Find hovered window
3627 // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
3628 UpdateHoveredWindowAndCaptureFlags();
3629
3630 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3631 UpdateMouseMovingWindowNewFrame();
3632
3633 // Background darkening/whitening
3634 if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3635 g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3636 else
3637 g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
3638
3639 g.MouseCursor = ImGuiMouseCursor_Arrow;
3640 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3641 g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3642
3643 // Mouse wheel scrolling, scale
3644 UpdateMouseWheel();
3645
3646 // Pressing TAB activate widget focus
3647 g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab));
3648 if (g.ActiveId == 0 && g.FocusTabPressed)
3649 {
3650 // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also
3651 // manipulate the Next fields even, even though they will be turned into Curr fields by the code below.
3652 g.FocusRequestNextWindow = g.NavWindow;
3653 g.FocusRequestNextCounterAll = INT_MAX;
3654 if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3655 g.FocusRequestNextCounterTab = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3656 else
3657 g.FocusRequestNextCounterTab = g.IO.KeyShift ? -1 : 0;
3658 }
3659
3660 // Turn queued focus request into current one
3661 g.FocusRequestCurrWindow = NULL;
3662 g.FocusRequestCurrCounterAll = g.FocusRequestCurrCounterTab = INT_MAX;
3663 if (g.FocusRequestNextWindow != NULL)
3664 {
3665 ImGuiWindow* window = g.FocusRequestNextWindow;
3666 g.FocusRequestCurrWindow = window;
3667 if (g.FocusRequestNextCounterAll != INT_MAX && window->DC.FocusCounterAll != -1)
3668 g.FocusRequestCurrCounterAll = ImModPositive(g.FocusRequestNextCounterAll, window->DC.FocusCounterAll + 1);
3669 if (g.FocusRequestNextCounterTab != INT_MAX && window->DC.FocusCounterTab != -1)
3670 g.FocusRequestCurrCounterTab = ImModPositive(g.FocusRequestNextCounterTab, window->DC.FocusCounterTab + 1);
3671 g.FocusRequestNextWindow = NULL;
3672 g.FocusRequestNextCounterAll = g.FocusRequestNextCounterTab = INT_MAX;
3673 }
3674
3675 g.NavIdTabCounter = INT_MAX;
3676
3677 // Mark all windows as not visible and compact unused memory.
3678 IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
3679 const float memory_compact_start_time = (g.IO.ConfigWindowsMemoryCompactTimer >= 0.0f) ? (float)g.Time - g.IO.ConfigWindowsMemoryCompactTimer : FLT_MAX;
3680 for (int i = 0; i != g.Windows.Size; i++)
3681 {
3682 ImGuiWindow* window = g.Windows[i];
3683 window->WasActive = window->Active;
3684 window->BeginCount = 0;
3685 window->Active = false;
3686 window->WriteAccessed = false;
3687
3688 // Garbage collect (this is totally functional but we may need decide if the side-effects are desirable)
3689 if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
3690 GcCompactTransientWindowBuffers(window);
3691 }
3692
3693 // Closing the focused window restore focus to the first active root window in descending z-order
3694 if (g.NavWindow && !g.NavWindow->WasActive)
3695 FocusTopMostWindowUnderOne(NULL, NULL);
3696
3697 // No window should be open at the beginning of the frame.
3698 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3699 g.CurrentWindowStack.resize(0);
3700 g.BeginPopupStack.resize(0);
3701 ClosePopupsOverWindow(g.NavWindow, false);
3702
3703 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
3704 UpdateDebugToolItemPicker();
3705
3706 // Create implicit/fallback window - which we will only render it if the user has added something to it.
3707 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3708 // This fallback is particularly important as it avoid ImGui:: calls from crashing.
3709 SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
3710 Begin("Debug##Default");
3711 g.FrameScopePushedImplicitWindow = true;
3712
3713 #ifdef IMGUI_ENABLE_TEST_ENGINE
3714 ImGuiTestEngineHook_PostNewFrame(&g);
3715 #endif
3716 }
3717
3718 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
UpdateDebugToolItemPicker()3719 void ImGui::UpdateDebugToolItemPicker()
3720 {
3721 ImGuiContext& g = *GImGui;
3722 g.DebugItemPickerBreakID = 0;
3723 if (g.DebugItemPickerActive)
3724 {
3725 const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
3726 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
3727 if (ImGui::IsKeyPressedMap(ImGuiKey_Escape))
3728 g.DebugItemPickerActive = false;
3729 if (ImGui::IsMouseClicked(0) && hovered_id)
3730 {
3731 g.DebugItemPickerBreakID = hovered_id;
3732 g.DebugItemPickerActive = false;
3733 }
3734 ImGui::SetNextWindowBgAlpha(0.60f);
3735 ImGui::BeginTooltip();
3736 ImGui::Text("HoveredId: 0x%08X", hovered_id);
3737 ImGui::Text("Press ESC to abort picking.");
3738 ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
3739 ImGui::EndTooltip();
3740 }
3741 }
3742
Initialize(ImGuiContext * context)3743 void ImGui::Initialize(ImGuiContext* context)
3744 {
3745 ImGuiContext& g = *context;
3746 IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3747
3748 // Add .ini handle for ImGuiWindow type
3749 ImGuiSettingsHandler ini_handler;
3750 ini_handler.TypeName = "Window";
3751 ini_handler.TypeHash = ImHashStr("Window");
3752 ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen;
3753 ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine;
3754 ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll;
3755 g.SettingsHandlers.push_back(ini_handler);
3756
3757 g.Initialized = true;
3758 }
3759
3760 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)3761 void ImGui::Shutdown(ImGuiContext* context)
3762 {
3763 // 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)
3764 ImGuiContext& g = *context;
3765 if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3766 {
3767 g.IO.Fonts->Locked = false;
3768 IM_DELETE(g.IO.Fonts);
3769 }
3770 g.IO.Fonts = NULL;
3771
3772 // Cleanup of other data are conditional on actually having initialized Dear ImGui.
3773 if (!g.Initialized)
3774 return;
3775
3776 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
3777 if (g.SettingsLoaded && g.IO.IniFilename != NULL)
3778 {
3779 ImGuiContext* backup_context = GImGui;
3780 SetCurrentContext(context);
3781 SaveIniSettingsToDisk(g.IO.IniFilename);
3782 SetCurrentContext(backup_context);
3783 }
3784
3785 // Clear everything else
3786 for (int i = 0; i < g.Windows.Size; i++)
3787 IM_DELETE(g.Windows[i]);
3788 g.Windows.clear();
3789 g.WindowsFocusOrder.clear();
3790 g.WindowsSortBuffer.clear();
3791 g.CurrentWindow = NULL;
3792 g.CurrentWindowStack.clear();
3793 g.WindowsById.Clear();
3794 g.NavWindow = NULL;
3795 g.HoveredWindow = g.HoveredRootWindow = NULL;
3796 g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
3797 g.MovingWindow = NULL;
3798 g.ColorModifiers.clear();
3799 g.StyleModifiers.clear();
3800 g.FontStack.clear();
3801 g.OpenPopupStack.clear();
3802 g.BeginPopupStack.clear();
3803 g.DrawDataBuilder.ClearFreeMemory();
3804 g.BackgroundDrawList.ClearFreeMemory();
3805 g.ForegroundDrawList.ClearFreeMemory();
3806
3807 g.TabBars.Clear();
3808 g.CurrentTabBarStack.clear();
3809 g.ShrinkWidthBuffer.clear();
3810
3811 g.PrivateClipboard.clear();
3812 g.InputTextState.ClearFreeMemory();
3813
3814 g.SettingsWindowsNames.clear();
3815 g.SettingsWindows.clear();
3816 g.SettingsHandlers.clear();
3817
3818 if (g.LogFile && g.LogFile != stdout)
3819 {
3820 fclose(g.LogFile);
3821 g.LogFile = NULL;
3822 }
3823 g.LogBuffer.clear();
3824
3825 g.Initialized = false;
3826 }
3827
3828 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)3829 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
3830 {
3831 const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
3832 const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
3833 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
3834 return d;
3835 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
3836 return d;
3837 return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
3838 }
3839
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)3840 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
3841 {
3842 out_sorted_windows->push_back(window);
3843 if (window->Active)
3844 {
3845 int count = window->DC.ChildWindows.Size;
3846 if (count > 1)
3847 ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
3848 for (int i = 0; i < count; i++)
3849 {
3850 ImGuiWindow* child = window->DC.ChildWindows[i];
3851 if (child->Active)
3852 AddWindowToSortBuffer(out_sorted_windows, child);
3853 }
3854 }
3855 }
3856
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)3857 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
3858 {
3859 if (draw_list->CmdBuffer.empty())
3860 return;
3861
3862 // Remove trailing command if unused
3863 ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
3864 if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
3865 {
3866 draw_list->CmdBuffer.pop_back();
3867 if (draw_list->CmdBuffer.empty())
3868 return;
3869 }
3870
3871 // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
3872 // May trigger for you if you are using PrimXXX functions incorrectly.
3873 IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
3874 IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
3875 if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
3876 IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
3877
3878 // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
3879 // If this assert triggers because you are drawing lots of stuff manually:
3880 // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
3881 // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics window to inspect draw list contents.
3882 // - If you want large meshes with more than 64K vertices, you can either:
3883 // (A) Handle the ImDrawCmd::VtxOffset value in your renderer back-end, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
3884 // Most example back-ends already support this from 1.71. Pre-1.71 back-ends won't.
3885 // Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them.
3886 // (B) Or handle 32-bits indices in your renderer back-end, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
3887 // Most example back-ends already support this. For example, the OpenGL example code detect index size at compile-time:
3888 // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
3889 // Your own engine or render API may use different parameters or function calls to specify index sizes.
3890 // 2 and 4 bytes indices are generally supported by most graphics API.
3891 // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
3892 // the 64K limit to split your draw commands in multiple draw lists.
3893 if (sizeof(ImDrawIdx) == 2)
3894 IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
3895
3896 out_list->push_back(draw_list);
3897 }
3898
AddWindowToDrawData(ImVector<ImDrawList * > * out_render_list,ImGuiWindow * window)3899 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
3900 {
3901 ImGuiContext& g = *GImGui;
3902 g.IO.MetricsRenderWindows++;
3903 AddDrawListToDrawData(out_render_list, window->DrawList);
3904 for (int i = 0; i < window->DC.ChildWindows.Size; i++)
3905 {
3906 ImGuiWindow* child = window->DC.ChildWindows[i];
3907 if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active
3908 AddWindowToDrawData(out_render_list, child);
3909 }
3910 }
3911
3912 // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
AddRootWindowToDrawData(ImGuiWindow * window)3913 static void AddRootWindowToDrawData(ImGuiWindow* window)
3914 {
3915 ImGuiContext& g = *GImGui;
3916 if (window->Flags & ImGuiWindowFlags_Tooltip)
3917 AddWindowToDrawData(&g.DrawDataBuilder.Layers[1], window);
3918 else
3919 AddWindowToDrawData(&g.DrawDataBuilder.Layers[0], window);
3920 }
3921
FlattenIntoSingleLayer()3922 void ImDrawDataBuilder::FlattenIntoSingleLayer()
3923 {
3924 int n = Layers[0].Size;
3925 int size = n;
3926 for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
3927 size += Layers[i].Size;
3928 Layers[0].resize(size);
3929 for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
3930 {
3931 ImVector<ImDrawList*>& layer = Layers[layer_n];
3932 if (layer.empty())
3933 continue;
3934 memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
3935 n += layer.Size;
3936 layer.resize(0);
3937 }
3938 }
3939
SetupDrawData(ImVector<ImDrawList * > * draw_lists,ImDrawData * draw_data)3940 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
3941 {
3942 ImGuiIO& io = ImGui::GetIO();
3943 draw_data->Valid = true;
3944 draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
3945 draw_data->CmdListsCount = draw_lists->Size;
3946 draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
3947 draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
3948 draw_data->DisplaySize = io.DisplaySize;
3949 draw_data->FramebufferScale = io.DisplayFramebufferScale;
3950 for (int n = 0; n < draw_lists->Size; n++)
3951 {
3952 draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
3953 draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
3954 }
3955 }
3956
3957 // When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result.
PushClipRect(const ImVec2 & clip_rect_min,const ImVec2 & clip_rect_max,bool intersect_with_current_clip_rect)3958 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
3959 {
3960 ImGuiWindow* window = GetCurrentWindow();
3961 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
3962 window->ClipRect = window->DrawList->_ClipRectStack.back();
3963 }
3964
PopClipRect()3965 void ImGui::PopClipRect()
3966 {
3967 ImGuiWindow* window = GetCurrentWindow();
3968 window->DrawList->PopClipRect();
3969 window->ClipRect = window->DrawList->_ClipRectStack.back();
3970 }
3971
3972 // 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()3973 void ImGui::EndFrame()
3974 {
3975 ImGuiContext& g = *GImGui;
3976 IM_ASSERT(g.Initialized);
3977 if (g.FrameCountEnded == g.FrameCount) // Don't process EndFrame() multiple times.
3978 return;
3979 IM_ASSERT(g.FrameScopeActive && "Forgot to call ImGui::NewFrame()?");
3980
3981 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
3982 if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f))
3983 {
3984 g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
3985 g.PlatformImeLastPos = g.PlatformImePos;
3986 }
3987
3988 // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
3989 // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
3990 if (g.CurrentWindowStack.Size != 1)
3991 {
3992 if (g.CurrentWindowStack.Size > 1)
3993 {
3994 IM_ASSERT(g.CurrentWindowStack.Size == 1 && "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
3995 while (g.CurrentWindowStack.Size > 1) // FIXME-ERRORHANDLING
3996 End();
3997 }
3998 else
3999 {
4000 IM_ASSERT(g.CurrentWindowStack.Size == 1 && "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
4001 }
4002 }
4003
4004 // Hide implicit/fallback "Debug" window if it hasn't been used
4005 g.FrameScopePushedImplicitWindow = false;
4006 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
4007 g.CurrentWindow->Active = false;
4008 End();
4009
4010 // Show CTRL+TAB list window
4011 if (g.NavWindowingTarget != NULL)
4012 NavUpdateWindowingOverlay();
4013
4014 // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
4015 if (g.DragDropActive)
4016 {
4017 bool is_delivered = g.DragDropPayload.Delivery;
4018 bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
4019 if (is_delivered || is_elapsed)
4020 ClearDragDrop();
4021 }
4022
4023 // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
4024 if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount)
4025 {
4026 g.DragDropWithinSourceOrTarget = true;
4027 SetTooltip("...");
4028 g.DragDropWithinSourceOrTarget = false;
4029 }
4030
4031 // End frame
4032 g.FrameScopeActive = false;
4033 g.FrameCountEnded = g.FrameCount;
4034
4035 // Initiate moving window + handle left-click and right-click focus
4036 UpdateMouseMovingWindowEndFrame();
4037
4038 // Sort the window list so that all child windows are after their parent
4039 // We cannot do that on FocusWindow() because childs may not exist yet
4040 g.WindowsSortBuffer.resize(0);
4041 g.WindowsSortBuffer.reserve(g.Windows.Size);
4042 for (int i = 0; i != g.Windows.Size; i++)
4043 {
4044 ImGuiWindow* window = g.Windows[i];
4045 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
4046 continue;
4047 AddWindowToSortBuffer(&g.WindowsSortBuffer, window);
4048 }
4049
4050 // This usually assert if there is a mismatch between the ImGuiWindowFlags_ChildWindow / ParentWindow values and DC.ChildWindows[] in parents, aka we've done something wrong.
4051 IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size);
4052 g.Windows.swap(g.WindowsSortBuffer);
4053 g.IO.MetricsActiveWindows = g.WindowsActiveCount;
4054
4055 // Unlock font atlas
4056 g.IO.Fonts->Locked = false;
4057
4058 // Clear Input data for next frame
4059 g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
4060 g.IO.InputQueueCharacters.resize(0);
4061 memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
4062 }
4063
Render()4064 void ImGui::Render()
4065 {
4066 ImGuiContext& g = *GImGui;
4067 IM_ASSERT(g.Initialized);
4068
4069 if (g.FrameCountEnded != g.FrameCount)
4070 EndFrame();
4071 g.FrameCountRendered = g.FrameCount;
4072
4073 // Gather ImDrawList to render (for each active window)
4074 g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsRenderWindows = 0;
4075 g.DrawDataBuilder.Clear();
4076 if (!g.BackgroundDrawList.VtxBuffer.empty())
4077 AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.BackgroundDrawList);
4078
4079 ImGuiWindow* windows_to_render_top_most[2];
4080 windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
4081 windows_to_render_top_most[1] = g.NavWindowingTarget ? g.NavWindowingList : NULL;
4082 for (int n = 0; n != g.Windows.Size; n++)
4083 {
4084 ImGuiWindow* window = g.Windows[n];
4085 if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
4086 AddRootWindowToDrawData(window);
4087 }
4088 for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
4089 if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
4090 AddRootWindowToDrawData(windows_to_render_top_most[n]);
4091 g.DrawDataBuilder.FlattenIntoSingleLayer();
4092
4093 // Draw software mouse cursor if requested
4094 if (g.IO.MouseDrawCursor)
4095 RenderMouseCursor(&g.ForegroundDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
4096
4097 if (!g.ForegroundDrawList.VtxBuffer.empty())
4098 AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.ForegroundDrawList);
4099
4100 // Setup ImDrawData structure for end-user
4101 SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
4102 g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
4103 g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
4104
4105 // (Legacy) Call the Render callback function. The current prefer way is to let the user retrieve GetDrawData() and call the render function themselves.
4106 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4107 if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
4108 g.IO.RenderDrawListsFn(&g.DrawData);
4109 #endif
4110 }
4111
4112 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4113 // CalcTextSize("") should return ImVec2(0.0f, g.FontSize)
CalcTextSize(const char * text,const char * text_end,bool hide_text_after_double_hash,float wrap_width)4114 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
4115 {
4116 ImGuiContext& g = *GImGui;
4117
4118 const char* text_display_end;
4119 if (hide_text_after_double_hash)
4120 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
4121 else
4122 text_display_end = text_end;
4123
4124 ImFont* font = g.Font;
4125 const float font_size = g.FontSize;
4126 if (text == text_display_end)
4127 return ImVec2(0.0f, font_size);
4128 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
4129
4130 // Round
4131 text_size.x = IM_FLOOR(text_size.x + 0.95f);
4132
4133 return text_size;
4134 }
4135
4136 // Find window given position, search front-to-back
4137 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically
4138 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
4139 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()4140 static void FindHoveredWindow()
4141 {
4142 ImGuiContext& g = *GImGui;
4143
4144 ImGuiWindow* hovered_window = NULL;
4145 if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
4146 hovered_window = g.MovingWindow;
4147
4148 ImVec2 padding_regular = g.Style.TouchExtraPadding;
4149 ImVec2 padding_for_resize_from_edges = g.IO.ConfigWindowsResizeFromEdges ? ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS)) : padding_regular;
4150 for (int i = g.Windows.Size - 1; i >= 0; i--)
4151 {
4152 ImGuiWindow* window = g.Windows[i];
4153 if (!window->Active || window->Hidden)
4154 continue;
4155 if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
4156 continue;
4157
4158 // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4159 ImRect bb(window->OuterRectClipped);
4160 if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
4161 bb.Expand(padding_regular);
4162 else
4163 bb.Expand(padding_for_resize_from_edges);
4164 if (!bb.Contains(g.IO.MousePos))
4165 continue;
4166
4167 // Those seemingly unnecessary extra tests are because the code here is a little different in viewport/docking branches.
4168 if (hovered_window == NULL)
4169 hovered_window = window;
4170 if (hovered_window)
4171 break;
4172 }
4173
4174 g.HoveredWindow = hovered_window;
4175 g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
4176
4177 }
4178
4179 // Test if mouse cursor is hovering given rectangle
4180 // NB- Rectangle is clipped by our current clip setting
4181 // 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)4182 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4183 {
4184 ImGuiContext& g = *GImGui;
4185
4186 // Clip
4187 ImRect rect_clipped(r_min, r_max);
4188 if (clip)
4189 rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
4190
4191 // Expand for touch input
4192 const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4193 if (!rect_for_touch.Contains(g.IO.MousePos))
4194 return false;
4195 return true;
4196 }
4197
GetKeyIndex(ImGuiKey imgui_key)4198 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4199 {
4200 IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4201 ImGuiContext& g = *GImGui;
4202 return g.IO.KeyMap[imgui_key];
4203 }
4204
4205 // Note that imgui doesn't know the semantic of each entry of io.KeysDown[]. Use your own indices/enums according to how your back-end/engine stored them into io.KeysDown[]!
IsKeyDown(int user_key_index)4206 bool ImGui::IsKeyDown(int user_key_index)
4207 {
4208 if (user_key_index < 0)
4209 return false;
4210 ImGuiContext& g = *GImGui;
4211 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4212 return g.IO.KeysDown[user_key_index];
4213 }
4214
4215 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
4216 // t1 = current time (e.g.: g.Time)
4217 // An event is triggered at:
4218 // t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N
CalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate)4219 int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
4220 {
4221 if (t1 == 0.0f)
4222 return 1;
4223 if (t0 >= t1)
4224 return 0;
4225 if (repeat_rate <= 0.0f)
4226 return (t0 < repeat_delay) && (t1 >= repeat_delay);
4227 const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
4228 const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
4229 const int count = count_t1 - count_t0;
4230 return count;
4231 }
4232
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)4233 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4234 {
4235 ImGuiContext& g = *GImGui;
4236 if (key_index < 0)
4237 return 0;
4238 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4239 const float t = g.IO.KeysDownDuration[key_index];
4240 return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
4241 }
4242
IsKeyPressed(int user_key_index,bool repeat)4243 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4244 {
4245 ImGuiContext& g = *GImGui;
4246 if (user_key_index < 0)
4247 return false;
4248 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4249 const float t = g.IO.KeysDownDuration[user_key_index];
4250 if (t == 0.0f)
4251 return true;
4252 if (repeat && t > g.IO.KeyRepeatDelay)
4253 return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4254 return false;
4255 }
4256
IsKeyReleased(int user_key_index)4257 bool ImGui::IsKeyReleased(int user_key_index)
4258 {
4259 ImGuiContext& g = *GImGui;
4260 if (user_key_index < 0) return false;
4261 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4262 return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4263 }
4264
IsMouseDown(int button)4265 bool ImGui::IsMouseDown(int button)
4266 {
4267 ImGuiContext& g = *GImGui;
4268 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4269 return g.IO.MouseDown[button];
4270 }
4271
IsAnyMouseDown()4272 bool ImGui::IsAnyMouseDown()
4273 {
4274 ImGuiContext& g = *GImGui;
4275 for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4276 if (g.IO.MouseDown[n])
4277 return true;
4278 return false;
4279 }
4280
IsMouseClicked(int button,bool repeat)4281 bool ImGui::IsMouseClicked(int button, bool repeat)
4282 {
4283 ImGuiContext& g = *GImGui;
4284 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4285 const float t = g.IO.MouseDownDuration[button];
4286 if (t == 0.0f)
4287 return true;
4288
4289 if (repeat && t > g.IO.KeyRepeatDelay)
4290 {
4291 // FIXME: 2019/05/03: Our old repeat code was wrong here and led to doubling the repeat rate, which made it an ok rate for repeat on mouse hold.
4292 int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
4293 if (amount > 0)
4294 return true;
4295 }
4296
4297 return false;
4298 }
4299
IsMouseReleased(int button)4300 bool ImGui::IsMouseReleased(int button)
4301 {
4302 ImGuiContext& g = *GImGui;
4303 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4304 return g.IO.MouseReleased[button];
4305 }
4306
IsMouseDoubleClicked(int button)4307 bool ImGui::IsMouseDoubleClicked(int button)
4308 {
4309 ImGuiContext& g = *GImGui;
4310 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4311 return g.IO.MouseDoubleClicked[button];
4312 }
4313
4314 // [Internal] This doesn't test if the button is pressed
IsMouseDragPastThreshold(int button,float lock_threshold)4315 bool ImGui::IsMouseDragPastThreshold(int button, float lock_threshold)
4316 {
4317 ImGuiContext& g = *GImGui;
4318 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4319 if (lock_threshold < 0.0f)
4320 lock_threshold = g.IO.MouseDragThreshold;
4321 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4322 }
4323
IsMouseDragging(int button,float lock_threshold)4324 bool ImGui::IsMouseDragging(int button, float lock_threshold)
4325 {
4326 ImGuiContext& g = *GImGui;
4327 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4328 if (!g.IO.MouseDown[button])
4329 return false;
4330 return IsMouseDragPastThreshold(button, lock_threshold);
4331 }
4332
GetMousePos()4333 ImVec2 ImGui::GetMousePos()
4334 {
4335 return GImGui->IO.MousePos;
4336 }
4337
4338 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4339 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4340 {
4341 ImGuiContext& g = *GImGui;
4342 if (g.BeginPopupStack.Size > 0)
4343 return g.OpenPopupStack[g.BeginPopupStack.Size-1].OpenMousePos;
4344 return g.IO.MousePos;
4345 }
4346
4347 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
IsMousePosValid(const ImVec2 * mouse_pos)4348 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4349 {
4350 // The assert is only to silence a false-positive in XCode Static Analysis.
4351 // Because GImGui is not dereferenced in every code path, the static analyzer assume that it may be NULL (which it doesn't for other functions).
4352 IM_ASSERT(GImGui != NULL);
4353 const float MOUSE_INVALID = -256000.0f;
4354 ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
4355 return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
4356 }
4357
4358 // Return the delta from the initial clicking position while the mouse button is clicked or was just released.
4359 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4360 // NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window.
GetMouseDragDelta(int button,float lock_threshold)4361 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
4362 {
4363 ImGuiContext& g = *GImGui;
4364 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4365 if (lock_threshold < 0.0f)
4366 lock_threshold = g.IO.MouseDragThreshold;
4367 if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
4368 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4369 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
4370 return g.IO.MousePos - g.IO.MouseClickedPos[button];
4371 return ImVec2(0.0f, 0.0f);
4372 }
4373
ResetMouseDragDelta(int button)4374 void ImGui::ResetMouseDragDelta(int button)
4375 {
4376 ImGuiContext& g = *GImGui;
4377 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4378 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4379 g.IO.MouseClickedPos[button] = g.IO.MousePos;
4380 }
4381
GetMouseCursor()4382 ImGuiMouseCursor ImGui::GetMouseCursor()
4383 {
4384 return GImGui->MouseCursor;
4385 }
4386
SetMouseCursor(ImGuiMouseCursor cursor_type)4387 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4388 {
4389 GImGui->MouseCursor = cursor_type;
4390 }
4391
CaptureKeyboardFromApp(bool capture)4392 void ImGui::CaptureKeyboardFromApp(bool capture)
4393 {
4394 GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4395 }
4396
CaptureMouseFromApp(bool capture)4397 void ImGui::CaptureMouseFromApp(bool capture)
4398 {
4399 GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4400 }
4401
IsItemActive()4402 bool ImGui::IsItemActive()
4403 {
4404 ImGuiContext& g = *GImGui;
4405 if (g.ActiveId)
4406 {
4407 ImGuiWindow* window = g.CurrentWindow;
4408 return g.ActiveId == window->DC.LastItemId;
4409 }
4410 return false;
4411 }
4412
IsItemActivated()4413 bool ImGui::IsItemActivated()
4414 {
4415 ImGuiContext& g = *GImGui;
4416 if (g.ActiveId)
4417 {
4418 ImGuiWindow* window = g.CurrentWindow;
4419 if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId)
4420 return true;
4421 }
4422 return false;
4423 }
4424
IsItemDeactivated()4425 bool ImGui::IsItemDeactivated()
4426 {
4427 ImGuiContext& g = *GImGui;
4428 ImGuiWindow* window = g.CurrentWindow;
4429 if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated)
4430 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
4431 return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
4432 }
4433
IsItemDeactivatedAfterEdit()4434 bool ImGui::IsItemDeactivatedAfterEdit()
4435 {
4436 ImGuiContext& g = *GImGui;
4437 return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
4438 }
4439
IsItemFocused()4440 bool ImGui::IsItemFocused()
4441 {
4442 ImGuiContext& g = *GImGui;
4443 ImGuiWindow* window = g.CurrentWindow;
4444
4445 if (g.NavId == 0 || g.NavDisableHighlight || g.NavId != window->DC.LastItemId)
4446 return false;
4447 return true;
4448 }
4449
IsItemClicked(int mouse_button)4450 bool ImGui::IsItemClicked(int mouse_button)
4451 {
4452 return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4453 }
4454
IsItemToggledSelection()4455 bool ImGui::IsItemToggledSelection()
4456 {
4457 ImGuiContext& g = *GImGui;
4458 return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
4459 }
4460
IsAnyItemHovered()4461 bool ImGui::IsAnyItemHovered()
4462 {
4463 ImGuiContext& g = *GImGui;
4464 return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4465 }
4466
IsAnyItemActive()4467 bool ImGui::IsAnyItemActive()
4468 {
4469 ImGuiContext& g = *GImGui;
4470 return g.ActiveId != 0;
4471 }
4472
IsAnyItemFocused()4473 bool ImGui::IsAnyItemFocused()
4474 {
4475 ImGuiContext& g = *GImGui;
4476 return g.NavId != 0 && !g.NavDisableHighlight;
4477 }
4478
IsItemVisible()4479 bool ImGui::IsItemVisible()
4480 {
4481 ImGuiWindow* window = GetCurrentWindowRead();
4482 return window->ClipRect.Overlaps(window->DC.LastItemRect);
4483 }
4484
IsItemEdited()4485 bool ImGui::IsItemEdited()
4486 {
4487 ImGuiWindow* window = GetCurrentWindowRead();
4488 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4489 }
4490
4491 // 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()4492 void ImGui::SetItemAllowOverlap()
4493 {
4494 ImGuiContext& g = *GImGui;
4495 if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
4496 g.HoveredIdAllowOverlap = true;
4497 if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
4498 g.ActiveIdAllowOverlap = true;
4499 }
4500
GetItemRectMin()4501 ImVec2 ImGui::GetItemRectMin()
4502 {
4503 ImGuiWindow* window = GetCurrentWindowRead();
4504 return window->DC.LastItemRect.Min;
4505 }
4506
GetItemRectMax()4507 ImVec2 ImGui::GetItemRectMax()
4508 {
4509 ImGuiWindow* window = GetCurrentWindowRead();
4510 return window->DC.LastItemRect.Max;
4511 }
4512
GetItemRectSize()4513 ImVec2 ImGui::GetItemRectSize()
4514 {
4515 ImGuiWindow* window = GetCurrentWindowRead();
4516 return window->DC.LastItemRect.GetSize();
4517 }
4518
GetViewportRect()4519 static ImRect GetViewportRect()
4520 {
4521 ImGuiContext& g = *GImGui;
4522 return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4523 }
4524
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)4525 static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4526 {
4527 ImGuiContext& g = *GImGui;
4528 ImGuiWindow* parent_window = g.CurrentWindow;
4529
4530 flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
4531 flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
4532
4533 // Size
4534 const ImVec2 content_avail = GetContentRegionAvail();
4535 ImVec2 size = ImFloor(size_arg);
4536 const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
4537 if (size.x <= 0.0f)
4538 size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
4539 if (size.y <= 0.0f)
4540 size.y = ImMax(content_avail.y + size.y, 4.0f);
4541 SetNextWindowSize(size);
4542
4543 // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value.
4544 char title[256];
4545 if (name)
4546 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
4547 else
4548 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
4549
4550 const float backup_border_size = g.Style.ChildBorderSize;
4551 if (!border)
4552 g.Style.ChildBorderSize = 0.0f;
4553 bool ret = Begin(title, NULL, flags);
4554 g.Style.ChildBorderSize = backup_border_size;
4555
4556 ImGuiWindow* child_window = g.CurrentWindow;
4557 child_window->ChildId = id;
4558 child_window->AutoFitChildAxises = (ImS8)auto_fit_axises;
4559
4560 // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
4561 // While this is not really documented/defined, it seems that the expected thing to do.
4562 if (child_window->BeginCount == 1)
4563 parent_window->DC.CursorPos = child_window->Pos;
4564
4565 // Process navigation-in immediately so NavInit can run on first frame
4566 if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
4567 {
4568 FocusWindow(child_window);
4569 NavInitWindow(child_window, false);
4570 SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
4571 g.ActiveIdSource = ImGuiInputSource_Nav;
4572 }
4573 return ret;
4574 }
4575
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4576 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4577 {
4578 ImGuiWindow* window = GetCurrentWindow();
4579 return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
4580 }
4581
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4582 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4583 {
4584 IM_ASSERT(id != 0);
4585 return BeginChildEx(NULL, id, size_arg, border, extra_flags);
4586 }
4587
EndChild()4588 void ImGui::EndChild()
4589 {
4590 ImGuiContext& g = *GImGui;
4591 ImGuiWindow* window = g.CurrentWindow;
4592
4593 IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() callss
4594 if (window->BeginCount > 1)
4595 {
4596 End();
4597 }
4598 else
4599 {
4600 ImVec2 sz = window->Size;
4601 if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
4602 sz.x = ImMax(4.0f, sz.x);
4603 if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
4604 sz.y = ImMax(4.0f, sz.y);
4605 End();
4606
4607 ImGuiWindow* parent_window = g.CurrentWindow;
4608 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
4609 ItemSize(sz);
4610 if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
4611 {
4612 ItemAdd(bb, window->ChildId);
4613 RenderNavHighlight(bb, window->ChildId);
4614
4615 // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
4616 if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
4617 RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
4618 }
4619 else
4620 {
4621 // Not navigable into
4622 ItemAdd(bb, 0);
4623 }
4624 }
4625 }
4626
4627 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)4628 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
4629 {
4630 ImGuiContext& g = *GImGui;
4631 const ImGuiStyle& style = g.Style;
4632 PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4633 PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4634 PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4635 PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
4636 bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
4637 PopStyleVar(3);
4638 PopStyleColor();
4639 return ret;
4640 }
4641
EndChildFrame()4642 void ImGui::EndChildFrame()
4643 {
4644 EndChild();
4645 }
4646
4647 // Save and compare stack sizes on Begin()/End() to detect usage errors
CheckStacksSize(ImGuiWindow * window,bool write)4648 static void CheckStacksSize(ImGuiWindow* window, bool write)
4649 {
4650 // 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)
4651 ImGuiContext& g = *GImGui;
4652 short* p_backup = &window->DC.StackSizesBackup[0];
4653 { int current = window->IDStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!"); p_backup++; } // Too few or too many PopID()/TreePop()
4654 { int current = window->DC.GroupStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!"); p_backup++; } // Too few or too many EndGroup()
4655 { int current = g.BeginPopupStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup()
4656 // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them.
4657 { int current = g.ColorModifiers.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushStyleColor/PopStyleColor Mismatch!"); p_backup++; } // Too few or too many PopStyleColor()
4658 { int current = g.StyleModifiers.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushStyleVar/PopStyleVar Mismatch!"); p_backup++; } // Too few or too many PopStyleVar()
4659 { int current = g.FontStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushFont/PopFont Mismatch!"); p_backup++; } // Too few or too many PopFont()
4660 IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
4661 }
4662
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)4663 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
4664 {
4665 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
4666 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
4667 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
4668 }
4669
FindWindowByID(ImGuiID id)4670 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
4671 {
4672 ImGuiContext& g = *GImGui;
4673 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
4674 }
4675
FindWindowByName(const char * name)4676 ImGuiWindow* ImGui::FindWindowByName(const char* name)
4677 {
4678 ImGuiID id = ImHashStr(name);
4679 return FindWindowByID(id);
4680 }
4681
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)4682 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
4683 {
4684 ImGuiContext& g = *GImGui;
4685 //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
4686
4687 // Create window the first time
4688 ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
4689 window->Flags = flags;
4690 g.WindowsById.SetVoidPtr(window->ID, window);
4691
4692 // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
4693 window->Pos = ImVec2(60, 60);
4694
4695 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
4696 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4697 if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
4698 {
4699 // Retrieve settings from .ini file
4700 window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings);
4701 SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
4702 window->Pos = ImVec2(settings->Pos.x, settings->Pos.y);
4703 window->Collapsed = settings->Collapsed;
4704 if (settings->Size.x > 0 && settings->Size.y > 0)
4705 size = ImVec2(settings->Size.x, settings->Size.y);
4706 }
4707 window->Size = window->SizeFull = ImFloor(size);
4708 window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values
4709
4710 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4711 {
4712 window->AutoFitFramesX = window->AutoFitFramesY = 2;
4713 window->AutoFitOnlyGrows = false;
4714 }
4715 else
4716 {
4717 if (window->Size.x <= 0.0f)
4718 window->AutoFitFramesX = 2;
4719 if (window->Size.y <= 0.0f)
4720 window->AutoFitFramesY = 2;
4721 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
4722 }
4723
4724 g.WindowsFocusOrder.push_back(window);
4725 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
4726 g.Windows.push_front(window); // Quite slow but rare and only once
4727 else
4728 g.Windows.push_back(window);
4729 return window;
4730 }
4731
CalcWindowSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)4732 static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
4733 {
4734 ImGuiContext& g = *GImGui;
4735 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
4736 {
4737 // Using -1,-1 on either X/Y axis to preserve the current size.
4738 ImRect cr = g.NextWindowData.SizeConstraintRect;
4739 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
4740 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
4741 if (g.NextWindowData.SizeCallback)
4742 {
4743 ImGuiSizeCallbackData data;
4744 data.UserData = g.NextWindowData.SizeCallbackUserData;
4745 data.Pos = window->Pos;
4746 data.CurrentSize = window->SizeFull;
4747 data.DesiredSize = new_size;
4748 g.NextWindowData.SizeCallback(&data);
4749 new_size = data.DesiredSize;
4750 }
4751 new_size.x = IM_FLOOR(new_size.x);
4752 new_size.y = IM_FLOOR(new_size.y);
4753 }
4754
4755 // Minimum size
4756 if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
4757 {
4758 new_size = ImMax(new_size, g.Style.WindowMinSize);
4759 new_size.y = ImMax(new_size.y, window->TitleBarHeight() + window->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
4760 }
4761 return new_size;
4762 }
4763
CalcWindowContentSize(ImGuiWindow * window)4764 static ImVec2 CalcWindowContentSize(ImGuiWindow* window)
4765 {
4766 if (window->Collapsed)
4767 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
4768 return window->ContentSize;
4769 if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
4770 return window->ContentSize;
4771
4772 ImVec2 sz;
4773 sz.x = IM_FLOOR((window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
4774 sz.y = IM_FLOOR((window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
4775 return sz;
4776 }
4777
CalcWindowAutoFitSize(ImGuiWindow * window,const ImVec2 & size_contents)4778 static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
4779 {
4780 ImGuiContext& g = *GImGui;
4781 ImGuiStyle& style = g.Style;
4782 ImVec2 size_decorations = ImVec2(0.0f, window->TitleBarHeight() + window->MenuBarHeight());
4783 ImVec2 size_pad = window->WindowPadding * 2.0f;
4784 ImVec2 size_desired = size_contents + size_pad + size_decorations;
4785 if (window->Flags & ImGuiWindowFlags_Tooltip)
4786 {
4787 // Tooltip always resize
4788 return size_desired;
4789 }
4790 else
4791 {
4792 // Maximum window size is determined by the viewport size or monitor size
4793 const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
4794 const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
4795 ImVec2 size_min = style.WindowMinSize;
4796 if (is_popup || is_menu) // Popups and menus bypass style.WindowMinSize by default, but we give then a non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)
4797 size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
4798 ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
4799
4800 // When the window cannot fit all contents (either because of constraints, either because screen is too small),
4801 // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
4802 ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
4803 bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - size_decorations.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);
4804 bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - size_decorations.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar);
4805 if (will_have_scrollbar_x)
4806 size_auto_fit.y += style.ScrollbarSize;
4807 if (will_have_scrollbar_y)
4808 size_auto_fit.x += style.ScrollbarSize;
4809 return size_auto_fit;
4810 }
4811 }
4812
CalcWindowExpectedSize(ImGuiWindow * window)4813 ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window)
4814 {
4815 ImVec2 size_contents = CalcWindowContentSize(window);
4816 ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents);
4817 ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
4818 return size_final;
4819 }
4820
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)4821 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
4822 {
4823 if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
4824 return ImGuiCol_PopupBg;
4825 if (flags & ImGuiWindowFlags_ChildWindow)
4826 return ImGuiCol_ChildBg;
4827 return ImGuiCol_WindowBg;
4828 }
4829
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)4830 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
4831 {
4832 ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left
4833 ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
4834 ImVec2 size_expected = pos_max - pos_min;
4835 ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
4836 *out_pos = pos_min;
4837 if (corner_norm.x == 0.0f)
4838 out_pos->x -= (size_constrained.x - size_expected.x);
4839 if (corner_norm.y == 0.0f)
4840 out_pos->y -= (size_constrained.y - size_expected.y);
4841 *out_size = size_constrained;
4842 }
4843
4844 struct ImGuiResizeGripDef
4845 {
4846 ImVec2 CornerPosN;
4847 ImVec2 InnerDir;
4848 int AngleMin12, AngleMax12;
4849 };
4850
4851 static const ImGuiResizeGripDef resize_grip_def[4] =
4852 {
4853 { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right
4854 { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left
4855 { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left
4856 { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right
4857 };
4858
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)4859 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
4860 {
4861 ImRect rect = window->Rect();
4862 if (thickness == 0.0f) rect.Max -= ImVec2(1,1);
4863 if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); // Top
4864 if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); // Right
4865 if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); // Bottom
4866 if (border_n == 3) return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); // Left
4867 IM_ASSERT(0);
4868 return ImRect();
4869 }
4870
4871 // Handle resize for: Resize Grips, Borders, Gamepad
4872 // Return true when using auto-fit (double click on resize grip)
UpdateManualResize(ImGuiWindow * window,const ImVec2 & size_auto_fit,int * border_held,int resize_grip_count,ImU32 resize_grip_col[4])4873 static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4])
4874 {
4875 ImGuiContext& g = *GImGui;
4876 ImGuiWindowFlags flags = window->Flags;
4877
4878 if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4879 return false;
4880 if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
4881 return false;
4882
4883 bool ret_auto_fit = false;
4884 const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
4885 const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
4886 const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f);
4887 const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f;
4888
4889 ImVec2 pos_target(FLT_MAX, FLT_MAX);
4890 ImVec2 size_target(FLT_MAX, FLT_MAX);
4891
4892 // Resize grips and borders are on layer 1
4893 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
4894 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
4895
4896 // Manual resize grips
4897 PushID("#RESIZE");
4898 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4899 {
4900 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4901 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
4902
4903 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
4904 ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size);
4905 if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
4906 if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
4907 bool hovered, held;
4908 ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
4909 //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
4910 if (hovered || held)
4911 g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
4912
4913 if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
4914 {
4915 // Manual auto-fit when double-clicking
4916 size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
4917 ret_auto_fit = true;
4918 ClearActiveID();
4919 }
4920 else if (held)
4921 {
4922 // Resize from any of the four corners
4923 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
4924 ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPosN); // Corner of the window corresponding to our corner grip
4925 CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target);
4926 }
4927 if (resize_grip_n == 0 || held || hovered)
4928 resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
4929 }
4930 for (int border_n = 0; border_n < resize_border_count; border_n++)
4931 {
4932 bool hovered, held;
4933 ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS);
4934 ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
4935 //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
4936 if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
4937 {
4938 g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
4939 if (held)
4940 *border_held = border_n;
4941 }
4942 if (held)
4943 {
4944 ImVec2 border_target = window->Pos;
4945 ImVec2 border_posn;
4946 if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Top
4947 if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Right
4948 if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Bottom
4949 if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Left
4950 CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
4951 }
4952 }
4953 PopID();
4954
4955 // Navigation resize (keyboard/gamepad)
4956 if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
4957 {
4958 ImVec2 nav_resize_delta;
4959 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
4960 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
4961 if (g.NavInputSource == ImGuiInputSource_NavGamepad)
4962 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
4963 if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
4964 {
4965 const float NAV_RESIZE_SPEED = 600.0f;
4966 nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
4967 g.NavWindowingToggleLayer = false;
4968 g.NavDisableMouseHover = true;
4969 resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
4970 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
4971 size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
4972 }
4973 }
4974
4975 // Apply back modified position/size to window
4976 if (size_target.x != FLT_MAX)
4977 {
4978 window->SizeFull = size_target;
4979 MarkIniSettingsDirty(window);
4980 }
4981 if (pos_target.x != FLT_MAX)
4982 {
4983 window->Pos = ImFloor(pos_target);
4984 MarkIniSettingsDirty(window);
4985 }
4986
4987 // Resize nav layer
4988 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
4989 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
4990
4991 window->Size = window->SizeFull;
4992 return ret_auto_fit;
4993 }
4994
ClampWindowRect(ImGuiWindow * window,const ImRect & rect,const ImVec2 & padding)4995 static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& rect, const ImVec2& padding)
4996 {
4997 ImGuiContext& g = *GImGui;
4998 ImVec2 size_for_clamping = (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) ? ImVec2(window->Size.x, window->TitleBarHeight()) : window->Size;
4999 window->Pos = ImMin(rect.Max - padding, ImMax(window->Pos + size_for_clamping, rect.Min + padding) - size_for_clamping);
5000 }
5001
RenderWindowOuterBorders(ImGuiWindow * window)5002 static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
5003 {
5004 ImGuiContext& g = *GImGui;
5005 float rounding = window->WindowRounding;
5006 float border_size = window->WindowBorderSize;
5007 if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
5008 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
5009
5010 int border_held = window->ResizeBorderHeld;
5011 if (border_held != -1)
5012 {
5013 struct ImGuiResizeBorderDef
5014 {
5015 ImVec2 InnerDir;
5016 ImVec2 CornerPosN1, CornerPosN2;
5017 float OuterAngle;
5018 };
5019 static const ImGuiResizeBorderDef resize_border_def[4] =
5020 {
5021 { ImVec2(0,+1), ImVec2(0,0), ImVec2(1,0), IM_PI*1.50f }, // Top
5022 { ImVec2(-1,0), ImVec2(1,0), ImVec2(1,1), IM_PI*0.00f }, // Right
5023 { ImVec2(0,-1), ImVec2(1,1), ImVec2(0,1), IM_PI*0.50f }, // Bottom
5024 { ImVec2(+1,0), ImVec2(0,1), ImVec2(0,0), IM_PI*1.00f } // Left
5025 };
5026 const ImGuiResizeBorderDef& def = resize_border_def[border_held];
5027 ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
5028 window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI*0.25f, def.OuterAngle);
5029 window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI*0.25f);
5030 window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual
5031 }
5032 if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5033 {
5034 float y = window->Pos.y + window->TitleBarHeight() - 1;
5035 window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), GetColorU32(ImGuiCol_Border), g.Style.FrameBorderSize);
5036 }
5037 }
5038
RenderWindowDecorations(ImGuiWindow * window,const ImRect & title_bar_rect,bool title_bar_is_highlight,int resize_grip_count,const ImU32 resize_grip_col[4],float resize_grip_draw_size)5039 void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size)
5040 {
5041 ImGuiContext& g = *GImGui;
5042 ImGuiStyle& style = g.Style;
5043 ImGuiWindowFlags flags = window->Flags;
5044
5045 // Draw window + handle manual resize
5046 // As we highlight the title bar when want_focus is set, multiple reappearing windows will have have their title bar highlighted on their reappearing frame.
5047 const float window_rounding = window->WindowRounding;
5048 const float window_border_size = window->WindowBorderSize;
5049 if (window->Collapsed)
5050 {
5051 // Title bar only
5052 float backup_border_size = style.FrameBorderSize;
5053 g.Style.FrameBorderSize = window->WindowBorderSize;
5054 ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5055 RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5056 g.Style.FrameBorderSize = backup_border_size;
5057 }
5058 else
5059 {
5060 // Window background
5061 if (!(flags & ImGuiWindowFlags_NoBackground))
5062 {
5063 ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5064 float alpha = 1.0f;
5065 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
5066 alpha = g.NextWindowData.BgAlphaVal;
5067 if (alpha != 1.0f)
5068 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
5069 window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
5070 }
5071
5072 // Title bar
5073 if (!(flags & ImGuiWindowFlags_NoTitleBar))
5074 {
5075 ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5076 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
5077 }
5078
5079 // Menu bar
5080 if (flags & ImGuiWindowFlags_MenuBar)
5081 {
5082 ImRect menu_bar_rect = window->MenuBarRect();
5083 menu_bar_rect.ClipWith(window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them.
5084 window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top);
5085 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5086 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5087 }
5088
5089 // Scrollbars
5090 if (window->ScrollbarX)
5091 Scrollbar(ImGuiAxis_X);
5092 if (window->ScrollbarY)
5093 Scrollbar(ImGuiAxis_Y);
5094
5095 // Render resize grips (after their input handling so we don't have a frame of latency)
5096 if (!(flags & ImGuiWindowFlags_NoResize))
5097 {
5098 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5099 {
5100 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5101 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5102 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size)));
5103 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size)));
5104 window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12);
5105 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5106 }
5107 }
5108
5109 // Borders
5110 RenderWindowOuterBorders(window);
5111 }
5112 }
5113
5114 // Render title text, collapse button, close button
RenderWindowTitleBarContents(ImGuiWindow * window,const ImRect & title_bar_rect,const char * name,bool * p_open)5115 void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
5116 {
5117 ImGuiContext& g = *GImGui;
5118 ImGuiStyle& style = g.Style;
5119 ImGuiWindowFlags flags = window->Flags;
5120
5121 const bool has_close_button = (p_open != NULL);
5122 const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
5123
5124 // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
5125 const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
5126 window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5127 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5128 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
5129
5130 // Layout buttons
5131 // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
5132 float pad_l = style.FramePadding.x;
5133 float pad_r = style.FramePadding.x;
5134 float button_sz = g.FontSize;
5135 ImVec2 close_button_pos;
5136 ImVec2 collapse_button_pos;
5137 if (has_close_button)
5138 {
5139 pad_r += button_sz;
5140 close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5141 }
5142 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
5143 {
5144 pad_r += button_sz;
5145 collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5146 }
5147 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
5148 {
5149 collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y);
5150 pad_l += button_sz;
5151 }
5152
5153 // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
5154 if (has_collapse_button)
5155 if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos))
5156 window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
5157
5158 // Close button
5159 if (has_close_button)
5160 if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
5161 *p_open = false;
5162
5163 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5164 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5165 window->DC.ItemFlags = item_flags_backup;
5166
5167 // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5168 // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
5169 const char* UNSAVED_DOCUMENT_MARKER = "*";
5170 const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f;
5171 const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5172
5173 // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
5174 // while uncentered title text will still reach edges correct.
5175 if (pad_l > style.FramePadding.x)
5176 pad_l += g.Style.ItemInnerSpacing.x;
5177 if (pad_r > style.FramePadding.x)
5178 pad_r += g.Style.ItemInnerSpacing.x;
5179 if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
5180 {
5181 float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
5182 float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
5183 pad_l = ImMax(pad_l, pad_extend * centerness);
5184 pad_r = ImMax(pad_r, pad_extend * centerness);
5185 }
5186
5187 ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y);
5188 ImRect clip_r(layout_r.Min.x, layout_r.Min.y, layout_r.Max.x + g.Style.ItemInnerSpacing.x, layout_r.Max.y);
5189 //if (g.IO.KeyCtrl) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
5190 RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
5191 if (flags & ImGuiWindowFlags_UnsavedDocument)
5192 {
5193 ImVec2 marker_pos = ImVec2(ImMax(layout_r.Min.x, layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, layout_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f);
5194 ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f));
5195 RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r);
5196 }
5197 }
5198
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)5199 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
5200 {
5201 window->ParentWindow = parent_window;
5202 window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
5203 if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
5204 window->RootWindow = parent_window->RootWindow;
5205 if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
5206 window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
5207 while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
5208 {
5209 IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
5210 window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
5211 }
5212 }
5213
5214 // Push a new Dear ImGui window to add widgets to.
5215 // - 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.
5216 // - Begin/End can be called multiple times during the frame with the same window name to append content.
5217 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
5218 // 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.
5219 // - 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.
5220 // - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed.
Begin(const char * name,bool * p_open,ImGuiWindowFlags flags)5221 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
5222 {
5223 ImGuiContext& g = *GImGui;
5224 const ImGuiStyle& style = g.Style;
5225 IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required
5226 IM_ASSERT(g.FrameScopeActive); // Forgot to call ImGui::NewFrame()
5227 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
5228
5229 // Find or create
5230 ImGuiWindow* window = FindWindowByName(name);
5231 const bool window_just_created = (window == NULL);
5232 if (window_just_created)
5233 {
5234 ImVec2 size_on_first_use = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here.
5235 window = CreateNewWindow(name, size_on_first_use, flags);
5236 }
5237
5238 // Automatically disable manual moving/resizing when NoInputs is set
5239 if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
5240 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
5241
5242 if (flags & ImGuiWindowFlags_NavFlattened)
5243 IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
5244
5245 const int current_frame = g.FrameCount;
5246 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
5247
5248 // Update the Appearing flag
5249 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
5250 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
5251 if (flags & ImGuiWindowFlags_Popup)
5252 {
5253 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5254 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
5255 window_just_activated_by_user |= (window != popup_ref.Window);
5256 }
5257 window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
5258 if (window->Appearing)
5259 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
5260
5261 // Update Flags, LastFrameActive, BeginOrderXXX fields
5262 if (first_begin_of_the_frame)
5263 {
5264 window->Flags = (ImGuiWindowFlags)flags;
5265 window->LastFrameActive = current_frame;
5266 window->LastTimeActive = (float)g.Time;
5267 window->BeginOrderWithinParent = 0;
5268 window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
5269 }
5270 else
5271 {
5272 flags = window->Flags;
5273 }
5274
5275 // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack
5276 ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
5277 ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
5278 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
5279
5280 // We allow window memory to be compacted so recreate the base stack when needed.
5281 if (window->IDStack.Size == 0)
5282 window->IDStack.push_back(window->ID);
5283
5284 // Add to stack
5285 // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
5286 g.CurrentWindowStack.push_back(window);
5287 g.CurrentWindow = NULL;
5288 CheckStacksSize(window, true);
5289 if (flags & ImGuiWindowFlags_Popup)
5290 {
5291 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5292 popup_ref.Window = window;
5293 g.BeginPopupStack.push_back(popup_ref);
5294 window->PopupId = popup_ref.PopupId;
5295 }
5296
5297 if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
5298 window->NavLastIds[0] = 0;
5299
5300 // Process SetNextWindow***() calls
5301 bool window_pos_set_by_api = false;
5302 bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
5303 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
5304 {
5305 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
5306 if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
5307 {
5308 // May be processed on the next frame if this is our first frame and we are measuring size
5309 // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
5310 window->SetWindowPosVal = g.NextWindowData.PosVal;
5311 window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
5312 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5313 }
5314 else
5315 {
5316 SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
5317 }
5318 }
5319 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
5320 {
5321 window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
5322 window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
5323 SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
5324 }
5325 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
5326 window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
5327 else if (first_begin_of_the_frame)
5328 window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
5329 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
5330 SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
5331 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
5332 FocusWindow(window);
5333 if (window->Appearing)
5334 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
5335
5336 // When reusing window again multiple times a frame, just append content (don't need to setup again)
5337 if (first_begin_of_the_frame)
5338 {
5339 // Initialize
5340 const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
5341 UpdateWindowParentAndRootLinks(window, flags, parent_window);
5342
5343 window->Active = true;
5344 window->HasCloseButton = (p_open != NULL);
5345 window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
5346 window->IDStack.resize(1);
5347
5348 // Restore buffer capacity when woken from a compacted state, to avoid
5349 if (window->MemoryCompacted)
5350 GcAwakeTransientWindowBuffers(window);
5351
5352 // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
5353 // The title bar always display the 'name' parameter, so we only update the string storage if it needs to be visible to the end-user elsewhere.
5354 bool window_title_visible_elsewhere = false;
5355 if (g.NavWindowingList != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
5356 window_title_visible_elsewhere = true;
5357 if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
5358 {
5359 size_t buf_len = (size_t)window->NameBufLen;
5360 window->Name = ImStrdupcpy(window->Name, &buf_len, name);
5361 window->NameBufLen = (int)buf_len;
5362 }
5363
5364 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
5365
5366 // Update contents size from last frame for auto-fitting (or use explicit size)
5367 window->ContentSize = CalcWindowContentSize(window);
5368 if (window->HiddenFramesCanSkipItems > 0)
5369 window->HiddenFramesCanSkipItems--;
5370 if (window->HiddenFramesCannotSkipItems > 0)
5371 window->HiddenFramesCannotSkipItems--;
5372
5373 // Hide new windows for one frame until they calculate their size
5374 if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
5375 window->HiddenFramesCannotSkipItems = 1;
5376
5377 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
5378 // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
5379 if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
5380 {
5381 window->HiddenFramesCannotSkipItems = 1;
5382 if (flags & ImGuiWindowFlags_AlwaysAutoResize)
5383 {
5384 if (!window_size_x_set_by_api)
5385 window->Size.x = window->SizeFull.x = 0.f;
5386 if (!window_size_y_set_by_api)
5387 window->Size.y = window->SizeFull.y = 0.f;
5388 window->ContentSize = ImVec2(0.f, 0.f);
5389 }
5390 }
5391
5392 // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)
5393 SetCurrentWindow(window);
5394
5395 // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
5396
5397 if (flags & ImGuiWindowFlags_ChildWindow)
5398 window->WindowBorderSize = style.ChildBorderSize;
5399 else
5400 window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
5401 window->WindowPadding = style.WindowPadding;
5402 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
5403 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
5404 window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
5405 window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
5406
5407 // Collapse window by double-clicking on title bar
5408 // 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
5409 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
5410 {
5411 // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar.
5412 ImRect title_bar_rect = window->TitleBarRect();
5413 if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
5414 window->WantCollapseToggle = true;
5415 if (window->WantCollapseToggle)
5416 {
5417 window->Collapsed = !window->Collapsed;
5418 MarkIniSettingsDirty(window);
5419 FocusWindow(window);
5420 }
5421 }
5422 else
5423 {
5424 window->Collapsed = false;
5425 }
5426 window->WantCollapseToggle = false;
5427
5428 // SIZE
5429
5430 // Calculate auto-fit size, handle automatic resize
5431 const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSize);
5432 bool use_current_size_for_scrollbar_x = window_just_created;
5433 bool use_current_size_for_scrollbar_y = window_just_created;
5434 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
5435 {
5436 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
5437 if (!window_size_x_set_by_api)
5438 {
5439 window->SizeFull.x = size_auto_fit.x;
5440 use_current_size_for_scrollbar_x = true;
5441 }
5442 if (!window_size_y_set_by_api)
5443 {
5444 window->SizeFull.y = size_auto_fit.y;
5445 use_current_size_for_scrollbar_y = true;
5446 }
5447 }
5448 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5449 {
5450 // Auto-fit may only grow window during the first few frames
5451 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
5452 if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
5453 {
5454 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
5455 use_current_size_for_scrollbar_x = true;
5456 }
5457 if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
5458 {
5459 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
5460 use_current_size_for_scrollbar_y = true;
5461 }
5462 if (!window->Collapsed)
5463 MarkIniSettingsDirty(window);
5464 }
5465
5466 // Apply minimum/maximum window size constraints and final size
5467 window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
5468 window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
5469
5470 // Decoration size
5471 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
5472
5473 // POSITION
5474
5475 // Popup latch its initial position, will position itself when it appears next frame
5476 if (window_just_activated_by_user)
5477 {
5478 window->AutoPosLastDirection = ImGuiDir_None;
5479 if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
5480 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
5481 }
5482
5483 // Position child window
5484 if (flags & ImGuiWindowFlags_ChildWindow)
5485 {
5486 IM_ASSERT(parent_window && parent_window->Active);
5487 window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
5488 parent_window->DC.ChildWindows.push_back(window);
5489 if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
5490 window->Pos = parent_window->DC.CursorPos;
5491 }
5492
5493 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
5494 if (window_pos_with_pivot)
5495 SetWindowPos(window, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
5496 else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
5497 window->Pos = FindBestWindowPosForPopup(window);
5498 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
5499 window->Pos = FindBestWindowPosForPopup(window);
5500 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
5501 window->Pos = FindBestWindowPosForPopup(window);
5502
5503 // Clamp position/size so window stays visible within its viewport or monitor
5504
5505 // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
5506 ImRect viewport_rect(GetViewportRect());
5507 if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5508 {
5509 if (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.
5510 {
5511 ImVec2 clamp_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
5512 ClampWindowRect(window, viewport_rect, clamp_padding);
5513 }
5514 }
5515 window->Pos = ImFloor(window->Pos);
5516
5517 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
5518 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
5519
5520 // Apply window focus (new and reactivated windows are moved to front)
5521 bool want_focus = false;
5522 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
5523 {
5524 if (flags & ImGuiWindowFlags_Popup)
5525 want_focus = true;
5526 else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
5527 want_focus = true;
5528 }
5529
5530 // Handle manual resize: Resize Grips, Borders, Gamepad
5531 int border_held = -1;
5532 ImU32 resize_grip_col[4] = {};
5533 const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // 4
5534 const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5535 if (!window->Collapsed)
5536 if (UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]))
5537 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true;
5538 window->ResizeBorderHeld = (signed char)border_held;
5539
5540 // SCROLLBAR VISIBILITY
5541
5542 // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
5543 if (!window->Collapsed)
5544 {
5545 // When reading the current size we need to read it after size constraints have been applied.
5546 // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again.
5547 ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height);
5548 ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes;
5549 ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
5550 float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
5551 float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
5552 //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
5553 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
5554 window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
5555 if (window->ScrollbarX && !window->ScrollbarY)
5556 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar);
5557 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
5558 }
5559
5560 // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
5561 // Update various regions. Variables they depends on should be set above in this function.
5562 // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
5563
5564 // Outer rectangle
5565 // Not affected by window border size. Used by:
5566 // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
5567 // - Begin() initial clipping rect for drawing window background and borders.
5568 // - Begin() clipping whole child
5569 const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
5570 const ImRect outer_rect = window->Rect();
5571 const ImRect title_bar_rect = window->TitleBarRect();
5572 window->OuterRectClipped = outer_rect;
5573 window->OuterRectClipped.ClipWith(host_rect);
5574
5575 // Inner rectangle
5576 // Not affected by window border size. Used by:
5577 // - InnerClipRect
5578 // - ScrollToBringRectIntoView()
5579 // - NavUpdatePageUpPageDown()
5580 // - Scrollbar()
5581 window->InnerRect.Min.x = window->Pos.x;
5582 window->InnerRect.Min.y = window->Pos.y + decoration_up_height;
5583 window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
5584 window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
5585
5586 // Inner clipping rectangle.
5587 // Will extend a little bit outside the normal work region.
5588 // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
5589 // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
5590 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
5591 // Affected by window/frame border size. Used by:
5592 // - Begin() initial clip rect
5593 float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5594 window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5595 window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
5596 window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5597 window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
5598 window->InnerClipRect.ClipWithFull(host_rect);
5599
5600 // Default item width. Make it proportional to window size if window manually resizes
5601 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
5602 window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f);
5603 else
5604 window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f);
5605
5606 // SCROLLING
5607
5608 // Lock down maximum scrolling
5609 // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
5610 // for right/bottom aligned items without creating a scrollbar.
5611 window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
5612 window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
5613
5614 // Apply scrolling
5615 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true);
5616 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
5617
5618 // DRAWING
5619
5620 // Setup draw list and outer clipping rectangle
5621 window->DrawList->Clear();
5622 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
5623 PushClipRect(host_rect.Min, host_rect.Max, false);
5624
5625 // Draw modal window background (darkens what is behind them, all viewports)
5626 const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0;
5627 const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
5628 if (dim_bg_for_modal || dim_bg_for_window_list)
5629 {
5630 const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
5631 window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
5632 }
5633
5634 // Draw navigation selection/windowing rectangle background
5635 if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
5636 {
5637 ImRect bb = window->Rect();
5638 bb.Expand(g.FontSize);
5639 if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
5640 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
5641 }
5642
5643 // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call.
5644 // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
5645 // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child.
5646 // We also disabled this when we have dimming overlay behind this specific one child.
5647 // FIXME: More code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected.
5648 bool render_decorations_in_parent = false;
5649 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
5650 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0)
5651 render_decorations_in_parent = true;
5652 if (render_decorations_in_parent)
5653 window->DrawList = parent_window->DrawList;
5654
5655 // Handle title bar, scrollbar, resize grips and resize borders
5656 const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
5657 const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
5658 RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size);
5659
5660 if (render_decorations_in_parent)
5661 window->DrawList = &window->DrawListInst;
5662
5663 // Draw navigation selection/windowing rectangle border
5664 if (g.NavWindowingTargetAnim == window)
5665 {
5666 float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
5667 ImRect bb = window->Rect();
5668 bb.Expand(g.FontSize);
5669 if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
5670 {
5671 bb.Expand(-g.FontSize - 1.0f);
5672 rounding = window->WindowRounding;
5673 }
5674 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
5675 }
5676
5677 // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
5678
5679 // Work rectangle.
5680 // Affected by window padding and border size. Used by:
5681 // - Columns() for right-most edge
5682 // - TreeNode(), CollapsingHeader() for right-most edge
5683 // - BeginTabBar() for right-most edge
5684 const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
5685 const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
5686 const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - window->ScrollbarSizes.x));
5687 const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - decoration_up_height - window->ScrollbarSizes.y));
5688 window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
5689 window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
5690 window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
5691 window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
5692
5693 // [LEGACY] Content Region
5694 // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
5695 // Used by:
5696 // - Mouse wheel scrolling + many other things
5697 window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
5698 window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height;
5699 window->ContentRegionRect.Max.x = window->ContentRegionRect.Min.x + (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : (window->Size.x - window->WindowPadding.x * 2.0f - window->ScrollbarSizes.x));
5700 window->ContentRegionRect.Max.y = window->ContentRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - decoration_up_height - window->ScrollbarSizes.y));
5701
5702 // Setup drawing context
5703 // (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.)
5704 window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
5705 window->DC.GroupOffset.x = 0.0f;
5706 window->DC.ColumnsOffset.x = 0.0f;
5707 window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y);
5708 window->DC.CursorPos = window->DC.CursorStartPos;
5709 window->DC.CursorPosPrevLine = window->DC.CursorPos;
5710 window->DC.CursorMaxPos = window->DC.CursorStartPos;
5711 window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
5712 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
5713 window->DC.NavHideHighlightOneFrame = false;
5714 window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f);
5715 window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
5716 window->DC.NavLayerActiveMaskNext = 0x00;
5717 window->DC.MenuBarAppending = false;
5718 window->DC.ChildWindows.resize(0);
5719 window->DC.LayoutType = ImGuiLayoutType_Vertical;
5720 window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
5721 window->DC.FocusCounterAll = window->DC.FocusCounterTab = -1;
5722 window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_;
5723 window->DC.ItemWidth = window->ItemWidthDefault;
5724 window->DC.TextWrapPos = -1.0f; // disabled
5725 window->DC.ItemFlagsStack.resize(0);
5726 window->DC.ItemWidthStack.resize(0);
5727 window->DC.TextWrapPosStack.resize(0);
5728 window->DC.CurrentColumns = NULL;
5729 window->DC.TreeDepth = 0;
5730 window->DC.TreeMayJumpToParentOnPopMask = 0x00;
5731 window->DC.StateStorage = &window->StateStorage;
5732 window->DC.GroupStack.resize(0);
5733 window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
5734
5735 if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
5736 {
5737 window->DC.ItemFlags = parent_window->DC.ItemFlags;
5738 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5739 }
5740
5741 if (window->AutoFitFramesX > 0)
5742 window->AutoFitFramesX--;
5743 if (window->AutoFitFramesY > 0)
5744 window->AutoFitFramesY--;
5745
5746 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
5747 if (want_focus)
5748 {
5749 FocusWindow(window);
5750 NavInitWindow(window, false);
5751 }
5752
5753 // Title bar
5754 if (!(flags & ImGuiWindowFlags_NoTitleBar))
5755 RenderWindowTitleBarContents(window, title_bar_rect, name, p_open);
5756
5757 // Pressing CTRL+C while holding on a window copy its content to the clipboard
5758 // 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.
5759 // Maybe we can support CTRL+C on every element?
5760 /*
5761 if (g.ActiveId == move_id)
5762 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
5763 LogToClipboard();
5764 */
5765
5766 // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
5767 // This is useful to allow creating context menus on title bar only, etc.
5768 window->DC.LastItemId = window->MoveId;
5769 window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
5770 window->DC.LastItemRect = title_bar_rect;
5771 #ifdef IMGUI_ENABLE_TEST_ENGINE
5772 if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
5773 IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId);
5774 #endif
5775 }
5776 else
5777 {
5778 // Append
5779 SetCurrentWindow(window);
5780 }
5781
5782 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
5783
5784 // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused)
5785 if (first_begin_of_the_frame)
5786 window->WriteAccessed = false;
5787
5788 window->BeginCount++;
5789 g.NextWindowData.ClearFlags();
5790
5791 if (flags & ImGuiWindowFlags_ChildWindow)
5792 {
5793 // Child window can be out of sight and have "negative" clip windows.
5794 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
5795 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
5796 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5797 if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
5798 window->HiddenFramesCanSkipItems = 1;
5799
5800 // Hide along with parent or if parent is collapsed
5801 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
5802 window->HiddenFramesCanSkipItems = 1;
5803 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
5804 window->HiddenFramesCannotSkipItems = 1;
5805 }
5806
5807 // Don't render if style alpha is 0.0 at the time of Begin(). This is arbitrary and inconsistent but has been there for a long while (may remove at some point)
5808 if (style.Alpha <= 0.0f)
5809 window->HiddenFramesCanSkipItems = 1;
5810
5811 // Update the Hidden flag
5812 window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);
5813
5814 // Update the SkipItems flag, used to early out of all items functions (no layout required)
5815 bool skip_items = false;
5816 if (window->Collapsed || !window->Active || window->Hidden)
5817 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
5818 skip_items = true;
5819 window->SkipItems = skip_items;
5820
5821 return !skip_items;
5822 }
5823
End()5824 void ImGui::End()
5825 {
5826 ImGuiContext& g = *GImGui;
5827
5828 if (g.CurrentWindowStack.Size <= 1 && g.FrameScopePushedImplicitWindow)
5829 {
5830 IM_ASSERT(g.CurrentWindowStack.Size > 1 && "Calling End() too many times!");
5831 return; // FIXME-ERRORHANDLING
5832 }
5833 IM_ASSERT(g.CurrentWindowStack.Size > 0);
5834
5835 ImGuiWindow* window = g.CurrentWindow;
5836
5837 if (window->DC.CurrentColumns)
5838 EndColumns();
5839 PopClipRect(); // Inner window clip rectangle
5840
5841 // Stop logging
5842 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
5843 LogFinish();
5844
5845 // Pop from window stack
5846 g.CurrentWindowStack.pop_back();
5847 if (window->Flags & ImGuiWindowFlags_Popup)
5848 g.BeginPopupStack.pop_back();
5849 CheckStacksSize(window, false);
5850 SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
5851 }
5852
BringWindowToFocusFront(ImGuiWindow * window)5853 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
5854 {
5855 ImGuiContext& g = *GImGui;
5856 if (g.WindowsFocusOrder.back() == window)
5857 return;
5858 for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the top-most window
5859 if (g.WindowsFocusOrder[i] == window)
5860 {
5861 memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
5862 g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
5863 break;
5864 }
5865 }
5866
BringWindowToDisplayFront(ImGuiWindow * window)5867 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
5868 {
5869 ImGuiContext& g = *GImGui;
5870 ImGuiWindow* current_front_window = g.Windows.back();
5871 if (current_front_window == window || current_front_window->RootWindow == window)
5872 return;
5873 for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
5874 if (g.Windows[i] == window)
5875 {
5876 memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
5877 g.Windows[g.Windows.Size - 1] = window;
5878 break;
5879 }
5880 }
5881
BringWindowToDisplayBack(ImGuiWindow * window)5882 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
5883 {
5884 ImGuiContext& g = *GImGui;
5885 if (g.Windows[0] == window)
5886 return;
5887 for (int i = 0; i < g.Windows.Size; i++)
5888 if (g.Windows[i] == window)
5889 {
5890 memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
5891 g.Windows[0] = window;
5892 break;
5893 }
5894 }
5895
5896 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)5897 void ImGui::FocusWindow(ImGuiWindow* window)
5898 {
5899 ImGuiContext& g = *GImGui;
5900
5901 if (g.NavWindow != window)
5902 {
5903 g.NavWindow = window;
5904 if (window && g.NavDisableMouseHover)
5905 g.NavMousePosDirty = true;
5906 g.NavInitRequest = false;
5907 g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
5908 g.NavIdIsAlive = false;
5909 g.NavLayer = ImGuiNavLayer_Main;
5910 //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
5911 }
5912
5913 // Close popups if any
5914 ClosePopupsOverWindow(window, false);
5915
5916 // Passing NULL allow to disable keyboard focus
5917 if (!window)
5918 return;
5919
5920 // Move the root window to the top of the pile
5921 if (window->RootWindow)
5922 window = window->RootWindow;
5923
5924 // Steal focus on active widgets
5925 if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
5926 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
5927 ClearActiveID();
5928
5929 // Bring to front
5930 BringWindowToFocusFront(window);
5931 if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
5932 BringWindowToDisplayFront(window);
5933 }
5934
FocusTopMostWindowUnderOne(ImGuiWindow * under_this_window,ImGuiWindow * ignore_window)5935 void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window)
5936 {
5937 ImGuiContext& g = *GImGui;
5938
5939 int start_idx = g.WindowsFocusOrder.Size - 1;
5940 if (under_this_window != NULL)
5941 {
5942 int under_this_window_idx = FindWindowFocusIndex(under_this_window);
5943 if (under_this_window_idx != -1)
5944 start_idx = under_this_window_idx - 1;
5945 }
5946 for (int i = start_idx; i >= 0; i--)
5947 {
5948 // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user.
5949 ImGuiWindow* window = g.WindowsFocusOrder[i];
5950 if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow))
5951 if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
5952 {
5953 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
5954 FocusWindow(focus_window);
5955 return;
5956 }
5957 }
5958 FocusWindow(NULL);
5959 }
5960
SetNextItemWidth(float item_width)5961 void ImGui::SetNextItemWidth(float item_width)
5962 {
5963 ImGuiContext& g = *GImGui;
5964 g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
5965 g.NextItemData.Width = item_width;
5966 }
5967
PushItemWidth(float item_width)5968 void ImGui::PushItemWidth(float item_width)
5969 {
5970 ImGuiContext& g = *GImGui;
5971 ImGuiWindow* window = g.CurrentWindow;
5972 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
5973 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
5974 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
5975 }
5976
PushMultiItemsWidths(int components,float w_full)5977 void ImGui::PushMultiItemsWidths(int components, float w_full)
5978 {
5979 ImGuiContext& g = *GImGui;
5980 ImGuiWindow* window = g.CurrentWindow;
5981 const ImGuiStyle& style = g.Style;
5982 const float w_item_one = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
5983 const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
5984 window->DC.ItemWidthStack.push_back(w_item_last);
5985 for (int i = 0; i < components-1; i++)
5986 window->DC.ItemWidthStack.push_back(w_item_one);
5987 window->DC.ItemWidth = window->DC.ItemWidthStack.back();
5988 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
5989 }
5990
PopItemWidth()5991 void ImGui::PopItemWidth()
5992 {
5993 ImGuiWindow* window = GetCurrentWindow();
5994 window->DC.ItemWidthStack.pop_back();
5995 window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
5996 }
5997
5998 // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
5999 // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
CalcItemWidth()6000 float ImGui::CalcItemWidth()
6001 {
6002 ImGuiContext& g = *GImGui;
6003 ImGuiWindow* window = g.CurrentWindow;
6004 float w;
6005 if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
6006 w = g.NextItemData.Width;
6007 else
6008 w = window->DC.ItemWidth;
6009 if (w < 0.0f)
6010 {
6011 float region_max_x = GetContentRegionMaxAbs().x;
6012 w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);
6013 }
6014 w = IM_FLOOR(w);
6015 return w;
6016 }
6017
6018 // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
6019 // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
6020 // Note that only CalcItemWidth() is publicly exposed.
6021 // The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)
CalcItemSize(ImVec2 size,float default_w,float default_h)6022 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
6023 {
6024 ImGuiWindow* window = GImGui->CurrentWindow;
6025
6026 ImVec2 region_max;
6027 if (size.x < 0.0f || size.y < 0.0f)
6028 region_max = GetContentRegionMaxAbs();
6029
6030 if (size.x == 0.0f)
6031 size.x = default_w;
6032 else if (size.x < 0.0f)
6033 size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x);
6034
6035 if (size.y == 0.0f)
6036 size.y = default_h;
6037 else if (size.y < 0.0f)
6038 size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y);
6039
6040 return size;
6041 }
6042
SetCurrentFont(ImFont * font)6043 void ImGui::SetCurrentFont(ImFont* font)
6044 {
6045 ImGuiContext& g = *GImGui;
6046 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
6047 IM_ASSERT(font->Scale > 0.0f);
6048 g.Font = font;
6049 g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
6050 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
6051
6052 ImFontAtlas* atlas = g.Font->ContainerAtlas;
6053 g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
6054 g.DrawListSharedData.Font = g.Font;
6055 g.DrawListSharedData.FontSize = g.FontSize;
6056 }
6057
PushFont(ImFont * font)6058 void ImGui::PushFont(ImFont* font)
6059 {
6060 ImGuiContext& g = *GImGui;
6061 if (!font)
6062 font = GetDefaultFont();
6063 SetCurrentFont(font);
6064 g.FontStack.push_back(font);
6065 g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
6066 }
6067
PopFont()6068 void ImGui::PopFont()
6069 {
6070 ImGuiContext& g = *GImGui;
6071 g.CurrentWindow->DrawList->PopTextureID();
6072 g.FontStack.pop_back();
6073 SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
6074 }
6075
PushItemFlag(ImGuiItemFlags option,bool enabled)6076 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
6077 {
6078 ImGuiWindow* window = GetCurrentWindow();
6079 if (enabled)
6080 window->DC.ItemFlags |= option;
6081 else
6082 window->DC.ItemFlags &= ~option;
6083 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
6084 }
6085
PopItemFlag()6086 void ImGui::PopItemFlag()
6087 {
6088 ImGuiWindow* window = GetCurrentWindow();
6089 window->DC.ItemFlagsStack.pop_back();
6090 window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
6091 }
6092
6093 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)6094 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
6095 {
6096 PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
6097 }
6098
PopAllowKeyboardFocus()6099 void ImGui::PopAllowKeyboardFocus()
6100 {
6101 PopItemFlag();
6102 }
6103
PushButtonRepeat(bool repeat)6104 void ImGui::PushButtonRepeat(bool repeat)
6105 {
6106 PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
6107 }
6108
PopButtonRepeat()6109 void ImGui::PopButtonRepeat()
6110 {
6111 PopItemFlag();
6112 }
6113
PushTextWrapPos(float wrap_pos_x)6114 void ImGui::PushTextWrapPos(float wrap_pos_x)
6115 {
6116 ImGuiWindow* window = GetCurrentWindow();
6117 window->DC.TextWrapPos = wrap_pos_x;
6118 window->DC.TextWrapPosStack.push_back(wrap_pos_x);
6119 }
6120
PopTextWrapPos()6121 void ImGui::PopTextWrapPos()
6122 {
6123 ImGuiWindow* window = GetCurrentWindow();
6124 window->DC.TextWrapPosStack.pop_back();
6125 window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
6126 }
6127
6128 // FIXME: This may incur a round-trip (if the end user got their data from a float4) but eventually we aim to store the in-flight colors as ImU32
PushStyleColor(ImGuiCol idx,ImU32 col)6129 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
6130 {
6131 ImGuiContext& g = *GImGui;
6132 ImGuiColorMod backup;
6133 backup.Col = idx;
6134 backup.BackupValue = g.Style.Colors[idx];
6135 g.ColorModifiers.push_back(backup);
6136 g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
6137 }
6138
PushStyleColor(ImGuiCol idx,const ImVec4 & col)6139 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
6140 {
6141 ImGuiContext& g = *GImGui;
6142 ImGuiColorMod backup;
6143 backup.Col = idx;
6144 backup.BackupValue = g.Style.Colors[idx];
6145 g.ColorModifiers.push_back(backup);
6146 g.Style.Colors[idx] = col;
6147 }
6148
PopStyleColor(int count)6149 void ImGui::PopStyleColor(int count)
6150 {
6151 ImGuiContext& g = *GImGui;
6152 while (count > 0)
6153 {
6154 ImGuiColorMod& backup = g.ColorModifiers.back();
6155 g.Style.Colors[backup.Col] = backup.BackupValue;
6156 g.ColorModifiers.pop_back();
6157 count--;
6158 }
6159 }
6160
6161 struct ImGuiStyleVarInfo
6162 {
6163 ImGuiDataType Type;
6164 ImU32 Count;
6165 ImU32 Offset;
GetVarPtrImGuiStyleVarInfo6166 void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
6167 };
6168
6169 static const ImGuiStyleVarInfo GStyleVarInfo[] =
6170 {
6171 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
6172 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
6173 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
6174 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
6175 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
6176 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
6177 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
6178 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
6179 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
6180 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
6181 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
6182 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
6183 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
6184 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
6185 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
6186 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
6187 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
6188 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
6189 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
6190 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
6191 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
6192 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
6193 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
6194 };
6195
GetStyleVarInfo(ImGuiStyleVar idx)6196 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
6197 {
6198 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
6199 IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
6200 return &GStyleVarInfo[idx];
6201 }
6202
PushStyleVar(ImGuiStyleVar idx,float val)6203 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
6204 {
6205 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
6206 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
6207 {
6208 ImGuiContext& g = *GImGui;
6209 float* pvar = (float*)var_info->GetVarPtr(&g.Style);
6210 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
6211 *pvar = val;
6212 return;
6213 }
6214 IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!");
6215 }
6216
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)6217 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
6218 {
6219 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
6220 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
6221 {
6222 ImGuiContext& g = *GImGui;
6223 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
6224 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
6225 *pvar = val;
6226 return;
6227 }
6228 IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!");
6229 }
6230
PopStyleVar(int count)6231 void ImGui::PopStyleVar(int count)
6232 {
6233 ImGuiContext& g = *GImGui;
6234 while (count > 0)
6235 {
6236 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
6237 ImGuiStyleMod& backup = g.StyleModifiers.back();
6238 const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
6239 void* data = info->GetVarPtr(&g.Style);
6240 if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; }
6241 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
6242 g.StyleModifiers.pop_back();
6243 count--;
6244 }
6245 }
6246
GetStyleColorName(ImGuiCol idx)6247 const char* ImGui::GetStyleColorName(ImGuiCol idx)
6248 {
6249 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
6250 switch (idx)
6251 {
6252 case ImGuiCol_Text: return "Text";
6253 case ImGuiCol_TextDisabled: return "TextDisabled";
6254 case ImGuiCol_WindowBg: return "WindowBg";
6255 case ImGuiCol_ChildBg: return "ChildBg";
6256 case ImGuiCol_PopupBg: return "PopupBg";
6257 case ImGuiCol_Border: return "Border";
6258 case ImGuiCol_BorderShadow: return "BorderShadow";
6259 case ImGuiCol_FrameBg: return "FrameBg";
6260 case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
6261 case ImGuiCol_FrameBgActive: return "FrameBgActive";
6262 case ImGuiCol_TitleBg: return "TitleBg";
6263 case ImGuiCol_TitleBgActive: return "TitleBgActive";
6264 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
6265 case ImGuiCol_MenuBarBg: return "MenuBarBg";
6266 case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
6267 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
6268 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
6269 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
6270 case ImGuiCol_CheckMark: return "CheckMark";
6271 case ImGuiCol_SliderGrab: return "SliderGrab";
6272 case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
6273 case ImGuiCol_Button: return "Button";
6274 case ImGuiCol_ButtonHovered: return "ButtonHovered";
6275 case ImGuiCol_ButtonActive: return "ButtonActive";
6276 case ImGuiCol_Header: return "Header";
6277 case ImGuiCol_HeaderHovered: return "HeaderHovered";
6278 case ImGuiCol_HeaderActive: return "HeaderActive";
6279 case ImGuiCol_Separator: return "Separator";
6280 case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
6281 case ImGuiCol_SeparatorActive: return "SeparatorActive";
6282 case ImGuiCol_ResizeGrip: return "ResizeGrip";
6283 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
6284 case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
6285 case ImGuiCol_Tab: return "Tab";
6286 case ImGuiCol_TabHovered: return "TabHovered";
6287 case ImGuiCol_TabActive: return "TabActive";
6288 case ImGuiCol_TabUnfocused: return "TabUnfocused";
6289 case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
6290 case ImGuiCol_PlotLines: return "PlotLines";
6291 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
6292 case ImGuiCol_PlotHistogram: return "PlotHistogram";
6293 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
6294 case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
6295 case ImGuiCol_DragDropTarget: return "DragDropTarget";
6296 case ImGuiCol_NavHighlight: return "NavHighlight";
6297 case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
6298 case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
6299 case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
6300 }
6301 IM_ASSERT(0);
6302 return "Unknown";
6303 }
6304
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)6305 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
6306 {
6307 if (window->RootWindow == potential_parent)
6308 return true;
6309 while (window != NULL)
6310 {
6311 if (window == potential_parent)
6312 return true;
6313 window = window->ParentWindow;
6314 }
6315 return false;
6316 }
6317
IsWindowHovered(ImGuiHoveredFlags flags)6318 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
6319 {
6320 IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
6321 ImGuiContext& g = *GImGui;
6322
6323 if (flags & ImGuiHoveredFlags_AnyWindow)
6324 {
6325 if (g.HoveredWindow == NULL)
6326 return false;
6327 }
6328 else
6329 {
6330 switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
6331 {
6332 case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
6333 if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
6334 return false;
6335 break;
6336 case ImGuiHoveredFlags_RootWindow:
6337 if (g.HoveredWindow != g.CurrentWindow->RootWindow)
6338 return false;
6339 break;
6340 case ImGuiHoveredFlags_ChildWindows:
6341 if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
6342 return false;
6343 break;
6344 default:
6345 if (g.HoveredWindow != g.CurrentWindow)
6346 return false;
6347 break;
6348 }
6349 }
6350
6351 if (!IsWindowContentHoverable(g.HoveredWindow, flags))
6352 return false;
6353 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
6354 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
6355 return false;
6356 return true;
6357 }
6358
IsWindowFocused(ImGuiFocusedFlags flags)6359 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
6360 {
6361 ImGuiContext& g = *GImGui;
6362
6363 if (flags & ImGuiFocusedFlags_AnyWindow)
6364 return g.NavWindow != NULL;
6365
6366 IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End()
6367 switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
6368 {
6369 case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
6370 return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
6371 case ImGuiFocusedFlags_RootWindow:
6372 return g.NavWindow == g.CurrentWindow->RootWindow;
6373 case ImGuiFocusedFlags_ChildWindows:
6374 return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
6375 default:
6376 return g.NavWindow == g.CurrentWindow;
6377 }
6378 }
6379
6380 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
6381 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmaticaly.
6382 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)6383 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
6384 {
6385 return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
6386 }
6387
GetWindowWidth()6388 float ImGui::GetWindowWidth()
6389 {
6390 ImGuiWindow* window = GImGui->CurrentWindow;
6391 return window->Size.x;
6392 }
6393
GetWindowHeight()6394 float ImGui::GetWindowHeight()
6395 {
6396 ImGuiWindow* window = GImGui->CurrentWindow;
6397 return window->Size.y;
6398 }
6399
GetWindowPos()6400 ImVec2 ImGui::GetWindowPos()
6401 {
6402 ImGuiContext& g = *GImGui;
6403 ImGuiWindow* window = g.CurrentWindow;
6404 return window->Pos;
6405 }
6406
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)6407 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
6408 {
6409 // Test condition (NB: bit 0 is always true) and clear flags for next time
6410 if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
6411 return;
6412
6413 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6414 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6415 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
6416
6417 // Set
6418 const ImVec2 old_pos = window->Pos;
6419 window->Pos = ImFloor(pos);
6420 ImVec2 offset = window->Pos - old_pos;
6421 window->DC.CursorPos += offset; // 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
6422 window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
6423 window->DC.CursorStartPos += offset;
6424 }
6425
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)6426 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
6427 {
6428 ImGuiWindow* window = GetCurrentWindowRead();
6429 SetWindowPos(window, pos, cond);
6430 }
6431
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)6432 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
6433 {
6434 if (ImGuiWindow* window = FindWindowByName(name))
6435 SetWindowPos(window, pos, cond);
6436 }
6437
GetWindowSize()6438 ImVec2 ImGui::GetWindowSize()
6439 {
6440 ImGuiWindow* window = GetCurrentWindowRead();
6441 return window->Size;
6442 }
6443
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)6444 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
6445 {
6446 // Test condition (NB: bit 0 is always true) and clear flags for next time
6447 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
6448 return;
6449
6450 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6451 window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6452
6453 // Set
6454 if (size.x > 0.0f)
6455 {
6456 window->AutoFitFramesX = 0;
6457 window->SizeFull.x = IM_FLOOR(size.x);
6458 }
6459 else
6460 {
6461 window->AutoFitFramesX = 2;
6462 window->AutoFitOnlyGrows = false;
6463 }
6464 if (size.y > 0.0f)
6465 {
6466 window->AutoFitFramesY = 0;
6467 window->SizeFull.y = IM_FLOOR(size.y);
6468 }
6469 else
6470 {
6471 window->AutoFitFramesY = 2;
6472 window->AutoFitOnlyGrows = false;
6473 }
6474 }
6475
SetWindowSize(const ImVec2 & size,ImGuiCond cond)6476 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
6477 {
6478 SetWindowSize(GImGui->CurrentWindow, size, cond);
6479 }
6480
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)6481 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
6482 {
6483 if (ImGuiWindow* window = FindWindowByName(name))
6484 SetWindowSize(window, size, cond);
6485 }
6486
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)6487 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
6488 {
6489 // Test condition (NB: bit 0 is always true) and clear flags for next time
6490 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
6491 return;
6492 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6493
6494 // Set
6495 window->Collapsed = collapsed;
6496 }
6497
SetWindowCollapsed(bool collapsed,ImGuiCond cond)6498 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
6499 {
6500 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
6501 }
6502
IsWindowCollapsed()6503 bool ImGui::IsWindowCollapsed()
6504 {
6505 ImGuiWindow* window = GetCurrentWindowRead();
6506 return window->Collapsed;
6507 }
6508
IsWindowAppearing()6509 bool ImGui::IsWindowAppearing()
6510 {
6511 ImGuiWindow* window = GetCurrentWindowRead();
6512 return window->Appearing;
6513 }
6514
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)6515 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
6516 {
6517 if (ImGuiWindow* window = FindWindowByName(name))
6518 SetWindowCollapsed(window, collapsed, cond);
6519 }
6520
SetWindowFocus()6521 void ImGui::SetWindowFocus()
6522 {
6523 FocusWindow(GImGui->CurrentWindow);
6524 }
6525
SetWindowFocus(const char * name)6526 void ImGui::SetWindowFocus(const char* name)
6527 {
6528 if (name)
6529 {
6530 if (ImGuiWindow* window = FindWindowByName(name))
6531 FocusWindow(window);
6532 }
6533 else
6534 {
6535 FocusWindow(NULL);
6536 }
6537 }
6538
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)6539 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
6540 {
6541 ImGuiContext& g = *GImGui;
6542 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6543 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
6544 g.NextWindowData.PosVal = pos;
6545 g.NextWindowData.PosPivotVal = pivot;
6546 g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
6547 }
6548
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)6549 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
6550 {
6551 ImGuiContext& g = *GImGui;
6552 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6553 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
6554 g.NextWindowData.SizeVal = size;
6555 g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
6556 }
6557
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)6558 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
6559 {
6560 ImGuiContext& g = *GImGui;
6561 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
6562 g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
6563 g.NextWindowData.SizeCallback = custom_callback;
6564 g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
6565 }
6566
6567 // Content size = inner scrollable rectangle, padded with WindowPadding.
6568 // SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
SetNextWindowContentSize(const ImVec2 & size)6569 void ImGui::SetNextWindowContentSize(const ImVec2& size)
6570 {
6571 ImGuiContext& g = *GImGui;
6572 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
6573 g.NextWindowData.ContentSizeVal = size;
6574 }
6575
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)6576 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
6577 {
6578 ImGuiContext& g = *GImGui;
6579 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6580 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
6581 g.NextWindowData.CollapsedVal = collapsed;
6582 g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
6583 }
6584
SetNextWindowFocus()6585 void ImGui::SetNextWindowFocus()
6586 {
6587 ImGuiContext& g = *GImGui;
6588 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
6589 }
6590
SetNextWindowBgAlpha(float alpha)6591 void ImGui::SetNextWindowBgAlpha(float alpha)
6592 {
6593 ImGuiContext& g = *GImGui;
6594 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
6595 g.NextWindowData.BgAlphaVal = alpha;
6596 }
6597
6598 // FIXME: This is in window space (not screen space!). We should try to obsolete all those functions.
GetContentRegionMax()6599 ImVec2 ImGui::GetContentRegionMax()
6600 {
6601 ImGuiContext& g = *GImGui;
6602 ImGuiWindow* window = g.CurrentWindow;
6603 ImVec2 mx = window->ContentRegionRect.Max - window->Pos;
6604 if (window->DC.CurrentColumns)
6605 mx.x = window->WorkRect.Max.x - window->Pos.x;
6606 return mx;
6607 }
6608
6609 // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.
GetContentRegionMaxAbs()6610 ImVec2 ImGui::GetContentRegionMaxAbs()
6611 {
6612 ImGuiContext& g = *GImGui;
6613 ImGuiWindow* window = g.CurrentWindow;
6614 ImVec2 mx = window->ContentRegionRect.Max;
6615 if (window->DC.CurrentColumns)
6616 mx.x = window->WorkRect.Max.x;
6617 return mx;
6618 }
6619
GetContentRegionAvail()6620 ImVec2 ImGui::GetContentRegionAvail()
6621 {
6622 ImGuiWindow* window = GImGui->CurrentWindow;
6623 return GetContentRegionMaxAbs() - window->DC.CursorPos;
6624 }
6625
6626 // In window space (not screen space!)
GetWindowContentRegionMin()6627 ImVec2 ImGui::GetWindowContentRegionMin()
6628 {
6629 ImGuiWindow* window = GImGui->CurrentWindow;
6630 return window->ContentRegionRect.Min - window->Pos;
6631 }
6632
GetWindowContentRegionMax()6633 ImVec2 ImGui::GetWindowContentRegionMax()
6634 {
6635 ImGuiWindow* window = GImGui->CurrentWindow;
6636 return window->ContentRegionRect.Max - window->Pos;
6637 }
6638
GetWindowContentRegionWidth()6639 float ImGui::GetWindowContentRegionWidth()
6640 {
6641 ImGuiWindow* window = GImGui->CurrentWindow;
6642 return window->ContentRegionRect.GetWidth();
6643 }
6644
GetTextLineHeight()6645 float ImGui::GetTextLineHeight()
6646 {
6647 ImGuiContext& g = *GImGui;
6648 return g.FontSize;
6649 }
6650
GetTextLineHeightWithSpacing()6651 float ImGui::GetTextLineHeightWithSpacing()
6652 {
6653 ImGuiContext& g = *GImGui;
6654 return g.FontSize + g.Style.ItemSpacing.y;
6655 }
6656
GetFrameHeight()6657 float ImGui::GetFrameHeight()
6658 {
6659 ImGuiContext& g = *GImGui;
6660 return g.FontSize + g.Style.FramePadding.y * 2.0f;
6661 }
6662
GetFrameHeightWithSpacing()6663 float ImGui::GetFrameHeightWithSpacing()
6664 {
6665 ImGuiContext& g = *GImGui;
6666 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
6667 }
6668
GetWindowDrawList()6669 ImDrawList* ImGui::GetWindowDrawList()
6670 {
6671 ImGuiWindow* window = GetCurrentWindow();
6672 return window->DrawList;
6673 }
6674
GetFont()6675 ImFont* ImGui::GetFont()
6676 {
6677 return GImGui->Font;
6678 }
6679
GetFontSize()6680 float ImGui::GetFontSize()
6681 {
6682 return GImGui->FontSize;
6683 }
6684
GetFontTexUvWhitePixel()6685 ImVec2 ImGui::GetFontTexUvWhitePixel()
6686 {
6687 return GImGui->DrawListSharedData.TexUvWhitePixel;
6688 }
6689
SetWindowFontScale(float scale)6690 void ImGui::SetWindowFontScale(float scale)
6691 {
6692 ImGuiContext& g = *GImGui;
6693 ImGuiWindow* window = GetCurrentWindow();
6694 window->FontWindowScale = scale;
6695 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
6696 }
6697
6698 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
6699 // 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()6700 ImVec2 ImGui::GetCursorPos()
6701 {
6702 ImGuiWindow* window = GetCurrentWindowRead();
6703 return window->DC.CursorPos - window->Pos + window->Scroll;
6704 }
6705
GetCursorPosX()6706 float ImGui::GetCursorPosX()
6707 {
6708 ImGuiWindow* window = GetCurrentWindowRead();
6709 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
6710 }
6711
GetCursorPosY()6712 float ImGui::GetCursorPosY()
6713 {
6714 ImGuiWindow* window = GetCurrentWindowRead();
6715 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
6716 }
6717
SetCursorPos(const ImVec2 & local_pos)6718 void ImGui::SetCursorPos(const ImVec2& local_pos)
6719 {
6720 ImGuiWindow* window = GetCurrentWindow();
6721 window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
6722 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6723 }
6724
SetCursorPosX(float x)6725 void ImGui::SetCursorPosX(float x)
6726 {
6727 ImGuiWindow* window = GetCurrentWindow();
6728 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
6729 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
6730 }
6731
SetCursorPosY(float y)6732 void ImGui::SetCursorPosY(float y)
6733 {
6734 ImGuiWindow* window = GetCurrentWindow();
6735 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
6736 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
6737 }
6738
GetCursorStartPos()6739 ImVec2 ImGui::GetCursorStartPos()
6740 {
6741 ImGuiWindow* window = GetCurrentWindowRead();
6742 return window->DC.CursorStartPos - window->Pos;
6743 }
6744
GetCursorScreenPos()6745 ImVec2 ImGui::GetCursorScreenPos()
6746 {
6747 ImGuiWindow* window = GetCurrentWindowRead();
6748 return window->DC.CursorPos;
6749 }
6750
SetCursorScreenPos(const ImVec2 & pos)6751 void ImGui::SetCursorScreenPos(const ImVec2& pos)
6752 {
6753 ImGuiWindow* window = GetCurrentWindow();
6754 window->DC.CursorPos = pos;
6755 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6756 }
6757
ActivateItem(ImGuiID id)6758 void ImGui::ActivateItem(ImGuiID id)
6759 {
6760 ImGuiContext& g = *GImGui;
6761 g.NavNextActivateId = id;
6762 }
6763
SetKeyboardFocusHere(int offset)6764 void ImGui::SetKeyboardFocusHere(int offset)
6765 {
6766 IM_ASSERT(offset >= -1); // -1 is allowed but not below
6767 ImGuiContext& g = *GImGui;
6768 ImGuiWindow* window = g.CurrentWindow;
6769 g.FocusRequestNextWindow = window;
6770 g.FocusRequestNextCounterAll = window->DC.FocusCounterAll + 1 + offset;
6771 g.FocusRequestNextCounterTab = INT_MAX;
6772 }
6773
SetItemDefaultFocus()6774 void ImGui::SetItemDefaultFocus()
6775 {
6776 ImGuiContext& g = *GImGui;
6777 ImGuiWindow* window = g.CurrentWindow;
6778 if (!window->Appearing)
6779 return;
6780 if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
6781 {
6782 g.NavInitRequest = false;
6783 g.NavInitResultId = g.NavWindow->DC.LastItemId;
6784 g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
6785 NavUpdateAnyRequestFlag();
6786 if (!IsItemVisible())
6787 SetScrollHereY();
6788 }
6789 }
6790
SetStateStorage(ImGuiStorage * tree)6791 void ImGui::SetStateStorage(ImGuiStorage* tree)
6792 {
6793 ImGuiWindow* window = GImGui->CurrentWindow;
6794 window->DC.StateStorage = tree ? tree : &window->StateStorage;
6795 }
6796
GetStateStorage()6797 ImGuiStorage* ImGui::GetStateStorage()
6798 {
6799 ImGuiWindow* window = GImGui->CurrentWindow;
6800 return window->DC.StateStorage;
6801 }
6802
PushID(const char * str_id)6803 void ImGui::PushID(const char* str_id)
6804 {
6805 ImGuiWindow* window = GImGui->CurrentWindow;
6806 window->IDStack.push_back(window->GetIDNoKeepAlive(str_id));
6807 }
6808
PushID(const char * str_id_begin,const char * str_id_end)6809 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6810 {
6811 ImGuiWindow* window = GImGui->CurrentWindow;
6812 window->IDStack.push_back(window->GetIDNoKeepAlive(str_id_begin, str_id_end));
6813 }
6814
PushID(const void * ptr_id)6815 void ImGui::PushID(const void* ptr_id)
6816 {
6817 ImGuiWindow* window = GImGui->CurrentWindow;
6818 window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6819 }
6820
PushID(int int_id)6821 void ImGui::PushID(int int_id)
6822 {
6823 ImGuiWindow* window = GImGui->CurrentWindow;
6824 window->IDStack.push_back(window->GetIDNoKeepAlive(int_id));
6825 }
6826
6827 // Push a given id value ignoring the ID stack as a seed.
PushOverrideID(ImGuiID id)6828 void ImGui::PushOverrideID(ImGuiID id)
6829 {
6830 ImGuiWindow* window = GImGui->CurrentWindow;
6831 window->IDStack.push_back(id);
6832 }
6833
PopID()6834 void ImGui::PopID()
6835 {
6836 ImGuiWindow* window = GImGui->CurrentWindow;
6837 window->IDStack.pop_back();
6838 }
6839
GetID(const char * str_id)6840 ImGuiID ImGui::GetID(const char* str_id)
6841 {
6842 ImGuiWindow* window = GImGui->CurrentWindow;
6843 return window->GetID(str_id);
6844 }
6845
GetID(const char * str_id_begin,const char * str_id_end)6846 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6847 {
6848 ImGuiWindow* window = GImGui->CurrentWindow;
6849 return window->GetID(str_id_begin, str_id_end);
6850 }
6851
GetID(const void * ptr_id)6852 ImGuiID ImGui::GetID(const void* ptr_id)
6853 {
6854 ImGuiWindow* window = GImGui->CurrentWindow;
6855 return window->GetID(ptr_id);
6856 }
6857
IsRectVisible(const ImVec2 & size)6858 bool ImGui::IsRectVisible(const ImVec2& size)
6859 {
6860 ImGuiWindow* window = GImGui->CurrentWindow;
6861 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
6862 }
6863
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)6864 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
6865 {
6866 ImGuiWindow* window = GImGui->CurrentWindow;
6867 return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
6868 }
6869
6870 // Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
BeginGroup()6871 void ImGui::BeginGroup()
6872 {
6873 ImGuiContext& g = *GImGui;
6874 ImGuiWindow* window = GetCurrentWindow();
6875
6876 window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
6877 ImGuiGroupData& group_data = window->DC.GroupStack.back();
6878 group_data.BackupCursorPos = window->DC.CursorPos;
6879 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
6880 group_data.BackupIndent = window->DC.Indent;
6881 group_data.BackupGroupOffset = window->DC.GroupOffset;
6882 group_data.BackupCurrLineSize = window->DC.CurrLineSize;
6883 group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
6884 group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
6885 group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
6886 group_data.EmitItem = true;
6887
6888 window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
6889 window->DC.Indent = window->DC.GroupOffset;
6890 window->DC.CursorMaxPos = window->DC.CursorPos;
6891 window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
6892 if (g.LogEnabled)
6893 g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
6894 }
6895
EndGroup()6896 void ImGui::EndGroup()
6897 {
6898 ImGuiContext& g = *GImGui;
6899 ImGuiWindow* window = GetCurrentWindow();
6900 IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls
6901
6902 ImGuiGroupData& group_data = window->DC.GroupStack.back();
6903
6904 ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));
6905
6906 window->DC.CursorPos = group_data.BackupCursorPos;
6907 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
6908 window->DC.Indent = group_data.BackupIndent;
6909 window->DC.GroupOffset = group_data.BackupGroupOffset;
6910 window->DC.CurrLineSize = group_data.BackupCurrLineSize;
6911 window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
6912 if (g.LogEnabled)
6913 g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
6914
6915 if (!group_data.EmitItem)
6916 {
6917 window->DC.GroupStack.pop_back();
6918 return;
6919 }
6920
6921 window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.
6922 ItemSize(group_bb.GetSize());
6923 ItemAdd(group_bb, 0);
6924
6925 // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group.
6926 // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets.
6927 // Also if you grep for LastItemId you'll notice it is only used in that context.
6928 // (The tests not symmetrical because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.)
6929 const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
6930 const bool group_contains_prev_active_id = !group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive;
6931 if (group_contains_curr_active_id)
6932 window->DC.LastItemId = g.ActiveId;
6933 else if (group_contains_prev_active_id)
6934 window->DC.LastItemId = g.ActiveIdPreviousFrame;
6935 window->DC.LastItemRect = group_bb;
6936
6937 // Forward Edited flag
6938 if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
6939 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
6940
6941 // Forward Deactivated flag
6942 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
6943 if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
6944 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated;
6945
6946 window->DC.GroupStack.pop_back();
6947 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
6948 }
6949
6950 // Gets back to previous line and continue with horizontal layout
6951 // offset_from_start_x == 0 : follow right after previous item
6952 // offset_from_start_x != 0 : align to specified x position (relative to window/group left)
6953 // spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0
6954 // spacing_w >= 0 : enforce spacing amount
SameLine(float offset_from_start_x,float spacing_w)6955 void ImGui::SameLine(float offset_from_start_x, float spacing_w)
6956 {
6957 ImGuiWindow* window = GetCurrentWindow();
6958 if (window->SkipItems)
6959 return;
6960
6961 ImGuiContext& g = *GImGui;
6962 if (offset_from_start_x != 0.0f)
6963 {
6964 if (spacing_w < 0.0f) spacing_w = 0.0f;
6965 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
6966 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6967 }
6968 else
6969 {
6970 if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
6971 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
6972 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6973 }
6974 window->DC.CurrLineSize = window->DC.PrevLineSize;
6975 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
6976 }
6977
Indent(float indent_w)6978 void ImGui::Indent(float indent_w)
6979 {
6980 ImGuiContext& g = *GImGui;
6981 ImGuiWindow* window = GetCurrentWindow();
6982 window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6983 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6984 }
6985
Unindent(float indent_w)6986 void ImGui::Unindent(float indent_w)
6987 {
6988 ImGuiContext& g = *GImGui;
6989 ImGuiWindow* window = GetCurrentWindow();
6990 window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6991 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6992 }
6993
6994
6995 //-----------------------------------------------------------------------------
6996 // [SECTION] SCROLLING
6997 //-----------------------------------------------------------------------------
6998
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window,bool snap_on_edges)6999 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges)
7000 {
7001 ImGuiContext& g = *GImGui;
7002 ImVec2 scroll = window->Scroll;
7003 if (window->ScrollTarget.x < FLT_MAX)
7004 {
7005 float cr_x = window->ScrollTargetCenterRatio.x;
7006 float target_x = window->ScrollTarget.x;
7007 if (snap_on_edges && cr_x <= 0.0f && target_x <= window->WindowPadding.x)
7008 target_x = 0.0f;
7009 else if (snap_on_edges && cr_x >= 1.0f && target_x >= window->ContentSize.x + window->WindowPadding.x + g.Style.ItemSpacing.x)
7010 target_x = window->ContentSize.x + window->WindowPadding.x * 2.0f;
7011 scroll.x = target_x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
7012 }
7013 if (window->ScrollTarget.y < FLT_MAX)
7014 {
7015 // 'snap_on_edges' allows for a discontinuity at the edge of scrolling limits to take account of WindowPadding so that scrolling to make the last item visible scroll far enough to see the padding.
7016 float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
7017 float cr_y = window->ScrollTargetCenterRatio.y;
7018 float target_y = window->ScrollTarget.y;
7019 if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y)
7020 target_y = 0.0f;
7021 if (snap_on_edges && cr_y >= 1.0f && target_y >= window->ContentSize.y + window->WindowPadding.y + g.Style.ItemSpacing.y)
7022 target_y = window->ContentSize.y + window->WindowPadding.y * 2.0f;
7023 scroll.y = target_y - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height);
7024 }
7025 scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
7026 if (!window->Collapsed && !window->SkipItems)
7027 {
7028 scroll.x = ImMin(scroll.x, window->ScrollMax.x);
7029 scroll.y = ImMin(scroll.y, window->ScrollMax.y);
7030 }
7031 return scroll;
7032 }
7033
7034 // Scroll to keep newly navigated item fully into view
ScrollToBringRectIntoView(ImGuiWindow * window,const ImRect & item_rect)7035 ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect)
7036 {
7037 ImGuiContext& g = *GImGui;
7038 ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
7039 //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7040
7041 ImVec2 delta_scroll;
7042 if (!window_rect.Contains(item_rect))
7043 {
7044 if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7045 SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x + g.Style.ItemSpacing.x, 0.0f);
7046 else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7047 SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f);
7048 if (item_rect.Min.y < window_rect.Min.y)
7049 SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f);
7050 else if (item_rect.Max.y >= window_rect.Max.y)
7051 SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
7052
7053 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window, false);
7054 delta_scroll = next_scroll - window->Scroll;
7055 }
7056
7057 // Also scroll parent window to keep us into view if necessary
7058 if (window->Flags & ImGuiWindowFlags_ChildWindow)
7059 delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll));
7060
7061 return delta_scroll;
7062 }
7063
GetScrollX()7064 float ImGui::GetScrollX()
7065 {
7066 ImGuiWindow* window = GImGui->CurrentWindow;
7067 return window->Scroll.x;
7068 }
7069
GetScrollY()7070 float ImGui::GetScrollY()
7071 {
7072 ImGuiWindow* window = GImGui->CurrentWindow;
7073 return window->Scroll.y;
7074 }
7075
GetScrollMaxX()7076 float ImGui::GetScrollMaxX()
7077 {
7078 ImGuiWindow* window = GImGui->CurrentWindow;
7079 return window->ScrollMax.x;
7080 }
7081
GetScrollMaxY()7082 float ImGui::GetScrollMaxY()
7083 {
7084 ImGuiWindow* window = GImGui->CurrentWindow;
7085 return window->ScrollMax.y;
7086 }
7087
SetScrollX(float scroll_x)7088 void ImGui::SetScrollX(float scroll_x)
7089 {
7090 ImGuiWindow* window = GetCurrentWindow();
7091 window->ScrollTarget.x = scroll_x;
7092 window->ScrollTargetCenterRatio.x = 0.0f;
7093 }
7094
SetScrollY(float scroll_y)7095 void ImGui::SetScrollY(float scroll_y)
7096 {
7097 ImGuiWindow* window = GetCurrentWindow();
7098 window->ScrollTarget.y = scroll_y;
7099 window->ScrollTargetCenterRatio.y = 0.0f;
7100 }
7101
SetScrollX(ImGuiWindow * window,float new_scroll_x)7102 void ImGui::SetScrollX(ImGuiWindow* window, float new_scroll_x)
7103 {
7104 window->ScrollTarget.x = new_scroll_x;
7105 window->ScrollTargetCenterRatio.x = 0.0f;
7106 }
7107
SetScrollY(ImGuiWindow * window,float new_scroll_y)7108 void ImGui::SetScrollY(ImGuiWindow* window, float new_scroll_y)
7109 {
7110 window->ScrollTarget.y = new_scroll_y;
7111 window->ScrollTargetCenterRatio.y = 0.0f;
7112 }
7113
7114
SetScrollFromPosX(ImGuiWindow * window,float local_x,float center_x_ratio)7115 void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
7116 {
7117 // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
7118 IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
7119 window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x);
7120 window->ScrollTargetCenterRatio.x = center_x_ratio;
7121 }
7122
SetScrollFromPosY(ImGuiWindow * window,float local_y,float center_y_ratio)7123 void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
7124 {
7125 // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
7126 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
7127 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
7128 local_y -= decoration_up_height;
7129 window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y);
7130 window->ScrollTargetCenterRatio.y = center_y_ratio;
7131 }
7132
SetScrollFromPosX(float local_x,float center_x_ratio)7133 void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
7134 {
7135 ImGuiContext& g = *GImGui;
7136 SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
7137 }
7138
SetScrollFromPosY(float local_y,float center_y_ratio)7139 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
7140 {
7141 ImGuiContext& g = *GImGui;
7142 SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
7143 }
7144
7145 // center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item.
SetScrollHereX(float center_x_ratio)7146 void ImGui::SetScrollHereX(float center_x_ratio)
7147 {
7148 ImGuiContext& g = *GImGui;
7149 ImGuiWindow* window = g.CurrentWindow;
7150 float target_x = window->DC.LastItemRect.Min.x - window->Pos.x; // Left of last item, in window space
7151 float last_item_width = window->DC.LastItemRect.GetWidth();
7152 target_x += (last_item_width * center_x_ratio) + (g.Style.ItemSpacing.x * (center_x_ratio - 0.5f) * 2.0f); // Precisely aim before, in the middle or after the last item.
7153 SetScrollFromPosX(target_x, center_x_ratio);
7154 }
7155
7156 // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
SetScrollHereY(float center_y_ratio)7157 void ImGui::SetScrollHereY(float center_y_ratio)
7158 {
7159 ImGuiContext& g = *GImGui;
7160 ImGuiWindow* window = g.CurrentWindow;
7161 float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
7162 target_y += (window->DC.PrevLineSize.y * center_y_ratio) + (g.Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line.
7163 SetScrollFromPosY(target_y, center_y_ratio);
7164 }
7165
7166 //-----------------------------------------------------------------------------
7167 // [SECTION] TOOLTIPS
7168 //-----------------------------------------------------------------------------
7169
BeginTooltip()7170 void ImGui::BeginTooltip()
7171 {
7172 ImGuiContext& g = *GImGui;
7173 if (g.DragDropWithinSourceOrTarget)
7174 {
7175 // The default tooltip position is a little offset to give space to see the context menu (it's also clamped within the current viewport/monitor)
7176 // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
7177 // Whatever we do we want to call SetNextWindowPos() to enforce a tooltip position and disable clipping the tooltip without our display area, like regular tooltip do.
7178 //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
7179 ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
7180 SetNextWindowPos(tooltip_pos);
7181 SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
7182 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
7183 BeginTooltipEx(0, true);
7184 }
7185 else
7186 {
7187 BeginTooltipEx(0, false);
7188 }
7189 }
7190
7191 // Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first.
BeginTooltipEx(ImGuiWindowFlags extra_flags,bool override_previous_tooltip)7192 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip)
7193 {
7194 ImGuiContext& g = *GImGui;
7195 char window_name[16];
7196 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
7197 if (override_previous_tooltip)
7198 if (ImGuiWindow* window = FindWindowByName(window_name))
7199 if (window->Active)
7200 {
7201 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
7202 window->Hidden = true;
7203 window->HiddenFramesCanSkipItems = 1;
7204 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
7205 }
7206 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
7207 Begin(window_name, NULL, flags | extra_flags);
7208 }
7209
EndTooltip()7210 void ImGui::EndTooltip()
7211 {
7212 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
7213 End();
7214 }
7215
SetTooltipV(const char * fmt,va_list args)7216 void ImGui::SetTooltipV(const char* fmt, va_list args)
7217 {
7218 ImGuiContext& g = *GImGui;
7219 if (g.DragDropWithinSourceOrTarget)
7220 BeginTooltip();
7221 else
7222 BeginTooltipEx(0, true);
7223 TextV(fmt, args);
7224 EndTooltip();
7225 }
7226
SetTooltip(const char * fmt,...)7227 void ImGui::SetTooltip(const char* fmt, ...)
7228 {
7229 va_list args;
7230 va_start(args, fmt);
7231 SetTooltipV(fmt, args);
7232 va_end(args);
7233 }
7234
7235 //-----------------------------------------------------------------------------
7236 // [SECTION] POPUPS
7237 //-----------------------------------------------------------------------------
7238
IsPopupOpen(ImGuiID id)7239 bool ImGui::IsPopupOpen(ImGuiID id)
7240 {
7241 ImGuiContext& g = *GImGui;
7242 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
7243 }
7244
IsPopupOpen(const char * str_id)7245 bool ImGui::IsPopupOpen(const char* str_id)
7246 {
7247 ImGuiContext& g = *GImGui;
7248 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
7249 }
7250
GetTopMostPopupModal()7251 ImGuiWindow* ImGui::GetTopMostPopupModal()
7252 {
7253 ImGuiContext& g = *GImGui;
7254 for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
7255 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
7256 if (popup->Flags & ImGuiWindowFlags_Modal)
7257 return popup;
7258 return NULL;
7259 }
7260
OpenPopup(const char * str_id)7261 void ImGui::OpenPopup(const char* str_id)
7262 {
7263 ImGuiContext& g = *GImGui;
7264 OpenPopupEx(g.CurrentWindow->GetID(str_id));
7265 }
7266
7267 // Mark popup as open (toggle toward open state).
7268 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
7269 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
7270 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id)7271 void ImGui::OpenPopupEx(ImGuiID id)
7272 {
7273 ImGuiContext& g = *GImGui;
7274 ImGuiWindow* parent_window = g.CurrentWindow;
7275 int current_stack_size = g.BeginPopupStack.Size;
7276 ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
7277 popup_ref.PopupId = id;
7278 popup_ref.Window = NULL;
7279 popup_ref.SourceWindow = g.NavWindow;
7280 popup_ref.OpenFrameCount = g.FrameCount;
7281 popup_ref.OpenParentId = parent_window->IDStack.back();
7282 popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
7283 popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
7284
7285 //IMGUI_DEBUG_LOG("OpenPopupEx(0x%08X)\n", g.FrameCount, id);
7286 if (g.OpenPopupStack.Size < current_stack_size + 1)
7287 {
7288 g.OpenPopupStack.push_back(popup_ref);
7289 }
7290 else
7291 {
7292 // Gently handle the user mistakenly calling OpenPopup() every frame. It is a programming mistake! However, if we were to run the regular code path, the ui
7293 // would become completely unusable because the popup will always be in hidden-while-calculating-size state _while_ claiming focus. Which would be a very confusing
7294 // situation for the programmer. Instead, we silently allow the popup to proceed, it will keep reappearing and the programming error will be more obvious to understand.
7295 if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
7296 {
7297 g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
7298 }
7299 else
7300 {
7301 // Close child popups if any, then flag popup for open/reopen
7302 g.OpenPopupStack.resize(current_stack_size + 1);
7303 g.OpenPopupStack[current_stack_size] = popup_ref;
7304 }
7305
7306 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
7307 // This is equivalent to what ClosePopupToLevel() does.
7308 //if (g.OpenPopupStack[current_stack_size].PopupId == id)
7309 // FocusWindow(parent_window);
7310 }
7311 }
7312
ClosePopupsOverWindow(ImGuiWindow * ref_window,bool restore_focus_to_window_under_popup)7313 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
7314 {
7315 ImGuiContext& g = *GImGui;
7316 if (g.OpenPopupStack.empty())
7317 return;
7318
7319 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
7320 // Don't close our own child popup windows.
7321 int popup_count_to_keep = 0;
7322 if (ref_window)
7323 {
7324 // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
7325 for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
7326 {
7327 ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
7328 if (!popup.Window)
7329 continue;
7330 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
7331 if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
7332 continue;
7333
7334 // Trim the stack when popups are not direct descendant of the reference window (the reference window is often the NavWindow)
7335 bool popup_or_descendent_is_ref_window = false;
7336 for (int m = popup_count_to_keep; m < g.OpenPopupStack.Size && !popup_or_descendent_is_ref_window; m++)
7337 if (ImGuiWindow* popup_window = g.OpenPopupStack[m].Window)
7338 if (popup_window->RootWindow == ref_window->RootWindow)
7339 popup_or_descendent_is_ref_window = true;
7340 if (!popup_or_descendent_is_ref_window)
7341 break;
7342 }
7343 }
7344 if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below
7345 {
7346 //IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
7347 ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
7348 }
7349 }
7350
ClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup)7351 void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
7352 {
7353 ImGuiContext& g = *GImGui;
7354 IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
7355 ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
7356 ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
7357 g.OpenPopupStack.resize(remaining);
7358
7359 if (restore_focus_to_window_under_popup)
7360 {
7361 if (focus_window && !focus_window->WasActive && popup_window)
7362 {
7363 // Fallback
7364 FocusTopMostWindowUnderOne(popup_window, NULL);
7365 }
7366 else
7367 {
7368 if (g.NavLayer == 0 && focus_window)
7369 focus_window = NavRestoreLastChildNavWindow(focus_window);
7370 FocusWindow(focus_window);
7371 }
7372 }
7373 }
7374
7375 // Close the popup we have begin-ed into.
CloseCurrentPopup()7376 void ImGui::CloseCurrentPopup()
7377 {
7378 ImGuiContext& g = *GImGui;
7379 int popup_idx = g.BeginPopupStack.Size - 1;
7380 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
7381 return;
7382
7383 // Closing a menu closes its top-most parent popup (unless a modal)
7384 while (popup_idx > 0)
7385 {
7386 ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
7387 ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
7388 bool close_parent = false;
7389 if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
7390 if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal))
7391 close_parent = true;
7392 if (!close_parent)
7393 break;
7394 popup_idx--;
7395 }
7396 //IMGUI_DEBUG_LOG("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
7397 ClosePopupToLevel(popup_idx, true);
7398
7399 // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
7400 // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
7401 // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
7402 if (ImGuiWindow* window = g.NavWindow)
7403 window->DC.NavHideHighlightOneFrame = true;
7404 }
7405
BeginPopupEx(ImGuiID id,ImGuiWindowFlags extra_flags)7406 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
7407 {
7408 ImGuiContext& g = *GImGui;
7409 if (!IsPopupOpen(id))
7410 {
7411 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7412 return false;
7413 }
7414
7415 char name[20];
7416 if (extra_flags & ImGuiWindowFlags_ChildMenu)
7417 ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
7418 else
7419 ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
7420
7421 bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup);
7422 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
7423 EndPopup();
7424
7425 return is_open;
7426 }
7427
BeginPopup(const char * str_id,ImGuiWindowFlags flags)7428 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
7429 {
7430 ImGuiContext& g = *GImGui;
7431 if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
7432 {
7433 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7434 return false;
7435 }
7436 flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
7437 return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
7438 }
7439
7440 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
7441 // Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup) so the actual value of *p_open is meaningless here.
BeginPopupModal(const char * name,bool * p_open,ImGuiWindowFlags flags)7442 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
7443 {
7444 ImGuiContext& g = *GImGui;
7445 ImGuiWindow* window = g.CurrentWindow;
7446 const ImGuiID id = window->GetID(name);
7447 if (!IsPopupOpen(id))
7448 {
7449 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7450 return false;
7451 }
7452
7453 // Center modal windows by default
7454 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
7455 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
7456 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
7457
7458 flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings;
7459 const bool is_open = Begin(name, p_open, flags);
7460 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
7461 {
7462 EndPopup();
7463 if (is_open)
7464 ClosePopupToLevel(g.BeginPopupStack.Size, true);
7465 return false;
7466 }
7467 return is_open;
7468 }
7469
EndPopup()7470 void ImGui::EndPopup()
7471 {
7472 ImGuiContext& g = *GImGui;
7473 IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
7474 IM_ASSERT(g.BeginPopupStack.Size > 0);
7475
7476 // Make all menus and popups wrap around for now, may need to expose that policy.
7477 NavMoveRequestTryWrapping(g.CurrentWindow, ImGuiNavMoveFlags_LoopY);
7478
7479 End();
7480 }
7481
OpenPopupOnItemClick(const char * str_id,int mouse_button)7482 bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
7483 {
7484 ImGuiWindow* window = GImGui->CurrentWindow;
7485 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7486 {
7487 ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
7488 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
7489 OpenPopupEx(id);
7490 return true;
7491 }
7492 return false;
7493 }
7494
7495 // This is a helper to handle the simplest case of associating one named popup to one given widget.
7496 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
7497 // You can pass a NULL str_id to use the identifier of the last item.
BeginPopupContextItem(const char * str_id,int mouse_button)7498 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
7499 {
7500 ImGuiWindow* window = GImGui->CurrentWindow;
7501 if (window->SkipItems)
7502 return false;
7503 ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
7504 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
7505 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7506 OpenPopupEx(id);
7507 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7508 }
7509
BeginPopupContextWindow(const char * str_id,int mouse_button,bool also_over_items)7510 bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
7511 {
7512 if (!str_id)
7513 str_id = "window_context";
7514 ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
7515 if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7516 if (also_over_items || !IsAnyItemHovered())
7517 OpenPopupEx(id);
7518 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7519 }
7520
BeginPopupContextVoid(const char * str_id,int mouse_button)7521 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
7522 {
7523 if (!str_id)
7524 str_id = "void_context";
7525 ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
7526 if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
7527 OpenPopupEx(id);
7528 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7529 }
7530
7531 // r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.)
7532 // r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it.
FindBestWindowPosForPopupEx(const ImVec2 & ref_pos,const ImVec2 & size,ImGuiDir * last_dir,const ImRect & r_outer,const ImRect & r_avoid,ImGuiPopupPositionPolicy policy)7533 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
7534 {
7535 ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
7536 //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
7537 //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
7538
7539 // Combo Box policy (we want a connecting edge)
7540 if (policy == ImGuiPopupPositionPolicy_ComboBox)
7541 {
7542 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
7543 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
7544 {
7545 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
7546 if (n != -1 && dir == *last_dir) // Already tried this direction?
7547 continue;
7548 ImVec2 pos;
7549 if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
7550 if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
7551 if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
7552 if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
7553 if (!r_outer.Contains(ImRect(pos, pos + size)))
7554 continue;
7555 *last_dir = dir;
7556 return pos;
7557 }
7558 }
7559
7560 // Default popup policy
7561 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
7562 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
7563 {
7564 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
7565 if (n != -1 && dir == *last_dir) // Already tried this direction?
7566 continue;
7567 float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x);
7568 float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y);
7569 if (avail_w < size.x || avail_h < size.y)
7570 continue;
7571 ImVec2 pos;
7572 pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
7573 pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
7574 *last_dir = dir;
7575 return pos;
7576 }
7577
7578 // Fallback, try to keep within display
7579 *last_dir = ImGuiDir_None;
7580 ImVec2 pos = ref_pos;
7581 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
7582 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
7583 return pos;
7584 }
7585
GetWindowAllowedExtentRect(ImGuiWindow * window)7586 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window)
7587 {
7588 IM_UNUSED(window);
7589 ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
7590 ImRect r_screen = GetViewportRect();
7591 r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
7592 return r_screen;
7593 }
7594
FindBestWindowPosForPopup(ImGuiWindow * window)7595 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
7596 {
7597 ImGuiContext& g = *GImGui;
7598
7599 ImRect r_outer = GetWindowAllowedExtentRect(window);
7600 if (window->Flags & ImGuiWindowFlags_ChildMenu)
7601 {
7602 // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
7603 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
7604 IM_ASSERT(g.CurrentWindow == window);
7605 ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
7606 float horizontal_overlap = g.Style.ItemInnerSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x).
7607 ImRect r_avoid;
7608 if (parent_window->DC.MenuBarAppending)
7609 r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
7610 else
7611 r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX);
7612 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7613 }
7614 if (window->Flags & ImGuiWindowFlags_Popup)
7615 {
7616 ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
7617 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7618 }
7619 if (window->Flags & ImGuiWindowFlags_Tooltip)
7620 {
7621 // Position tooltip (always follows mouse)
7622 float sc = g.Style.MouseCursorScale;
7623 ImVec2 ref_pos = NavCalcPreferredRefPos();
7624 ImRect r_avoid;
7625 if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
7626 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
7627 else
7628 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
7629 ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7630 if (window->AutoPosLastDirection == ImGuiDir_None)
7631 pos = ref_pos + ImVec2(2, 2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
7632 return pos;
7633 }
7634 IM_ASSERT(0);
7635 return window->Pos;
7636 }
7637
7638
7639 //-----------------------------------------------------------------------------
7640 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
7641 //-----------------------------------------------------------------------------
7642
ImGetDirQuadrantFromDelta(float dx,float dy)7643 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
7644 {
7645 if (ImFabs(dx) > ImFabs(dy))
7646 return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
7647 return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
7648 }
7649
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)7650 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
7651 {
7652 if (a1 < b0)
7653 return a1 - b0;
7654 if (b1 < a0)
7655 return a0 - b1;
7656 return 0.0f;
7657 }
7658
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)7659 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
7660 {
7661 if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
7662 {
7663 r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
7664 r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
7665 }
7666 else
7667 {
7668 r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
7669 r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
7670 }
7671 }
7672
7673 // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)7674 static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
7675 {
7676 ImGuiContext& g = *GImGui;
7677 ImGuiWindow* window = g.CurrentWindow;
7678 if (g.NavLayer != window->DC.NavLayerCurrent)
7679 return false;
7680
7681 const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
7682 g.NavScoringCount++;
7683
7684 // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
7685 if (window->ParentWindow == g.NavWindow)
7686 {
7687 IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
7688 if (!window->ClipRect.Overlaps(cand))
7689 return false;
7690 cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
7691 }
7692
7693 // We perform scoring on items bounding box clipped by the current clipping rectangle on the other axis (clipping on our movement axis would give us equal scores for all clipped items)
7694 // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
7695 NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
7696
7697 // Compute distance between boxes
7698 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
7699 float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
7700 float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items
7701 if (dby != 0.0f && dbx != 0.0f)
7702 dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
7703 float dist_box = ImFabs(dbx) + ImFabs(dby);
7704
7705 // Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter)
7706 float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
7707 float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
7708 float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
7709
7710 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
7711 ImGuiDir quadrant;
7712 float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
7713 if (dbx != 0.0f || dby != 0.0f)
7714 {
7715 // For non-overlapping boxes, use distance between boxes
7716 dax = dbx;
7717 day = dby;
7718 dist_axial = dist_box;
7719 quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
7720 }
7721 else if (dcx != 0.0f || dcy != 0.0f)
7722 {
7723 // For overlapping boxes with different centers, use distance between centers
7724 dax = dcx;
7725 day = dcy;
7726 dist_axial = dist_center;
7727 quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
7728 }
7729 else
7730 {
7731 // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter)
7732 quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
7733 }
7734
7735 #if IMGUI_DEBUG_NAV_SCORING
7736 char buf[128];
7737 if (IsMouseHoveringRect(cand.Min, cand.Max))
7738 {
7739 ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]);
7740 ImDrawList* draw_list = GetForegroundDrawList(window);
7741 draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
7742 draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
7743 draw_list->AddRectFilled(cand.Max - ImVec2(4,4), cand.Max + CalcTextSize(buf) + ImVec2(4,4), IM_COL32(40,0,0,150));
7744 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
7745 }
7746 else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
7747 {
7748 if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
7749 if (quadrant == g.NavMoveDir)
7750 {
7751 ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
7752 ImDrawList* draw_list = GetForegroundDrawList(window);
7753 draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
7754 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
7755 }
7756 }
7757 #endif
7758
7759 // Is it in the quadrant we're interesting in moving to?
7760 bool new_best = false;
7761 if (quadrant == g.NavMoveDir)
7762 {
7763 // Does it beat the current best candidate?
7764 if (dist_box < result->DistBox)
7765 {
7766 result->DistBox = dist_box;
7767 result->DistCenter = dist_center;
7768 return true;
7769 }
7770 if (dist_box == result->DistBox)
7771 {
7772 // Try using distance between center points to break ties
7773 if (dist_center < result->DistCenter)
7774 {
7775 result->DistCenter = dist_center;
7776 new_best = true;
7777 }
7778 else if (dist_center == result->DistCenter)
7779 {
7780 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
7781 // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index),
7782 // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis.
7783 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
7784 new_best = true;
7785 }
7786 }
7787 }
7788
7789 // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches
7790 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
7791 // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too.
7792 // 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward.
7793 // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
7794 if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
7795 if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
7796 if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f))
7797 {
7798 result->DistAxial = dist_axial;
7799 new_best = true;
7800 }
7801
7802 return new_best;
7803 }
7804
7805 // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
NavProcessItem(ImGuiWindow * window,const ImRect & nav_bb,const ImGuiID id)7806 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
7807 {
7808 ImGuiContext& g = *GImGui;
7809 //if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag.
7810 // return;
7811
7812 const ImGuiItemFlags item_flags = window->DC.ItemFlags;
7813 const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
7814
7815 // Process Init Request
7816 if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
7817 {
7818 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
7819 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
7820 {
7821 g.NavInitResultId = id;
7822 g.NavInitResultRectRel = nav_bb_rel;
7823 }
7824 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
7825 {
7826 g.NavInitRequest = false; // Found a match, clear request
7827 NavUpdateAnyRequestFlag();
7828 }
7829 }
7830
7831 // Process Move Request (scoring for navigation)
7832 // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
7833 if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled|ImGuiItemFlags_NoNav)))
7834 {
7835 ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
7836 #if IMGUI_DEBUG_NAV_SCORING
7837 // [DEBUG] Score all items in NavWindow at all times
7838 if (!g.NavMoveRequest)
7839 g.NavMoveDir = g.NavMoveDirLast;
7840 bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
7841 #else
7842 bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
7843 #endif
7844 if (new_best)
7845 {
7846 result->ID = id;
7847 result->SelectScopeId = g.MultiSelectScopeId;
7848 result->Window = window;
7849 result->RectRel = nav_bb_rel;
7850 }
7851
7852 const float VISIBLE_RATIO = 0.70f;
7853 if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
7854 if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)
7855 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
7856 {
7857 result = &g.NavMoveResultLocalVisibleSet;
7858 result->ID = id;
7859 result->SelectScopeId = g.MultiSelectScopeId;
7860 result->Window = window;
7861 result->RectRel = nav_bb_rel;
7862 }
7863 }
7864
7865 // Update window-relative bounding box of navigated item
7866 if (g.NavId == id)
7867 {
7868 g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
7869 g.NavLayer = window->DC.NavLayerCurrent;
7870 g.NavIdIsAlive = true;
7871 g.NavIdTabCounter = window->DC.FocusCounterTab;
7872 window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position)
7873 }
7874 }
7875
NavMoveRequestButNoResultYet()7876 bool ImGui::NavMoveRequestButNoResultYet()
7877 {
7878 ImGuiContext& g = *GImGui;
7879 return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
7880 }
7881
NavMoveRequestCancel()7882 void ImGui::NavMoveRequestCancel()
7883 {
7884 ImGuiContext& g = *GImGui;
7885 g.NavMoveRequest = false;
7886 NavUpdateAnyRequestFlag();
7887 }
7888
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)7889 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
7890 {
7891 ImGuiContext& g = *GImGui;
7892 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
7893 NavMoveRequestCancel();
7894 g.NavMoveDir = move_dir;
7895 g.NavMoveClipDir = clip_dir;
7896 g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
7897 g.NavMoveRequestFlags = move_flags;
7898 g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
7899 }
7900
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)7901 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
7902 {
7903 ImGuiContext& g = *GImGui;
7904 if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0)
7905 return;
7906 IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
7907 ImRect bb_rel = window->NavRectRel[0];
7908
7909 ImGuiDir clip_dir = g.NavMoveDir;
7910 if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7911 {
7912 bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x;
7913 if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; }
7914 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7915 }
7916 if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7917 {
7918 bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
7919 if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; }
7920 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7921 }
7922 if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
7923 {
7924 bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
7925 if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; }
7926 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7927 }
7928 if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
7929 {
7930 bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
7931 if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; }
7932 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7933 }
7934 }
7935
7936 // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
7937 // This way we could find the last focused window among our children. It would be much less confusing this way?
NavSaveLastChildNavWindowIntoParent(ImGuiWindow * nav_window)7938 static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
7939 {
7940 ImGuiWindow* parent_window = nav_window;
7941 while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
7942 parent_window = parent_window->ParentWindow;
7943 if (parent_window && parent_window != nav_window)
7944 parent_window->NavLastChildNavWindow = nav_window;
7945 }
7946
7947 // Restore the last focused child.
7948 // Call when we are expected to land on the Main Layer (0) after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)7949 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
7950 {
7951 return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window;
7952 }
7953
NavRestoreLayer(ImGuiNavLayer layer)7954 static void NavRestoreLayer(ImGuiNavLayer layer)
7955 {
7956 ImGuiContext& g = *GImGui;
7957 g.NavLayer = layer;
7958 if (layer == 0)
7959 g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
7960 if (layer == 0 && g.NavWindow->NavLastIds[0] != 0)
7961 ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]);
7962 else
7963 ImGui::NavInitWindow(g.NavWindow, true);
7964 }
7965
NavUpdateAnyRequestFlag()7966 static inline void ImGui::NavUpdateAnyRequestFlag()
7967 {
7968 ImGuiContext& g = *GImGui;
7969 g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
7970 if (g.NavAnyRequest)
7971 IM_ASSERT(g.NavWindow != NULL);
7972 }
7973
7974 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)7975 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
7976 {
7977 ImGuiContext& g = *GImGui;
7978 IM_ASSERT(window == g.NavWindow);
7979 bool init_for_nav = false;
7980 if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
7981 if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
7982 init_for_nav = true;
7983 //IMGUI_DEBUG_LOG("[Nav] NavInitWindow() init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
7984 if (init_for_nav)
7985 {
7986 SetNavID(0, g.NavLayer);
7987 g.NavInitRequest = true;
7988 g.NavInitRequestFromMove = false;
7989 g.NavInitResultId = 0;
7990 g.NavInitResultRectRel = ImRect();
7991 NavUpdateAnyRequestFlag();
7992 }
7993 else
7994 {
7995 g.NavId = window->NavLastIds[0];
7996 }
7997 }
7998
NavCalcPreferredRefPos()7999 static ImVec2 ImGui::NavCalcPreferredRefPos()
8000 {
8001 ImGuiContext& g = *GImGui;
8002 if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
8003 {
8004 // Mouse (we need a fallback in case the mouse becomes invalid after being used)
8005 if (IsMousePosValid(&g.IO.MousePos))
8006 return g.IO.MousePos;
8007 return g.LastValidMousePos;
8008 }
8009 else
8010 {
8011 // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
8012 const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
8013 ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight()));
8014 ImRect visible_rect = GetViewportRect();
8015 return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta.
8016 }
8017 }
8018
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)8019 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
8020 {
8021 ImGuiContext& g = *GImGui;
8022 if (mode == ImGuiInputReadMode_Down)
8023 return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user)
8024
8025 const float t = g.IO.NavInputsDownDuration[n];
8026 if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input.
8027 return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
8028 if (t < 0.0f)
8029 return 0.0f;
8030 if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input.
8031 return (t == 0.0f) ? 1.0f : 0.0f;
8032 if (mode == ImGuiInputReadMode_Repeat)
8033 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f);
8034 if (mode == ImGuiInputReadMode_RepeatSlow)
8035 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f);
8036 if (mode == ImGuiInputReadMode_RepeatFast)
8037 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f);
8038 return 0.0f;
8039 }
8040
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)8041 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
8042 {
8043 ImVec2 delta(0.0f, 0.0f);
8044 if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
8045 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode));
8046 if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
8047 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode));
8048 if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
8049 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
8050 if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
8051 delta *= slow_factor;
8052 if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
8053 delta *= fast_factor;
8054 return delta;
8055 }
8056
NavUpdate()8057 static void ImGui::NavUpdate()
8058 {
8059 ImGuiContext& g = *GImGui;
8060 g.IO.WantSetMousePos = false;
8061 #if 0
8062 if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
8063 #endif
8064
8065 // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard)
8066 bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
8067 bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
8068 if (nav_gamepad_active)
8069 if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f)
8070 g.NavInputSource = ImGuiInputSource_NavGamepad;
8071
8072 // Update Keyboard->Nav inputs mapping
8073 if (nav_keyboard_active)
8074 {
8075 #define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } } while (0)
8076 NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate );
8077 NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input );
8078 NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel );
8079 NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
8080 NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
8081 NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ );
8082 NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
8083 if (g.IO.KeyCtrl)
8084 g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
8085 if (g.IO.KeyShift)
8086 g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
8087 if (g.IO.KeyAlt && !g.IO.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu.
8088 g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f;
8089 #undef NAV_MAP_KEY
8090 }
8091 memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration));
8092 for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++)
8093 g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f;
8094
8095 // Process navigation init request (select first/default focus)
8096 // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void)
8097 if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove) && g.NavWindow)
8098 {
8099 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
8100 //IMGUI_DEBUG_LOG("[Nav] Apply NavInitRequest result: 0x%08X Layer %d in \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name);
8101 if (g.NavInitRequestFromMove)
8102 SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel);
8103 else
8104 SetNavID(g.NavInitResultId, g.NavLayer);
8105 g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
8106 }
8107 g.NavInitRequest = false;
8108 g.NavInitRequestFromMove = false;
8109 g.NavInitResultId = 0;
8110 g.NavJustMovedToId = 0;
8111
8112 // Process navigation move request
8113 if (g.NavMoveRequest)
8114 NavUpdateMoveResult();
8115
8116 // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
8117 if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
8118 {
8119 IM_ASSERT(g.NavMoveRequest);
8120 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
8121 g.NavDisableHighlight = false;
8122 g.NavMoveRequestForward = ImGuiNavForward_None;
8123 }
8124
8125 // Apply application mouse position movement, after we had a chance to process move request result.
8126 if (g.NavMousePosDirty && g.NavIdIsAlive)
8127 {
8128 // Set mouse position given our knowledge of the navigated item position from last frame
8129 if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
8130 {
8131 if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
8132 {
8133 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos();
8134 g.IO.WantSetMousePos = true;
8135 }
8136 }
8137 g.NavMousePosDirty = false;
8138 }
8139 g.NavIdIsAlive = false;
8140 g.NavJustTabbedId = 0;
8141 IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
8142
8143 // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0
8144 if (g.NavWindow)
8145 NavSaveLastChildNavWindowIntoParent(g.NavWindow);
8146 if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0)
8147 g.NavWindow->NavLastChildNavWindow = NULL;
8148
8149 // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
8150 NavUpdateWindowing();
8151
8152 // Set output flags for user application
8153 g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
8154 g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
8155
8156 // Process NavCancel input (to close a popup, get back to parent, clear focus)
8157 if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
8158 {
8159 if (g.ActiveId != 0)
8160 {
8161 if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
8162 ClearActiveID();
8163 }
8164 else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
8165 {
8166 // Exit child window
8167 ImGuiWindow* child_window = g.NavWindow;
8168 ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
8169 IM_ASSERT(child_window->ChildId != 0);
8170 FocusWindow(parent_window);
8171 SetNavID(child_window->ChildId, 0);
8172 g.NavIdIsAlive = false;
8173 if (g.NavDisableMouseHover)
8174 g.NavMousePosDirty = true;
8175 }
8176 else if (g.OpenPopupStack.Size > 0)
8177 {
8178 // Close open popup/menu
8179 if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
8180 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
8181 }
8182 else if (g.NavLayer != 0)
8183 {
8184 // Leave the "menu" layer
8185 NavRestoreLayer(ImGuiNavLayer_Main);
8186 }
8187 else
8188 {
8189 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
8190 if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
8191 g.NavWindow->NavLastIds[0] = 0;
8192 g.NavId = 0;
8193 }
8194 }
8195
8196 // Process manual activation request
8197 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
8198 if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8199 {
8200 bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
8201 bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
8202 if (g.ActiveId == 0 && activate_pressed)
8203 g.NavActivateId = g.NavId;
8204 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
8205 g.NavActivateDownId = g.NavId;
8206 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
8207 g.NavActivatePressedId = g.NavId;
8208 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
8209 g.NavInputId = g.NavId;
8210 }
8211 if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8212 g.NavDisableHighlight = true;
8213 if (g.NavActivateId != 0)
8214 IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
8215 g.NavMoveRequest = false;
8216
8217 // Process programmatic activation request
8218 if (g.NavNextActivateId != 0)
8219 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
8220 g.NavNextActivateId = 0;
8221
8222 // Initiate directional inputs request
8223 if (g.NavMoveRequestForward == ImGuiNavForward_None)
8224 {
8225 g.NavMoveDir = ImGuiDir_None;
8226 g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
8227 if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8228 {
8229 if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft, ImGuiNavInput_KeyLeft_, ImGuiInputReadMode_Repeat)) { g.NavMoveDir = ImGuiDir_Left; }
8230 if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadRight,ImGuiNavInput_KeyRight_,ImGuiInputReadMode_Repeat)) { g.NavMoveDir = ImGuiDir_Right; }
8231 if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadUp, ImGuiNavInput_KeyUp_, ImGuiInputReadMode_Repeat)) { g.NavMoveDir = ImGuiDir_Up; }
8232 if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadDown, ImGuiNavInput_KeyDown_, ImGuiInputReadMode_Repeat)) { g.NavMoveDir = ImGuiDir_Down; }
8233 }
8234 g.NavMoveClipDir = g.NavMoveDir;
8235 }
8236 else
8237 {
8238 // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window)
8239 // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
8240 IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
8241 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
8242 g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
8243 }
8244
8245 // Update PageUp/PageDown/Home/End scroll
8246 // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
8247 float nav_scoring_rect_offset_y = 0.0f;
8248 if (nav_keyboard_active)
8249 nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
8250
8251 // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match
8252 if (g.NavMoveDir != ImGuiDir_None)
8253 {
8254 g.NavMoveRequest = true;
8255 g.NavMoveDirLast = g.NavMoveDir;
8256 }
8257 if (g.NavMoveRequest && g.NavId == 0)
8258 {
8259 //IMGUI_DEBUG_LOG("[Nav] NavInitRequest from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
8260 g.NavInitRequest = g.NavInitRequestFromMove = true;
8261 g.NavInitResultId = 0;
8262 g.NavDisableHighlight = false;
8263 }
8264 NavUpdateAnyRequestFlag();
8265
8266 // Scrolling
8267 if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
8268 {
8269 // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
8270 ImGuiWindow* window = g.NavWindow;
8271 const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * g.IO.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
8272 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
8273 {
8274 if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
8275 SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
8276 if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
8277 SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
8278 }
8279
8280 // *Normal* Manual scroll with NavScrollXXX keys
8281 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
8282 ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f);
8283 if (scroll_dir.x != 0.0f && window->ScrollbarX)
8284 {
8285 SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
8286 g.NavMoveFromClampedRefRect = true;
8287 }
8288 if (scroll_dir.y != 0.0f)
8289 {
8290 SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
8291 g.NavMoveFromClampedRefRect = true;
8292 }
8293 }
8294
8295 // Reset search results
8296 g.NavMoveResultLocal.Clear();
8297 g.NavMoveResultLocalVisibleSet.Clear();
8298 g.NavMoveResultOther.Clear();
8299
8300 // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items
8301 if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
8302 {
8303 ImGuiWindow* window = g.NavWindow;
8304 ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1,1), window->InnerRect.Max - window->Pos + ImVec2(1,1));
8305 if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
8306 {
8307 float pad = window->CalcFontSize() * 0.5f;
8308 window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item
8309 window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel);
8310 g.NavId = 0;
8311 }
8312 g.NavMoveFromClampedRefRect = false;
8313 }
8314
8315 // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items)
8316 ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0);
8317 g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
8318 g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y);
8319 g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x);
8320 g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
8321 IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
8322 //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
8323 g.NavScoringCount = 0;
8324 #if IMGUI_DEBUG_NAV_RECTS
8325 if (g.NavWindow)
8326 {
8327 ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow);
8328 if (1) { for (int layer = 0; layer < 2; layer++) draw_list->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG]
8329 if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
8330 }
8331 #endif
8332 }
8333
8334 // Apply result from previous frame navigation directional move request
NavUpdateMoveResult()8335 static void ImGui::NavUpdateMoveResult()
8336 {
8337 ImGuiContext& g = *GImGui;
8338 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
8339 {
8340 // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)
8341 if (g.NavId != 0)
8342 {
8343 g.NavDisableHighlight = false;
8344 g.NavDisableMouseHover = true;
8345 }
8346 return;
8347 }
8348
8349 // Select which result to use
8350 ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
8351
8352 // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
8353 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
8354 if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
8355 result = &g.NavMoveResultLocalVisibleSet;
8356
8357 // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
8358 if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
8359 if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
8360 result = &g.NavMoveResultOther;
8361 IM_ASSERT(g.NavWindow && result->Window);
8362
8363 // Scroll to keep newly navigated item fully into view.
8364 if (g.NavLayer == 0)
8365 {
8366 ImVec2 delta_scroll;
8367 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge)
8368 {
8369 float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
8370 delta_scroll.y = result->Window->Scroll.y - scroll_target;
8371 SetScrollY(result->Window, scroll_target);
8372 }
8373 else
8374 {
8375 ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
8376 delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs);
8377 }
8378
8379 // Offset our result position so mouse position can be applied immediately after in NavUpdate()
8380 result->RectRel.TranslateX(-delta_scroll.x);
8381 result->RectRel.TranslateY(-delta_scroll.y);
8382 }
8383
8384 ClearActiveID();
8385 g.NavWindow = result->Window;
8386 if (g.NavId != result->ID)
8387 {
8388 // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
8389 g.NavJustMovedToId = result->ID;
8390 g.NavJustMovedToMultiSelectScopeId = result->SelectScopeId;
8391 }
8392 SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel);
8393 g.NavMoveFromClampedRefRect = false;
8394 }
8395
8396 // Handle PageUp/PageDown/Home/End keys
NavUpdatePageUpPageDown()8397 static float ImGui::NavUpdatePageUpPageDown()
8398 {
8399 ImGuiContext& g = *GImGui;
8400 if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL)
8401 return 0.0f;
8402 if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != 0)
8403 return 0.0f;
8404
8405 ImGuiWindow* window = g.NavWindow;
8406 const bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
8407 const bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
8408 const bool home_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
8409 const bool end_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
8410 if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed
8411 {
8412 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
8413 {
8414 // Fallback manual-scroll when window has no navigable item
8415 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
8416 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
8417 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
8418 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
8419 else if (home_pressed)
8420 SetScrollY(window, 0.0f);
8421 else if (end_pressed)
8422 SetScrollY(window, window->ScrollMax.y);
8423 }
8424 else
8425 {
8426 ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
8427 const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
8428 float nav_scoring_rect_offset_y = 0.0f;
8429 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
8430 {
8431 nav_scoring_rect_offset_y = -page_offset_y;
8432 g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item)
8433 g.NavMoveClipDir = ImGuiDir_Up;
8434 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
8435 }
8436 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
8437 {
8438 nav_scoring_rect_offset_y = +page_offset_y;
8439 g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item)
8440 g.NavMoveClipDir = ImGuiDir_Down;
8441 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
8442 }
8443 else if (home_pressed)
8444 {
8445 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
8446 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
8447 // Preserve current horizontal position if we have any.
8448 nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
8449 if (nav_rect_rel.IsInverted())
8450 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
8451 g.NavMoveDir = ImGuiDir_Down;
8452 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
8453 }
8454 else if (end_pressed)
8455 {
8456 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
8457 if (nav_rect_rel.IsInverted())
8458 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
8459 g.NavMoveDir = ImGuiDir_Up;
8460 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
8461 }
8462 return nav_scoring_rect_offset_y;
8463 }
8464 }
8465 return 0.0f;
8466 }
8467
FindWindowFocusIndex(ImGuiWindow * window)8468 static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
8469 {
8470 ImGuiContext& g = *GImGui;
8471 for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--)
8472 if (g.WindowsFocusOrder[i] == window)
8473 return i;
8474 return -1;
8475 }
8476
FindWindowNavFocusable(int i_start,int i_stop,int dir)8477 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
8478 {
8479 ImGuiContext& g = *GImGui;
8480 for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
8481 if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
8482 return g.WindowsFocusOrder[i];
8483 return NULL;
8484 }
8485
NavUpdateWindowingHighlightWindow(int focus_change_dir)8486 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
8487 {
8488 ImGuiContext& g = *GImGui;
8489 IM_ASSERT(g.NavWindowingTarget);
8490 if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
8491 return;
8492
8493 const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
8494 ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
8495 if (!window_target)
8496 window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
8497 if (window_target) // Don't reset windowing target if there's a single window in the list
8498 g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
8499 g.NavWindowingToggleLayer = false;
8500 }
8501
8502 // Windowing management mode
8503 // Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
8504 // Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
NavUpdateWindowing()8505 static void ImGui::NavUpdateWindowing()
8506 {
8507 ImGuiContext& g = *GImGui;
8508 ImGuiWindow* apply_focus_window = NULL;
8509 bool apply_toggle_layer = false;
8510
8511 ImGuiWindow* modal_window = GetTopMostPopupModal();
8512 if (modal_window != NULL)
8513 {
8514 g.NavWindowingTarget = NULL;
8515 return;
8516 }
8517
8518 // Fade out
8519 if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
8520 {
8521 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
8522 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
8523 g.NavWindowingTargetAnim = NULL;
8524 }
8525
8526 // Start CTRL-TAB or Square+L/R window selection
8527 bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
8528 bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
8529 if (start_windowing_with_gamepad || start_windowing_with_keyboard)
8530 if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
8531 {
8532 g.NavWindowingTarget = g.NavWindowingTargetAnim = window;
8533 g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
8534 g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
8535 g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
8536 }
8537
8538 // Gamepad update
8539 g.NavWindowingTimer += g.IO.DeltaTime;
8540 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
8541 {
8542 // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise
8543 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
8544
8545 // Select window to focus
8546 const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
8547 if (focus_change_dir != 0)
8548 {
8549 NavUpdateWindowingHighlightWindow(focus_change_dir);
8550 g.NavWindowingHighlightAlpha = 1.0f;
8551 }
8552
8553 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
8554 if (!IsNavInputDown(ImGuiNavInput_Menu))
8555 {
8556 g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
8557 if (g.NavWindowingToggleLayer && g.NavWindow)
8558 apply_toggle_layer = true;
8559 else if (!g.NavWindowingToggleLayer)
8560 apply_focus_window = g.NavWindowingTarget;
8561 g.NavWindowingTarget = NULL;
8562 }
8563 }
8564
8565 // Keyboard: Focus
8566 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
8567 {
8568 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
8569 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
8570 if (IsKeyPressedMap(ImGuiKey_Tab, true))
8571 NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
8572 if (!g.IO.KeyCtrl)
8573 apply_focus_window = g.NavWindowingTarget;
8574 }
8575
8576 // Keyboard: Press and Release ALT to toggle menu layer
8577 // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB
8578 if (IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed))
8579 g.NavWindowingToggleLayer = true;
8580 if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
8581 if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
8582 apply_toggle_layer = true;
8583
8584 // Move window
8585 if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
8586 {
8587 ImVec2 move_delta;
8588 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
8589 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
8590 if (g.NavInputSource == ImGuiInputSource_NavGamepad)
8591 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
8592 if (move_delta.x != 0.0f || move_delta.y != 0.0f)
8593 {
8594 const float NAV_MOVE_SPEED = 800.0f;
8595 const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't code variable framerate very well
8596 SetWindowPos(g.NavWindowingTarget->RootWindow, g.NavWindowingTarget->RootWindow->Pos + move_delta * move_speed, ImGuiCond_Always);
8597 g.NavDisableMouseHover = true;
8598 MarkIniSettingsDirty(g.NavWindowingTarget);
8599 }
8600 }
8601
8602 // Apply final focus
8603 if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
8604 {
8605 ClearActiveID();
8606 g.NavDisableHighlight = false;
8607 g.NavDisableMouseHover = true;
8608 apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
8609 ClosePopupsOverWindow(apply_focus_window, false);
8610 FocusWindow(apply_focus_window);
8611 if (apply_focus_window->NavLastIds[0] == 0)
8612 NavInitWindow(apply_focus_window, false);
8613
8614 // If the window only has a menu layer, select it directly
8615 if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu))
8616 g.NavLayer = ImGuiNavLayer_Menu;
8617 }
8618 if (apply_focus_window)
8619 g.NavWindowingTarget = NULL;
8620
8621 // Apply menu/layer toggle
8622 if (apply_toggle_layer && g.NavWindow)
8623 {
8624 // Move to parent menu if necessary
8625 ImGuiWindow* new_nav_window = g.NavWindow;
8626 while (new_nav_window->ParentWindow
8627 && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
8628 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
8629 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8630 new_nav_window = new_nav_window->ParentWindow;
8631 if (new_nav_window != g.NavWindow)
8632 {
8633 ImGuiWindow* old_nav_window = g.NavWindow;
8634 FocusWindow(new_nav_window);
8635 new_nav_window->NavLastChildNavWindow = old_nav_window;
8636 }
8637 g.NavDisableHighlight = false;
8638 g.NavDisableMouseHover = true;
8639
8640 // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID.
8641 const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
8642 NavRestoreLayer(new_nav_layer);
8643 }
8644 }
8645
8646 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)8647 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
8648 {
8649 if (window->Flags & ImGuiWindowFlags_Popup)
8650 return "(Popup)";
8651 if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
8652 return "(Main menu bar)";
8653 return "(Untitled)";
8654 }
8655
8656 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingOverlay()8657 void ImGui::NavUpdateWindowingOverlay()
8658 {
8659 ImGuiContext& g = *GImGui;
8660 IM_ASSERT(g.NavWindowingTarget != NULL);
8661
8662 if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
8663 return;
8664
8665 if (g.NavWindowingList == NULL)
8666 g.NavWindowingList = FindWindowByName("###NavWindowingList");
8667 SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
8668 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
8669 PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
8670 Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
8671 for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
8672 {
8673 ImGuiWindow* window = g.WindowsFocusOrder[n];
8674 if (!IsWindowNavFocusable(window))
8675 continue;
8676 const char* label = window->Name;
8677 if (label == FindRenderedTextEnd(label))
8678 label = GetFallbackWindowNameForWindowingList(window);
8679 Selectable(label, g.NavWindowingTarget == window);
8680 }
8681 End();
8682 PopStyleVar();
8683 }
8684
8685
8686 //-----------------------------------------------------------------------------
8687 // [SECTION] DRAG AND DROP
8688 //-----------------------------------------------------------------------------
8689
ClearDragDrop()8690 void ImGui::ClearDragDrop()
8691 {
8692 ImGuiContext& g = *GImGui;
8693 g.DragDropActive = false;
8694 g.DragDropPayload.Clear();
8695 g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
8696 g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
8697 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
8698 g.DragDropAcceptFrameCount = -1;
8699
8700 g.DragDropPayloadBufHeap.clear();
8701 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8702 }
8703
8704 // Call when current ID is active.
8705 // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
BeginDragDropSource(ImGuiDragDropFlags flags)8706 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
8707 {
8708 ImGuiContext& g = *GImGui;
8709 ImGuiWindow* window = g.CurrentWindow;
8710
8711 bool source_drag_active = false;
8712 ImGuiID source_id = 0;
8713 ImGuiID source_parent_id = 0;
8714 int mouse_button = 0;
8715 if (!(flags & ImGuiDragDropFlags_SourceExtern))
8716 {
8717 source_id = window->DC.LastItemId;
8718 if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
8719 return false;
8720 if (g.IO.MouseDown[mouse_button] == false)
8721 return false;
8722
8723 if (source_id == 0)
8724 {
8725 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
8726 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
8727 if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
8728 {
8729 IM_ASSERT(0);
8730 return false;
8731 }
8732
8733 // Early out
8734 if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
8735 return false;
8736
8737 // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
8738 // We build a throwaway ID based on current ID stack + relative AABB of items in window.
8739 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
8740 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
8741 source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
8742 bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id);
8743 if (is_hovered && g.IO.MouseClicked[mouse_button])
8744 {
8745 SetActiveID(source_id, window);
8746 FocusWindow(window);
8747 }
8748 if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
8749 g.ActiveIdAllowOverlap = is_hovered;
8750 }
8751 else
8752 {
8753 g.ActiveIdAllowOverlap = false;
8754 }
8755 if (g.ActiveId != source_id)
8756 return false;
8757 source_parent_id = window->IDStack.back();
8758 source_drag_active = IsMouseDragging(mouse_button);
8759 }
8760 else
8761 {
8762 window = NULL;
8763 source_id = ImHashStr("#SourceExtern");
8764 source_drag_active = true;
8765 }
8766
8767 if (source_drag_active)
8768 {
8769 if (!g.DragDropActive)
8770 {
8771 IM_ASSERT(source_id != 0);
8772 ClearDragDrop();
8773 ImGuiPayload& payload = g.DragDropPayload;
8774 payload.SourceId = source_id;
8775 payload.SourceParentId = source_parent_id;
8776 g.DragDropActive = true;
8777 g.DragDropSourceFlags = flags;
8778 g.DragDropMouseButton = mouse_button;
8779 }
8780 g.DragDropSourceFrameCount = g.FrameCount;
8781 g.DragDropWithinSourceOrTarget = true;
8782
8783 if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8784 {
8785 // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
8786 // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
8787 BeginTooltip();
8788 if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
8789 {
8790 ImGuiWindow* tooltip_window = g.CurrentWindow;
8791 tooltip_window->SkipItems = true;
8792 tooltip_window->HiddenFramesCanSkipItems = 1;
8793 }
8794 }
8795
8796 if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
8797 window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
8798
8799 return true;
8800 }
8801 return false;
8802 }
8803
EndDragDropSource()8804 void ImGui::EndDragDropSource()
8805 {
8806 ImGuiContext& g = *GImGui;
8807 IM_ASSERT(g.DragDropActive);
8808 IM_ASSERT(g.DragDropWithinSourceOrTarget && "Not after a BeginDragDropSource()?");
8809
8810 if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8811 EndTooltip();
8812
8813 // Discard the drag if have not called SetDragDropPayload()
8814 if (g.DragDropPayload.DataFrameCount == -1)
8815 ClearDragDrop();
8816 g.DragDropWithinSourceOrTarget = false;
8817 }
8818
8819 // Use 'cond' to choose to submit payload on drag start or every frame
SetDragDropPayload(const char * type,const void * data,size_t data_size,ImGuiCond cond)8820 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
8821 {
8822 ImGuiContext& g = *GImGui;
8823 ImGuiPayload& payload = g.DragDropPayload;
8824 if (cond == 0)
8825 cond = ImGuiCond_Always;
8826
8827 IM_ASSERT(type != NULL);
8828 IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
8829 IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
8830 IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
8831 IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
8832
8833 if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
8834 {
8835 // Copy payload
8836 ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
8837 g.DragDropPayloadBufHeap.resize(0);
8838 if (data_size > sizeof(g.DragDropPayloadBufLocal))
8839 {
8840 // Store in heap
8841 g.DragDropPayloadBufHeap.resize((int)data_size);
8842 payload.Data = g.DragDropPayloadBufHeap.Data;
8843 memcpy(payload.Data, data, data_size);
8844 }
8845 else if (data_size > 0)
8846 {
8847 // Store locally
8848 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8849 payload.Data = g.DragDropPayloadBufLocal;
8850 memcpy(payload.Data, data, data_size);
8851 }
8852 else
8853 {
8854 payload.Data = NULL;
8855 }
8856 payload.DataSize = (int)data_size;
8857 }
8858 payload.DataFrameCount = g.FrameCount;
8859
8860 return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
8861 }
8862
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)8863 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
8864 {
8865 ImGuiContext& g = *GImGui;
8866 if (!g.DragDropActive)
8867 return false;
8868
8869 ImGuiWindow* window = g.CurrentWindow;
8870 if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8871 return false;
8872 IM_ASSERT(id != 0);
8873 if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
8874 return false;
8875 if (window->SkipItems)
8876 return false;
8877
8878 IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8879 g.DragDropTargetRect = bb;
8880 g.DragDropTargetId = id;
8881 g.DragDropWithinSourceOrTarget = true;
8882 return true;
8883 }
8884
8885 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
8886 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
8887 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
8888 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()8889 bool ImGui::BeginDragDropTarget()
8890 {
8891 ImGuiContext& g = *GImGui;
8892 if (!g.DragDropActive)
8893 return false;
8894
8895 ImGuiWindow* window = g.CurrentWindow;
8896 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
8897 return false;
8898 if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8899 return false;
8900
8901 const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
8902 ImGuiID id = window->DC.LastItemId;
8903 if (id == 0)
8904 id = window->GetIDFromRectangle(display_rect);
8905 if (g.DragDropPayload.SourceId == id)
8906 return false;
8907
8908 IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8909 g.DragDropTargetRect = display_rect;
8910 g.DragDropTargetId = id;
8911 g.DragDropWithinSourceOrTarget = true;
8912 return true;
8913 }
8914
IsDragDropPayloadBeingAccepted()8915 bool ImGui::IsDragDropPayloadBeingAccepted()
8916 {
8917 ImGuiContext& g = *GImGui;
8918 return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
8919 }
8920
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)8921 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
8922 {
8923 ImGuiContext& g = *GImGui;
8924 ImGuiWindow* window = g.CurrentWindow;
8925 ImGuiPayload& payload = g.DragDropPayload;
8926 IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
8927 IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
8928 if (type != NULL && !payload.IsDataType(type))
8929 return NULL;
8930
8931 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
8932 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
8933 const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
8934 ImRect r = g.DragDropTargetRect;
8935 float r_surface = r.GetWidth() * r.GetHeight();
8936 if (r_surface < g.DragDropAcceptIdCurrRectSurface)
8937 {
8938 g.DragDropAcceptFlags = flags;
8939 g.DragDropAcceptIdCurr = g.DragDropTargetId;
8940 g.DragDropAcceptIdCurrRectSurface = r_surface;
8941 }
8942
8943 // Render default drop visuals
8944 payload.Preview = was_accepted_previously;
8945 flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
8946 if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
8947 {
8948 // FIXME-DRAG: Settle on a proper default visuals for drop target.
8949 r.Expand(3.5f);
8950 bool push_clip_rect = !window->ClipRect.Contains(r);
8951 if (push_clip_rect) window->DrawList->PushClipRect(r.Min-ImVec2(1,1), r.Max+ImVec2(1,1));
8952 window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
8953 if (push_clip_rect) window->DrawList->PopClipRect();
8954 }
8955
8956 g.DragDropAcceptFrameCount = g.FrameCount;
8957 payload.Delivery = was_accepted_previously && !IsMouseDown(g.DragDropMouseButton); // For extern drag sources affecting os window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased()
8958 if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
8959 return NULL;
8960
8961 return &payload;
8962 }
8963
GetDragDropPayload()8964 const ImGuiPayload* ImGui::GetDragDropPayload()
8965 {
8966 ImGuiContext& g = *GImGui;
8967 return g.DragDropActive ? &g.DragDropPayload : NULL;
8968 }
8969
8970 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()8971 void ImGui::EndDragDropTarget()
8972 {
8973 ImGuiContext& g = *GImGui;
8974 IM_ASSERT(g.DragDropActive);
8975 IM_ASSERT(g.DragDropWithinSourceOrTarget);
8976 g.DragDropWithinSourceOrTarget = false;
8977 }
8978
8979
8980 //-----------------------------------------------------------------------------
8981 // [SECTION] LOGGING/CAPTURING
8982 //-----------------------------------------------------------------------------
8983 // All text output from the interface can be captured into tty/file/clipboard.
8984 // By default, tree nodes are automatically opened during logging.
8985 //-----------------------------------------------------------------------------
8986
8987 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)8988 void ImGui::LogText(const char* fmt, ...)
8989 {
8990 ImGuiContext& g = *GImGui;
8991 if (!g.LogEnabled)
8992 return;
8993
8994 va_list args;
8995 va_start(args, fmt);
8996 if (g.LogFile)
8997 vfprintf(g.LogFile, fmt, args);
8998 else
8999 g.LogBuffer.appendfv(fmt, args);
9000 va_end(args);
9001 }
9002
9003 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
9004 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)9005 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
9006 {
9007 ImGuiContext& g = *GImGui;
9008 ImGuiWindow* window = g.CurrentWindow;
9009
9010 if (!text_end)
9011 text_end = FindRenderedTextEnd(text, text_end);
9012
9013 const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1);
9014 if (ref_pos)
9015 g.LogLinePosY = ref_pos->y;
9016 if (log_new_line)
9017 g.LogLineFirstItem = true;
9018
9019 const char* text_remaining = text;
9020 if (g.LogDepthRef > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth
9021 g.LogDepthRef = window->DC.TreeDepth;
9022 const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
9023 for (;;)
9024 {
9025 // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
9026 // We don't add a trailing \n to allow a subsequent item on the same line to be captured.
9027 const char* line_start = text_remaining;
9028 const char* line_end = ImStreolRange(line_start, text_end);
9029 const bool is_first_line = (line_start == text);
9030 const bool is_last_line = (line_end == text_end);
9031 if (!is_last_line || (line_start != line_end))
9032 {
9033 const int char_count = (int)(line_end - line_start);
9034 if (log_new_line || !is_first_line)
9035 LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start);
9036 else if (g.LogLineFirstItem)
9037 LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start);
9038 else
9039 LogText(" %.*s", char_count, line_start);
9040 g.LogLineFirstItem = false;
9041 }
9042 else if (log_new_line)
9043 {
9044 // An empty "" string at a different Y position should output a carriage return.
9045 LogText(IM_NEWLINE);
9046 break;
9047 }
9048
9049 if (is_last_line)
9050 break;
9051 text_remaining = line_end + 1;
9052 }
9053 }
9054
9055 // Start logging/capturing text output
LogBegin(ImGuiLogType type,int auto_open_depth)9056 void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
9057 {
9058 ImGuiContext& g = *GImGui;
9059 ImGuiWindow* window = g.CurrentWindow;
9060 IM_ASSERT(g.LogEnabled == false);
9061 IM_ASSERT(g.LogFile == NULL);
9062 IM_ASSERT(g.LogBuffer.empty());
9063 g.LogEnabled = true;
9064 g.LogType = type;
9065 g.LogDepthRef = window->DC.TreeDepth;
9066 g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
9067 g.LogLinePosY = FLT_MAX;
9068 g.LogLineFirstItem = true;
9069 }
9070
LogToTTY(int auto_open_depth)9071 void ImGui::LogToTTY(int auto_open_depth)
9072 {
9073 ImGuiContext& g = *GImGui;
9074 if (g.LogEnabled)
9075 return;
9076 LogBegin(ImGuiLogType_TTY, auto_open_depth);
9077 g.LogFile = stdout;
9078 }
9079
9080 // Start logging/capturing text output to given file
LogToFile(int auto_open_depth,const char * filename)9081 void ImGui::LogToFile(int auto_open_depth, const char* filename)
9082 {
9083 ImGuiContext& g = *GImGui;
9084 if (g.LogEnabled)
9085 return;
9086
9087 // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
9088 // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
9089 // By opening the file in binary mode "ab" we have consistent output everywhere.
9090 if (!filename)
9091 filename = g.IO.LogFilename;
9092 if (!filename || !filename[0])
9093 return;
9094 FILE* f = ImFileOpen(filename, "ab");
9095 if (f == NULL)
9096 {
9097 IM_ASSERT(0);
9098 return;
9099 }
9100
9101 LogBegin(ImGuiLogType_File, auto_open_depth);
9102 g.LogFile = f;
9103 }
9104
9105 // Start logging/capturing text output to clipboard
LogToClipboard(int auto_open_depth)9106 void ImGui::LogToClipboard(int auto_open_depth)
9107 {
9108 ImGuiContext& g = *GImGui;
9109 if (g.LogEnabled)
9110 return;
9111 LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
9112 }
9113
LogToBuffer(int auto_open_depth)9114 void ImGui::LogToBuffer(int auto_open_depth)
9115 {
9116 ImGuiContext& g = *GImGui;
9117 if (g.LogEnabled)
9118 return;
9119 LogBegin(ImGuiLogType_Buffer, auto_open_depth);
9120 }
9121
LogFinish()9122 void ImGui::LogFinish()
9123 {
9124 ImGuiContext& g = *GImGui;
9125 if (!g.LogEnabled)
9126 return;
9127
9128 LogText(IM_NEWLINE);
9129 switch (g.LogType)
9130 {
9131 case ImGuiLogType_TTY:
9132 fflush(g.LogFile);
9133 break;
9134 case ImGuiLogType_File:
9135 fclose(g.LogFile);
9136 break;
9137 case ImGuiLogType_Buffer:
9138 break;
9139 case ImGuiLogType_Clipboard:
9140 if (!g.LogBuffer.empty())
9141 SetClipboardText(g.LogBuffer.begin());
9142 break;
9143 case ImGuiLogType_None:
9144 IM_ASSERT(0);
9145 break;
9146 }
9147
9148 g.LogEnabled = false;
9149 g.LogType = ImGuiLogType_None;
9150 g.LogFile = NULL;
9151 g.LogBuffer.clear();
9152 }
9153
9154 // Helper to display logging buttons
9155 // FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
LogButtons()9156 void ImGui::LogButtons()
9157 {
9158 ImGuiContext& g = *GImGui;
9159
9160 PushID("LogButtons");
9161 const bool log_to_tty = Button("Log To TTY"); SameLine();
9162 const bool log_to_file = Button("Log To File"); SameLine();
9163 const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
9164 PushAllowKeyboardFocus(false);
9165 SetNextItemWidth(80.0f);
9166 SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
9167 PopAllowKeyboardFocus();
9168 PopID();
9169
9170 // Start logging at the end of the function so that the buttons don't appear in the log
9171 if (log_to_tty)
9172 LogToTTY();
9173 if (log_to_file)
9174 LogToFile();
9175 if (log_to_clipboard)
9176 LogToClipboard();
9177 }
9178
9179 //-----------------------------------------------------------------------------
9180 // [SECTION] SETTINGS
9181 //-----------------------------------------------------------------------------
9182
MarkIniSettingsDirty()9183 void ImGui::MarkIniSettingsDirty()
9184 {
9185 ImGuiContext& g = *GImGui;
9186 if (g.SettingsDirtyTimer <= 0.0f)
9187 g.SettingsDirtyTimer = g.IO.IniSavingRate;
9188 }
9189
MarkIniSettingsDirty(ImGuiWindow * window)9190 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
9191 {
9192 ImGuiContext& g = *GImGui;
9193 if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
9194 if (g.SettingsDirtyTimer <= 0.0f)
9195 g.SettingsDirtyTimer = g.IO.IniSavingRate;
9196 }
9197
CreateNewWindowSettings(const char * name)9198 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
9199 {
9200 ImGuiContext& g = *GImGui;
9201 g.SettingsWindows.push_back(ImGuiWindowSettings());
9202 ImGuiWindowSettings* settings = &g.SettingsWindows.back();
9203 #if !IMGUI_DEBUG_INI_SETTINGS
9204 // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
9205 // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier.
9206 if (const char* p = strstr(name, "###"))
9207 name = p;
9208 #endif
9209 size_t name_len = strlen(name);
9210 settings->NameOffset = g.SettingsWindowsNames.size();
9211 g.SettingsWindowsNames.append(name, name + name_len + 1); // Append with zero terminator
9212 settings->ID = ImHashStr(name, name_len);
9213 return settings;
9214 }
9215
FindWindowSettings(ImGuiID id)9216 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
9217 {
9218 ImGuiContext& g = *GImGui;
9219 for (int i = 0; i != g.SettingsWindows.Size; i++)
9220 if (g.SettingsWindows[i].ID == id)
9221 return &g.SettingsWindows[i];
9222 return NULL;
9223 }
9224
FindOrCreateWindowSettings(const char * name)9225 ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
9226 {
9227 if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name)))
9228 return settings;
9229 return CreateNewWindowSettings(name);
9230 }
9231
LoadIniSettingsFromDisk(const char * ini_filename)9232 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
9233 {
9234 size_t file_data_size = 0;
9235 char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
9236 if (!file_data)
9237 return;
9238 LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
9239 IM_FREE(file_data);
9240 }
9241
FindSettingsHandler(const char * type_name)9242 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
9243 {
9244 ImGuiContext& g = *GImGui;
9245 const ImGuiID type_hash = ImHashStr(type_name);
9246 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9247 if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
9248 return &g.SettingsHandlers[handler_n];
9249 return NULL;
9250 }
9251
9252 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)9253 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
9254 {
9255 ImGuiContext& g = *GImGui;
9256 IM_ASSERT(g.Initialized);
9257 IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
9258
9259 // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
9260 // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy..
9261 if (ini_size == 0)
9262 ini_size = strlen(ini_data);
9263 char* buf = (char*)IM_ALLOC(ini_size + 1);
9264 char* buf_end = buf + ini_size;
9265 memcpy(buf, ini_data, ini_size);
9266 buf[ini_size] = 0;
9267
9268 void* entry_data = NULL;
9269 ImGuiSettingsHandler* entry_handler = NULL;
9270
9271 char* line_end = NULL;
9272 for (char* line = buf; line < buf_end; line = line_end + 1)
9273 {
9274 // Skip new lines markers, then find end of the line
9275 while (*line == '\n' || *line == '\r')
9276 line++;
9277 line_end = line;
9278 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
9279 line_end++;
9280 line_end[0] = 0;
9281 if (line[0] == ';')
9282 continue;
9283 if (line[0] == '[' && line_end > line && line_end[-1] == ']')
9284 {
9285 // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
9286 line_end[-1] = 0;
9287 const char* name_end = line_end - 1;
9288 const char* type_start = line + 1;
9289 char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']');
9290 const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
9291 if (!type_end || !name_start)
9292 {
9293 name_start = type_start; // Import legacy entries that have no type
9294 type_start = "Window";
9295 }
9296 else
9297 {
9298 *type_end = 0; // Overwrite first ']'
9299 name_start++; // Skip second '['
9300 }
9301 entry_handler = FindSettingsHandler(type_start);
9302 entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
9303 }
9304 else if (entry_handler != NULL && entry_data != NULL)
9305 {
9306 // Let type handler parse the line
9307 entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
9308 }
9309 }
9310 IM_FREE(buf);
9311 g.SettingsLoaded = true;
9312 }
9313
SaveIniSettingsToDisk(const char * ini_filename)9314 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
9315 {
9316 ImGuiContext& g = *GImGui;
9317 g.SettingsDirtyTimer = 0.0f;
9318 if (!ini_filename)
9319 return;
9320
9321 size_t ini_data_size = 0;
9322 const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
9323 FILE* f = ImFileOpen(ini_filename, "wt");
9324 if (!f)
9325 return;
9326 fwrite(ini_data, sizeof(char), ini_data_size, f);
9327 fclose(f);
9328 }
9329
9330 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)9331 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
9332 {
9333 ImGuiContext& g = *GImGui;
9334 g.SettingsDirtyTimer = 0.0f;
9335 g.SettingsIniData.Buf.resize(0);
9336 g.SettingsIniData.Buf.push_back(0);
9337 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9338 {
9339 ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
9340 handler->WriteAllFn(&g, handler, &g.SettingsIniData);
9341 }
9342 if (out_size)
9343 *out_size = (size_t)g.SettingsIniData.size();
9344 return g.SettingsIniData.c_str();
9345 }
9346
SettingsHandlerWindow_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)9347 static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
9348 {
9349 ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHashStr(name));
9350 if (!settings)
9351 settings = ImGui::CreateNewWindowSettings(name);
9352 return (void*)settings;
9353 }
9354
SettingsHandlerWindow_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)9355 static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
9356 {
9357 ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
9358 int x, y;
9359 int i;
9360 if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) settings->Pos = ImVec2ih((short)x, (short)y);
9361 else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) settings->Size = ImVec2ih((short)x, (short)y);
9362 else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0);
9363 }
9364
SettingsHandlerWindow_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)9365 static void SettingsHandlerWindow_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
9366 {
9367 // Gather data from windows that were active during this session
9368 // (if a window wasn't opened in this session we preserve its settings)
9369 ImGuiContext& g = *ctx;
9370 for (int i = 0; i != g.Windows.Size; i++)
9371 {
9372 ImGuiWindow* window = g.Windows[i];
9373 if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
9374 continue;
9375
9376 ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID);
9377 if (!settings)
9378 {
9379 settings = ImGui::CreateNewWindowSettings(window->Name);
9380 window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings);
9381 }
9382 IM_ASSERT(settings->ID == window->ID);
9383 settings->Pos = ImVec2ih((short)window->Pos.x, (short)window->Pos.y);
9384 settings->Size = ImVec2ih((short)window->SizeFull.x, (short)window->SizeFull.y);
9385 settings->Collapsed = window->Collapsed;
9386 }
9387
9388 // Write to text buffer
9389 buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve
9390 for (int i = 0; i != g.SettingsWindows.Size; i++)
9391 {
9392 const ImGuiWindowSettings* settings = &g.SettingsWindows[i];
9393 const char* settings_name = g.SettingsWindowsNames.c_str() + settings->NameOffset;
9394 buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
9395 buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
9396 buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
9397 buf->appendf("Collapsed=%d\n", settings->Collapsed);
9398 buf->appendf("\n");
9399 }
9400 }
9401
9402
9403 //-----------------------------------------------------------------------------
9404 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
9405 //-----------------------------------------------------------------------------
9406
9407 // (this section is filled in the 'docking' branch)
9408
9409
9410 //-----------------------------------------------------------------------------
9411 // [SECTION] DOCKING
9412 //-----------------------------------------------------------------------------
9413
9414 // (this section is filled in the 'docking' branch)
9415
9416
9417 //-----------------------------------------------------------------------------
9418 // [SECTION] PLATFORM DEPENDENT HELPERS
9419 //-----------------------------------------------------------------------------
9420
9421 #if defined(_WIN32) && !defined(_WINDOWS_) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
9422 #ifndef WIN32_LEAN_AND_MEAN
9423 #define WIN32_LEAN_AND_MEAN
9424 #endif
9425 #ifndef __MINGW32__
9426 #include <Windows.h>
9427 #else
9428 #include <windows.h>
9429 #endif
9430 #elif defined(__APPLE__)
9431 #include <TargetConditionals.h>
9432 #endif
9433
9434 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
9435
9436 #ifdef _MSC_VER
9437 #pragma comment(lib, "user32")
9438 #endif
9439
9440 // Win32 clipboard implementation
GetClipboardTextFn_DefaultImpl(void *)9441 static const char* GetClipboardTextFn_DefaultImpl(void*)
9442 {
9443 static ImVector<char> buf_local;
9444 buf_local.clear();
9445 if (!::OpenClipboard(NULL))
9446 return NULL;
9447 HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
9448 if (wbuf_handle == NULL)
9449 {
9450 ::CloseClipboard();
9451 return NULL;
9452 }
9453 if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle))
9454 {
9455 int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
9456 buf_local.resize(buf_len);
9457 ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
9458 }
9459 ::GlobalUnlock(wbuf_handle);
9460 ::CloseClipboard();
9461 return buf_local.Data;
9462 }
9463
SetClipboardTextFn_DefaultImpl(void *,const char * text)9464 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9465 {
9466 if (!::OpenClipboard(NULL))
9467 return;
9468 const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
9469 HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
9470 if (wbuf_handle == NULL)
9471 {
9472 ::CloseClipboard();
9473 return;
9474 }
9475 ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle);
9476 ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
9477 ::GlobalUnlock(wbuf_handle);
9478 ::EmptyClipboard();
9479 if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
9480 ::GlobalFree(wbuf_handle);
9481 ::CloseClipboard();
9482 }
9483
9484 #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
9485
9486 #include <Carbon/Carbon.h> // Use old API to avoid need for separate .mm file
9487 static PasteboardRef main_clipboard = 0;
9488
9489 // OSX clipboard implementation
9490 // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
SetClipboardTextFn_DefaultImpl(void *,const char * text)9491 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9492 {
9493 if (!main_clipboard)
9494 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
9495 PasteboardClear(main_clipboard);
9496 CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
9497 if (cf_data)
9498 {
9499 PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
9500 CFRelease(cf_data);
9501 }
9502 }
9503
GetClipboardTextFn_DefaultImpl(void *)9504 static const char* GetClipboardTextFn_DefaultImpl(void*)
9505 {
9506 if (!main_clipboard)
9507 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
9508 PasteboardSynchronize(main_clipboard);
9509
9510 ItemCount item_count = 0;
9511 PasteboardGetItemCount(main_clipboard, &item_count);
9512 for (ItemCount i = 0; i < item_count; i++)
9513 {
9514 PasteboardItemID item_id = 0;
9515 PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
9516 CFArrayRef flavor_type_array = 0;
9517 PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
9518 for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
9519 {
9520 CFDataRef cf_data;
9521 if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
9522 {
9523 static ImVector<char> clipboard_text;
9524 int length = (int)CFDataGetLength(cf_data);
9525 clipboard_text.resize(length + 1);
9526 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)clipboard_text.Data);
9527 clipboard_text[length] = 0;
9528 CFRelease(cf_data);
9529 return clipboard_text.Data;
9530 }
9531 }
9532 }
9533 return NULL;
9534 }
9535
9536 #else
9537
9538 // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
GetClipboardTextFn_DefaultImpl(void *)9539 static const char* GetClipboardTextFn_DefaultImpl(void*)
9540 {
9541 ImGuiContext& g = *GImGui;
9542 return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
9543 }
9544
SetClipboardTextFn_DefaultImpl(void *,const char * text)9545 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9546 {
9547 ImGuiContext& g = *GImGui;
9548 g.PrivateClipboard.clear();
9549 const char* text_end = text + strlen(text);
9550 g.PrivateClipboard.resize((int)(text_end - text) + 1);
9551 memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
9552 g.PrivateClipboard[(int)(text_end - text)] = 0;
9553 }
9554
9555 #endif
9556
9557 // Win32 API IME support (for Asian languages, etc.)
9558 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
9559
9560 #include <imm.h>
9561 #ifdef _MSC_VER
9562 #pragma comment(lib, "imm32")
9563 #endif
9564
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)9565 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
9566 {
9567 // Notify OS Input Method Editor of text input position
9568 ImGuiIO& io = ImGui::GetIO();
9569 if (HWND hwnd = (HWND)io.ImeWindowHandle)
9570 if (HIMC himc = ::ImmGetContext(hwnd))
9571 {
9572 COMPOSITIONFORM cf;
9573 cf.ptCurrentPos.x = x;
9574 cf.ptCurrentPos.y = y;
9575 cf.dwStyle = CFS_FORCE_POSITION;
9576 ::ImmSetCompositionWindow(himc, &cf);
9577 ::ImmReleaseContext(hwnd, himc);
9578 }
9579 }
9580
9581 #else
9582
ImeSetInputScreenPosFn_DefaultImpl(int,int)9583 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
9584
9585 #endif
9586
9587 //-----------------------------------------------------------------------------
9588 // [SECTION] METRICS/DEBUG WINDOW
9589 //-----------------------------------------------------------------------------
9590
9591 #ifndef IMGUI_DISABLE_METRICS_WINDOW
ShowMetricsWindow(bool * p_open)9592 void ImGui::ShowMetricsWindow(bool* p_open)
9593 {
9594 if (!ImGui::Begin("Dear ImGui Metrics", p_open))
9595 {
9596 ImGui::End();
9597 return;
9598 }
9599
9600 // State
9601 enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
9602 const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentRegionRect" };
9603 static bool show_windows_rects = false;
9604 static int show_windows_rect_type = WRT_WorkRect;
9605 static bool show_windows_begin_order = false;
9606 static bool show_drawcmd_clip_rects = true;
9607
9608 // Basic info
9609 ImGuiContext& g = *GImGui;
9610 ImGuiIO& io = ImGui::GetIO();
9611 ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
9612 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
9613 ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
9614 ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
9615 ImGui::Text("%d active allocations", io.MetricsActiveAllocations);
9616 ImGui::Separator();
9617
9618 // Helper functions to display common structures:
9619 // - NodeDrawList
9620 // - NodeColumns
9621 // - NodeWindow
9622 // - NodeWindows
9623 // - NodeTabBar
9624 struct Funcs
9625 {
9626 static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
9627 {
9628 if (rect_type == WRT_OuterRect) { return window->Rect(); }
9629 else if (rect_type == WRT_OuterRectClipped) { return window->OuterRectClipped; }
9630 else if (rect_type == WRT_InnerRect) { return window->InnerRect; }
9631 else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; }
9632 else if (rect_type == WRT_WorkRect) { return window->WorkRect; }
9633 else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
9634 else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; }
9635 IM_ASSERT(0);
9636 return ImRect();
9637 }
9638
9639 static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
9640 {
9641 bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size);
9642 if (draw_list == ImGui::GetWindowDrawList())
9643 {
9644 ImGui::SameLine();
9645 ImGui::TextColored(ImVec4(1.0f,0.4f,0.4f,1.0f), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
9646 if (node_open) ImGui::TreePop();
9647 return;
9648 }
9649
9650 ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
9651 if (window && IsItemHovered())
9652 fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
9653 if (!node_open)
9654 return;
9655
9656 if (window && !window->WasActive)
9657 ImGui::Text("(Note: owning Window is inactive: DrawList is not being rendered!)");
9658
9659 int elem_offset = 0;
9660 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
9661 {
9662 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
9663 continue;
9664 if (pcmd->UserCallback)
9665 {
9666 ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
9667 continue;
9668 }
9669 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
9670 char buf[300];
9671 ImFormatString(buf, IM_ARRAYSIZE(buf), "Draw %4d triangles, tex 0x%p, clip_rect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
9672 pcmd->ElemCount/3, (void*)(intptr_t)pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
9673 bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
9674 if (show_drawcmd_clip_rects && fg_draw_list && ImGui::IsItemHovered())
9675 {
9676 ImRect clip_rect = pcmd->ClipRect;
9677 ImRect vtxs_rect;
9678 for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
9679 vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
9680 clip_rect.Floor(); fg_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,0,255,255));
9681 vtxs_rect.Floor(); fg_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,255,0,255));
9682 }
9683 if (!pcmd_node_open)
9684 continue;
9685
9686 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
9687 ImGui::Text("ElemCount: %d, ElemCount/3: %d, VtxOffset: +%d, IdxOffset: +%d", pcmd->ElemCount, pcmd->ElemCount/3, pcmd->VtxOffset, pcmd->IdxOffset);
9688 ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
9689 while (clipper.Step())
9690 for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
9691 {
9692 char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
9693 ImVec2 triangles_pos[3];
9694 for (int n = 0; n < 3; n++, idx_i++)
9695 {
9696 int vtx_i = idx_buffer ? idx_buffer[idx_i] : idx_i;
9697 ImDrawVert& v = draw_list->VtxBuffer[vtx_i];
9698 triangles_pos[n] = v.pos;
9699 buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
9700 (n == 0) ? "elem" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
9701 }
9702 ImGui::Selectable(buf, false);
9703 if (fg_draw_list && ImGui::IsItemHovered())
9704 {
9705 ImDrawListFlags backup_flags = fg_draw_list->Flags;
9706 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles.
9707 fg_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f);
9708 fg_draw_list->Flags = backup_flags;
9709 }
9710 }
9711 ImGui::TreePop();
9712 }
9713 ImGui::TreePop();
9714 }
9715
9716 static void NodeColumns(const ImGuiColumns* columns)
9717 {
9718 if (!ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
9719 return;
9720 ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
9721 for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
9722 ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm));
9723 ImGui::TreePop();
9724 }
9725
9726 static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
9727 {
9728 if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
9729 return;
9730 for (int i = 0; i < windows.Size; i++)
9731 Funcs::NodeWindow(windows[i], "Window");
9732 ImGui::TreePop();
9733 }
9734
9735 static void NodeWindow(ImGuiWindow* window, const char* label)
9736 {
9737 if (window == NULL)
9738 {
9739 ImGui::BulletText("%s: NULL", label);
9740 return;
9741 }
9742 if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, (window->Active || window->WasActive), window))
9743 return;
9744 ImGuiWindowFlags flags = window->Flags;
9745 NodeDrawList(window, window->DrawList, "DrawList");
9746 ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y);
9747 ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
9748 (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
9749 (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
9750 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
9751 ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y);
9752 ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
9753 ImGui::BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
9754 ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
9755 ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
9756 if (!window->NavRectRel[0].IsInverted())
9757 ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y);
9758 else
9759 ImGui::BulletText("NavRectRel[0]: <None>");
9760 if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
9761 if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow");
9762 if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
9763 if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
9764 {
9765 for (int n = 0; n < window->ColumnsStorage.Size; n++)
9766 NodeColumns(&window->ColumnsStorage[n]);
9767 ImGui::TreePop();
9768 }
9769 NodeStorage(&window->StateStorage, "Storage");
9770 ImGui::TreePop();
9771 }
9772
9773 static void NodeTabBar(ImGuiTabBar* tab_bar)
9774 {
9775 // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
9776 char buf[256];
9777 char* p = buf;
9778 const char* buf_end = buf + IM_ARRAYSIZE(buf);
9779 ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : "");
9780 if (ImGui::TreeNode(tab_bar, "%s", buf))
9781 {
9782 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
9783 {
9784 const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
9785 ImGui::PushID(tab);
9786 if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2);
9787 if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine();
9788 ImGui::Text("%02d%c Tab 0x%08X", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID);
9789 ImGui::PopID();
9790 }
9791 ImGui::TreePop();
9792 }
9793 }
9794
9795 static void NodeStorage(ImGuiStorage* storage, const char* label)
9796 {
9797 if (!ImGui::TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
9798 return;
9799 for (int n = 0; n < storage->Data.Size; n++)
9800 {
9801 const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n];
9802 ImGui::BulletText("Key 0x%08X Value { i: %d }", p.key, p.val_i); // Important: we currently don't store a type, real value may not be integer.
9803 }
9804 ImGui::TreePop();
9805 }
9806 };
9807
9808 Funcs::NodeWindows(g.Windows, "Windows");
9809 if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
9810 {
9811 for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
9812 Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
9813 ImGui::TreePop();
9814 }
9815
9816 if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
9817 {
9818 for (int i = 0; i < g.OpenPopupStack.Size; i++)
9819 {
9820 ImGuiWindow* window = g.OpenPopupStack[i].Window;
9821 ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : "");
9822 }
9823 ImGui::TreePop();
9824 }
9825
9826 if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.Data.Size))
9827 {
9828 for (int n = 0; n < g.TabBars.Data.Size; n++)
9829 Funcs::NodeTabBar(g.TabBars.GetByIndex(n));
9830 ImGui::TreePop();
9831 }
9832
9833 #if 0
9834 if (ImGui::TreeNode("Docking"))
9835 {
9836 ImGui::TreePop();
9837 }
9838 #endif
9839
9840 #if 0
9841 if (ImGui::TreeNode("Tables", "Tables (%d)", g.Tables.Data.Size))
9842 {
9843 ImGui::TreePop();
9844 }
9845 #endif
9846
9847 if (ImGui::TreeNode("Internal state"))
9848 {
9849 const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
9850 ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
9851 ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
9852 ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
9853 ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]);
9854 ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
9855 ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
9856 ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
9857 ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
9858 ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
9859 ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
9860 ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
9861 ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
9862 ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
9863 ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
9864 ImGui::TreePop();
9865 }
9866
9867 if (ImGui::TreeNode("Tools"))
9868 {
9869 // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
9870 if (ImGui::Button("Item Picker.."))
9871 ImGui::DebugStartItemPicker();
9872
9873 ImGui::Checkbox("Show windows begin order", &show_windows_begin_order);
9874 ImGui::Checkbox("Show windows rectangles", &show_windows_rects);
9875 ImGui::SameLine();
9876 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 12);
9877 show_windows_rects |= ImGui::Combo("##show_windows_rect_type", &show_windows_rect_type, wrt_rects_names, WRT_Count);
9878 if (show_windows_rects && g.NavWindow)
9879 {
9880 ImGui::BulletText("'%s':", g.NavWindow->Name);
9881 ImGui::Indent();
9882 for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
9883 {
9884 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
9885 ImGui::Text("(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), wrt_rects_names[rect_n]);
9886 }
9887 ImGui::Unindent();
9888 }
9889 ImGui::Checkbox("Show clipping rectangle when hovering ImDrawCmd node", &show_drawcmd_clip_rects);
9890 ImGui::TreePop();
9891 }
9892
9893 // Tool: Display windows Rectangles and Begin Order
9894 if (show_windows_rects || show_windows_begin_order)
9895 {
9896 for (int n = 0; n < g.Windows.Size; n++)
9897 {
9898 ImGuiWindow* window = g.Windows[n];
9899 if (!window->WasActive)
9900 continue;
9901 ImDrawList* draw_list = GetForegroundDrawList(window);
9902 if (show_windows_rects)
9903 {
9904 ImRect r = Funcs::GetWindowRect(window, show_windows_rect_type);
9905 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
9906 }
9907 if (show_windows_begin_order && !(window->Flags & ImGuiWindowFlags_ChildWindow))
9908 {
9909 char buf[32];
9910 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
9911 float font_size = ImGui::GetFontSize();
9912 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
9913 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
9914 }
9915 }
9916 }
9917 ImGui::End();
9918 }
9919
9920 #else
9921
ShowMetricsWindow(bool *)9922 void ImGui::ShowMetricsWindow(bool*) { }
9923
9924 #endif
9925
9926 //-----------------------------------------------------------------------------
9927
9928 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
9929 // 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.
9930 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
9931 #include "imgui_user.inl"
9932 #endif
9933
9934 //-----------------------------------------------------------------------------
9935