1 // dear imgui, v1.80
2 // (main code and documentation)
3
4 // Help:
5 // - Read FAQ at http://dearimgui.org/faq
6 // - Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
7 // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
8 // Read imgui.cpp for details, links and comments.
9
10 // Resources:
11 // - FAQ http://dearimgui.org/faq
12 // - Homepage & latest https://github.com/ocornut/imgui
13 // - Releases & changelog https://github.com/ocornut/imgui/releases
14 // - Gallery https://github.com/ocornut/imgui/issues/3488 (please post your screenshots/video there!)
15 // - Glossary https://github.com/ocornut/imgui/wiki/Glossary
16 // - Wiki https://github.com/ocornut/imgui/wiki
17 // - Issues & support https://github.com/ocornut/imgui/issues
18
19 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
20 // See LICENSE.txt for copyright and licensing details (standard MIT License).
21 // This library is free but needs your support to sustain development and maintenance.
22 // Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.org".
23 // Individuals: you can support continued development via donations. See docs/README or web page.
24
25 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
26 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
27 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
28 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
29 // to a better solution or official support for them.
30
31 /*
32
33 Index of this file:
34
35 DOCUMENTATION
36
37 - MISSION STATEMENT
38 - END-USER GUIDE
39 - PROGRAMMER GUIDE
40 - READ FIRST
41 - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
42 - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
43 - HOW A SIMPLE APPLICATION MAY LOOK LIKE
44 - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
45 - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
46 - API BREAKING CHANGES (read me when you update!)
47 - FREQUENTLY ASKED QUESTIONS (FAQ)
48 - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
49
50 CODE
51 (search for "[SECTION]" in the code to find them)
52
53 // [SECTION] INCLUDES
54 // [SECTION] FORWARD DECLARATIONS
55 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
56 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
57 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
58 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
59 // [SECTION] MISC HELPERS/UTILITIES (File functions)
60 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
61 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
62 // [SECTION] ImGuiStorage
63 // [SECTION] ImGuiTextFilter
64 // [SECTION] ImGuiTextBuffer
65 // [SECTION] ImGuiListClipper
66 // [SECTION] STYLING
67 // [SECTION] RENDER HELPERS
68 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
69 // [SECTION] ERROR CHECKING
70 // [SECTION] LAYOUT
71 // [SECTION] SCROLLING
72 // [SECTION] TOOLTIPS
73 // [SECTION] POPUPS
74 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
75 // [SECTION] DRAG AND DROP
76 // [SECTION] LOGGING/CAPTURING
77 // [SECTION] SETTINGS
78 // [SECTION] PLATFORM DEPENDENT HELPERS
79 // [SECTION] METRICS/DEBUGGER WINDOW
80
81 */
82
83 //-----------------------------------------------------------------------------
84 // DOCUMENTATION
85 //-----------------------------------------------------------------------------
86
87 /*
88
89 MISSION STATEMENT
90 =================
91
92 - Easy to use to create code-driven and data-driven tools.
93 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
94 - Easy to hack and improve.
95 - Minimize setup and maintenance.
96 - Minimize state storage on user side.
97 - Portable, minimize dependencies, run on target (consoles, phones, etc.).
98 - Efficient runtime and memory consumption.
99
100 Designed for developers and content-creators, not the typical end-user! Some of the current weaknesses includes:
101
102 - Doesn't look fancy, doesn't animate.
103 - Limited layout features, intricate layouts are typically crafted in code.
104
105
106 END-USER GUIDE
107 ==============
108
109 - Double-click on title bar to collapse window.
110 - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
111 - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
112 - Click and drag on any empty space to move window.
113 - TAB/SHIFT+TAB to cycle through keyboard editable fields.
114 - CTRL+Click on a slider or drag box to input value as text.
115 - Use mouse wheel to scroll.
116 - Text editor:
117 - Hold SHIFT or use mouse to select text.
118 - CTRL+Left/Right to word jump.
119 - CTRL+Shift+Left/Right to select words.
120 - CTRL+A our Double-Click to select all.
121 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
122 - CTRL+Z,CTRL+Y to undo/redo.
123 - ESCAPE to revert text to its original value.
124 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
125 - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
126 - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
127 - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://dearimgui.org/controls_sheets
128
129
130 PROGRAMMER GUIDE
131 ================
132
133 READ FIRST
134 ----------
135 - Remember to read the FAQ (https://www.dearimgui.org/faq)
136 - 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
137 or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs.
138 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
139 - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
140 - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
141 You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in the FAQ.
142 - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
143 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,
144 where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
145 - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
146 - This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
147 - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
148 If you get an assert, read the messages and comments around the assert.
149 - 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.
150 - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
151 See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
152 However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
153 - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
154
155
156 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
157 ----------------------------------------------
158 - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
159 - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over master.
160 - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file.
161 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
162 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
163 from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
164 likely be a comment about it. Please report any issue to the GitHub page!
165 - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file.
166 - Try to keep your copy of Dear ImGui reasonably up to date.
167
168
169 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
170 ---------------------------------------------------------------
171 - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
172 - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder.
173 - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system.
174 It is recommended you build and statically link the .cpp files as part of your project and NOT as shared library (DLL).
175 - 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.
176 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
177 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
178 Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
179 phases of your own application. All rendering information are stored into command-lists that you will retrieve after calling ImGui::Render().
180 - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code.
181 - 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.
182
183
184 HOW A SIMPLE APPLICATION MAY LOOK LIKE
185 --------------------------------------
186 EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder).
187 The sub-folders in examples/ contains examples applications following this structure.
188
189 // Application init: create a dear imgui context, setup some options, load fonts
190 ImGui::CreateContext();
191 ImGuiIO& io = ImGui::GetIO();
192 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
193 // TODO: Fill optional fields of the io structure later.
194 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
195
196 // Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
197 ImGui_ImplWin32_Init(hwnd);
198 ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
199
200 // Application main loop
201 while (true)
202 {
203 // Feed inputs to dear imgui, start new frame
204 ImGui_ImplDX11_NewFrame();
205 ImGui_ImplWin32_NewFrame();
206 ImGui::NewFrame();
207
208 // Any application code here
209 ImGui::Text("Hello, world!");
210
211 // Render dear imgui into screen
212 ImGui::Render();
213 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
214 g_pSwapChain->Present(1, 0);
215 }
216
217 // Shutdown
218 ImGui_ImplDX11_Shutdown();
219 ImGui_ImplWin32_Shutdown();
220 ImGui::DestroyContext();
221
222 EXHIBIT 2: IMPLEMENTING CUSTOM BACKEND / CUSTOM ENGINE
223
224 // Application init: create a dear imgui context, setup some options, load fonts
225 ImGui::CreateContext();
226 ImGuiIO& io = ImGui::GetIO();
227 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
228 // TODO: Fill optional fields of the io structure later.
229 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
230
231 // Build and load the texture atlas into a texture
232 // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
233 int width, height;
234 unsigned char* pixels = NULL;
235 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
236
237 // At this point you've got the texture data and you need to upload that your your graphic system:
238 // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
239 // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
240 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
241 io.Fonts->TexID = (void*)texture;
242
243 // Application main loop
244 while (true)
245 {
246 // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
247 // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends)
248 io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds)
249 io.DisplaySize.x = 1920.0f; // set the current display width
250 io.DisplaySize.y = 1280.0f; // set the current display height here
251 io.MousePos = my_mouse_pos; // set the mouse position
252 io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states
253 io.MouseDown[1] = my_mouse_buttons[1];
254
255 // Call NewFrame(), after this point you can use ImGui::* functions anytime
256 // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use Dear ImGui everywhere)
257 ImGui::NewFrame();
258
259 // Most of your application code here
260 ImGui::Text("Hello, world!");
261 MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
262 MyGameRender(); // may use any Dear ImGui functions as well!
263
264 // Render dear imgui, swap buffers
265 // (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)
266 ImGui::EndFrame();
267 ImGui::Render();
268 ImDrawData* draw_data = ImGui::GetDrawData();
269 MyImGuiRenderFunction(draw_data);
270 SwapBuffers();
271 }
272
273 // Shutdown
274 ImGui::DestroyContext();
275
276 To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest your application,
277 you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
278 Please read the FAQ and example applications for details about this!
279
280
281 HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
282 ---------------------------------------------
283 The backends in impl_impl_XXX.cpp files contains many working implementations of a rendering function.
284
285 void void MyImGuiRenderFunction(ImDrawData* draw_data)
286 {
287 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
288 // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
289 // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
290 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
291 for (int n = 0; n < draw_data->CmdListsCount; n++)
292 {
293 const ImDrawList* cmd_list = draw_data->CmdLists[n];
294 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by Dear ImGui
295 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by Dear ImGui
296 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
297 {
298 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
299 if (pcmd->UserCallback)
300 {
301 pcmd->UserCallback(cmd_list, pcmd);
302 }
303 else
304 {
305 // The texture for the draw call is specified by pcmd->TextureId.
306 // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
307 MyEngineBindTexture((MyTexture*)pcmd->TextureId);
308
309 // We are using scissoring to clip some objects. All low-level graphics API should supports it.
310 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
311 // (some elements visible outside their bounds) but you can fix that once everything else works!
312 // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
313 // In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
314 // However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
315 // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
316 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
317 ImVec2 pos = draw_data->DisplayPos;
318 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));
319
320 // Render 'pcmd->ElemCount/3' indexed triangles.
321 // By default the indices ImDrawIdx are 16-bit, you can change them to 32-bit in imconfig.h if your engine doesn't support 16-bit indices.
322 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
323 }
324 idx_buffer += pcmd->ElemCount;
325 }
326 }
327 }
328
329
330 USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
331 ------------------------------------------
332 - The gamepad/keyboard navigation is fairly functional and keeps being improved.
333 - Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PS4, Switch, XB1) without a mouse!
334 - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
335 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
336 - Keyboard:
337 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
338 NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
339 - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
340 will be set. For more advanced uses, you may want to read from:
341 - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
342 - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
343 - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
344 Please reach out if you think the game vs navigation input sharing could be improved.
345 - Gamepad:
346 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
347 - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
348 Note that io.NavInputs[] is cleared by EndFrame().
349 - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
350 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
351 - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
352 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.).
353 - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://dearimgui.org/controls_sheets
354 - 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
355 to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
356 - Mouse:
357 - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
358 - 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.
359 - 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.
360 Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
361 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.
362 When that happens your backend NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the backends in examples/ do that.
363 (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!)
364 (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
365 to set a boolean to ignore your other external mouse positions until the external source is moved again.)
366
367
368 API BREAKING CHANGES
369 ====================
370
371 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
372 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.
373 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.
374 You can read releases logs https://github.com/ocornut/imgui/releases for more details.
375
376 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018):
377 - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit().
378 - ImGuiCol_ModalWindowDarkening -> use ImGuiCol_ModalWindowDimBg
379 - ImGuiInputTextCallback -> use ImGuiTextEditCallback
380 - ImGuiInputTextCallbackData -> use ImGuiTextEditCallbackData
381 - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete).
382 - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added!
383 - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API.
384 - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures
385 - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/.
386 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018):
387 - io.RenderDrawListsFn pointer -> use ImGui::GetDrawData() value and call the render function of your backend
388 - ImGui::IsAnyWindowFocused() -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)
389 - ImGui::IsAnyWindowHovered() -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
390 - ImGuiStyleVar_Count_ -> use ImGuiStyleVar_COUNT
391 - ImGuiMouseCursor_Count_ -> use ImGuiMouseCursor_COUNT
392 - removed redirecting functions names that were marked obsolete in 1.61 (May 2018):
393 - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision.
394 - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter.
395 - 2020/10/05 (1.79) - removed ImGuiListClipper: Renamed constructor parameters which created an ambiguous alternative to using the ImGuiListClipper::Begin() function, with misleading edge cases (note: imgui_memory_editor <0.40 from imgui_club/ used this old clipper API. Update your copy if needed).
396 - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently).
397 - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton.
398 - 2020/09/21 (1.79) - renamed OpenPopupContextItem() back to OpenPopupOnItemClick(), reverting the change from 1.77. For varieties of reason this is more self-explanatory.
399 - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result.
400 - 2020/09/17 (1.79) - removed ImFont::DisplayOffset in favor of ImFontConfig::GlyphOffset. DisplayOffset was applied after scaling and not very meaningful/useful outside of being needed by the default ProggyClean font. If you scaled this value after calling AddFontDefault(), this is now done automatically. It was also getting in the way of better font scaling, so let's get rid of it now!
401 - 2020/08/17 (1.78) - obsoleted use of the trailing 'float power=1.0f' parameter for DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(), DragFloatRange2(), DragScalar(), DragScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(), SliderScalar(), SliderScalarN(), VSliderFloat() and VSliderScalar().
402 replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
403 worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
404 - if you omitted the 'power' parameter (likely!), you are not affected.
405 - if you set the 'power' parameter to 1.0f (same as previous default value): 1/ your compiler may warn on float>int conversion, 2/ everything else will work. 3/ you can replace the 1.0f value with 0 to fix the warning, and be technically correct.
406 - if you set the 'power' parameter to >1.0f (to enable non-linear editing): 1/ your compiler may warn on float>int conversion, 2/ code will assert at runtime, 3/ in case asserts are disabled, the code will not crash and enable the _Logarithmic flag. 4/ you can replace the >1.0f value with ImGuiSliderFlags_Logarithmic to fix the warning/assert and get a _similar_ effect as previous uses of power >1.0f.
407 see https://github.com/ocornut/imgui/issues/3361 for all details.
408 kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version were removed directly as they were most unlikely ever used.
409 for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.
410 - obsoleted use of v_min > v_max in DragInt, DragFloat, DragScalar to lock edits (introduced in 1.73, was not demoed nor documented very), will be replaced by a more generic ReadOnly feature. You may use the ImGuiSliderFlags_ReadOnly internal flag in the meantime.
411 - 2020/06/23 (1.77) - removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor of BeginPopupContextWindow(const char*, ImGuiPopupFlags flags) with ImGuiPopupFlags_NoOverItems.
412 - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79]
413 - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
414 - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
415 - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
416 - 2019/12/17 (1.75) - [undid this change in 1.76] made Columns() limited to 64 columns by asserting above that limit. While the current code technically supports it, future code may not so we're putting the restriction ahead.
417 - 2019/12/13 (1.75) - [imgui_internal.h] changed ImRect() default constructor initializes all fields to 0.0f instead of (FLT_MAX,FLT_MAX,-FLT_MAX,-FLT_MAX). If you used ImRect::Add() to create bounding boxes by adding multiple points into it, you may need to fix your initial value.
418 - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
419 - ShowTestWindow() -> use ShowDemoWindow()
420 - IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
421 - IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
422 - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
423 - GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing()
424 - ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg
425 - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding
426 - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
427 - IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS
428 - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was the vaguely documented and rarely if ever used). Instead we added an explicit PrimUnreserve() API.
429 - 2019/12/06 (1.75) - removed implicit default parameter to IsMouseDragging(int button = 0) to be consistent with other mouse functions (none of the other functions have it).
430 - 2019/11/21 (1.74) - ImFontAtlas::AddCustomRectRegular() now requires an ID larger than 0x110000 (instead of 0x10000) to conform with supporting Unicode planes 1-16 in a future update. ID below 0x110000 will now assert.
431 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
432 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
433 - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
434 - Begin() [old 5 args version] -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
435 - IsRootWindowOrAnyChildHovered() -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
436 - AlignFirstTextHeightToWidgets() -> use AlignTextToFramePadding()
437 - SetNextWindowPosCenter() -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
438 - ImFont::Glyph -> use ImFontGlyph
439 - 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.
440 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.
441 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).
442 If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
443 - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
444 - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
445 - 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.
446 - 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
447 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.
448 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.
449 Please reach out if you are affected.
450 - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
451 - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
452 - 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.
453 - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
454 - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
455 - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
456 - 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 an arbitrary small value!
457 - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
458 - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
459 - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
460 - 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.
461 - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
462 - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
463 - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
464 - 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.
465 If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
466 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
467 - 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.
468 NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
469 Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
470 - 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).
471 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
472 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
473 - 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.
474 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
475 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
476 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
477 - 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.).
478 old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports.
479 when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call.
480 in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
481 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
482 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
483 - 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.
484 If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
485 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.
486 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.
487 - 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",
488 consistent with other functions. Kept redirection functions (will obsolete).
489 - 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.
490 - 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 backend ahead of merging the Nav branch).
491 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
492 - 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.
493 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
494 - 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.
495 - 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.
496 - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
497 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
498 - removed Shutdown() function, as DestroyContext() serve this purpose.
499 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
500 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
501 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
502 - 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.
503 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
504 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
505 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
506 - 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.
507 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
508 - 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
509 - 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.
510 - 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.
511 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
512 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
513 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
514 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
515 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
516 - 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.
517 - 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.
518 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.
519 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
520 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
521 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
522 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
523 - 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.
524 - 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.
525 - 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.
526 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
527 IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
528 IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
529 IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
530 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
531 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
532 - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
533 - 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).
534 - 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 backend if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
535 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
536 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
537 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
538 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
539 - 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.
540 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
541 - 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.
542 - 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).
543 - 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).
544 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
545 - 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.
546 - 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.
547 - 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))'
548 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
549 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
550 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
551 - 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().
552 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
553 - 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.
554 - 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.
555 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
556 If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
557 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:
558 ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) { float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a; 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); }
559 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.
560 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
561 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
562 - 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).
563 - 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.
564 - 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).
565 - 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)
566 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
567 - 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.
568 - 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.
569 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
570 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
571 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
572 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.
573 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!
574 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
575 - 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.
576 - 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
577 - 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.
578 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
579 - 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.
580 this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
581 - if you are using a vanilla copy of one of the imgui_impl_XXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
582 - the signature of the io.RenderDrawListsFn handler has changed!
583 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
584 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
585 parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
586 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
587 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
588 - 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.
589 - 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!
590 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
591 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
592 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
593 - 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.
594 - 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
595 - 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!
596 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
597 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
598 - 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.
599 - 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.
600 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
601 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
602 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
603 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
604 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
605 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
606 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
607 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
608 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
609 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
610 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
611 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
612 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
613 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
614 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
615 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
616 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
617 - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
618 - old: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
619 - new: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->TexId = YourTexIdentifier;
620 you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. It is now recommended that you sample the font texture with bilinear interpolation.
621 - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
622 - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
623 - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
624 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
625 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
626 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
627 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
628 - 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)
629 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
630 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
631 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
632 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
633 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
634 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
635
636
637 FREQUENTLY ASKED QUESTIONS (FAQ)
638 ================================
639
640 Read all answers online:
641 https://www.dearimgui.org/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
642 Read all answers locally (with a text editor or ideally a Markdown viewer):
643 docs/FAQ.md
644 Some answers are copied down here to facilitate searching in code.
645
646 Q&A: Basics
647 ===========
648
649 Q: Where is the documentation?
650 A: This library is poorly documented at the moment and expects of the user to be acquainted with C/C++.
651 - Run the examples/ and explore them.
652 - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
653 - The demo covers most features of Dear ImGui, so you can read the code and see its output.
654 - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
655 - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the
656 examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
657 - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
658 - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
659 - Your programming IDE is your friend, find the type or function declaration to find comments
660 associated to it.
661
662 Q: What is this library called?
663 Q: Which version should I get?
664 >> This library is called "Dear ImGui", please don't call it "ImGui" :)
665 >> See https://www.dearimgui.org/faq for details.
666
667 Q&A: Integration
668 ================
669
670 Q: How to get started?
671 A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
672
673 Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or to my application?
674 A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
675 >> See https://www.dearimgui.org/faq for fully detailed answer. You really want to read this.
676
677 Q. How can I enable keyboard controls?
678 Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
679 Q: I integrated Dear ImGui in my engine and little squares are showing instead of text..
680 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
681 Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries..
682 >> See https://www.dearimgui.org/faq
683
684 Q&A: Usage
685 ----------
686
687 Q: Why is my widget not reacting when I click on it?
688 Q: How can I have widgets with an empty label?
689 Q: How can I have multiple widgets with the same label?
690 Q: How can I display an image? What is ImTextureID, how does it works?
691 Q: How can I use my own math types instead of ImVec2/ImVec4?
692 Q: How can I interact with standard C++ types (such as std::string and std::vector)?
693 Q: How can I display custom shapes? (using low-level ImDrawList API)
694 >> See https://www.dearimgui.org/faq
695
696 Q&A: Fonts, Text
697 ================
698
699 Q: How should I handle DPI in my application?
700 Q: How can I load a different font than the default?
701 Q: How can I easily use icons in my application?
702 Q: How can I load multiple fonts?
703 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
704 >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/edit/master/docs/FONTS.md
705
706 Q&A: Concerns
707 =============
708
709 Q: Who uses Dear ImGui?
710 Q: Can you create elaborate/serious tools with Dear ImGui?
711 Q: Can you reskin the look of Dear ImGui?
712 Q: Why using C++ (as opposed to C)?
713 >> See https://www.dearimgui.org/faq
714
715 Q&A: Community
716 ==============
717
718 Q: How can I help?
719 A: - Businesses: please reach out to "contact AT dearimgui.org" if you work in a place using Dear ImGui!
720 We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
721 This is among the most useful thing you can do for Dear ImGui. With increased funding we can hire more people working on this project.
722 - Individuals: you can support continued development via PayPal donations. See README.
723 - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt
724 and see how you want to help and can help!
725 - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
726 You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/3488). Visuals are ideal as they inspire other programmers.
727 But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
728 - 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).
729
730 */
731
732 //-------------------------------------------------------------------------
733 // [SECTION] INCLUDES
734 //-------------------------------------------------------------------------
735
736 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
737 #define _CRT_SECURE_NO_WARNINGS
738 #endif
739
740 #include "imgui.h"
741 #ifndef IMGUI_DISABLE
742
743 #ifndef IMGUI_DEFINE_MATH_OPERATORS
744 #define IMGUI_DEFINE_MATH_OPERATORS
745 #endif
746 #include "imgui_internal.h"
747
748 // System includes
749 #include <ctype.h> // toupper
750 #include <stdio.h> // vsnprintf, sscanf, printf
751 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
752 #include <stddef.h> // intptr_t
753 #else
754 #include <stdint.h> // intptr_t
755 #endif
756
757 // [Windows] OS specific includes (optional)
758 #if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
759 #define IMGUI_DISABLE_WIN32_FUNCTIONS
760 #endif
761 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
762 #ifndef WIN32_LEAN_AND_MEAN
763 #define WIN32_LEAN_AND_MEAN
764 #endif
765 #ifndef NOMINMAX
766 #define NOMINMAX
767 #endif
768 #ifndef __MINGW32__
769 #include <Windows.h> // _wfopen, OpenClipboard
770 #else
771 #include <windows.h>
772 #endif
773 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have all Win32 functions
774 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
775 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
776 #endif
777 #endif
778
779 // [Apple] OS specific includes
780 #if defined(__APPLE__)
781 #include <TargetConditionals.h>
782 #endif
783
784 // Visual Studio warnings
785 #ifdef _MSC_VER
786 #pragma warning (disable: 4127) // condition expression is constant
787 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
788 #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
789 #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
790 #endif
791 #endif
792
793 // Clang/GCC warnings with -Weverything
794 #if defined(__clang__)
795 #if __has_warning("-Wunknown-warning-option")
796 #pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
797 #endif
798 #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
799 #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
800 #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.
801 #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.
802 #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.
803 #pragma clang diagnostic ignored "-Wglobal-constructors" // warning: declaration requires a global destructor // similar to above, not sure what the exact difference is.
804 #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
805 #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.
806 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type 'int'
807 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
808 #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.
809 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
810 #elif defined(__GNUC__)
811 // We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association.
812 #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
813 #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
814 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
815 #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
816 #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
817 #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
818 #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
819 #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
820 #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
821 #endif
822
823 // Debug options
824 #define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
825 #define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window
826 #define IMGUI_DEBUG_INI_SETTINGS 0 // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower)
827
828 // 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.
829 static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in
830 static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
831
832 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend)
833 static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow().
834 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
835 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 certain time, unless mouse moved.
836
837 //-------------------------------------------------------------------------
838 // [SECTION] FORWARD DECLARATIONS
839 //-------------------------------------------------------------------------
840
841 static void SetCurrentWindow(ImGuiWindow* window);
842 static void FindHoveredWindow();
843 static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags);
844 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
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 WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
853 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
854 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
855 static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
856 static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
857
858 // Platform Dependents default implementation for IO functions
859 static const char* GetClipboardTextFn_DefaultImpl(void* user_data);
860 static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
861 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
862
863 namespace ImGui
864 {
865 // Navigation
866 static void NavUpdate();
867 static void NavUpdateWindowing();
868 static void NavUpdateWindowingOverlay();
869 static void NavUpdateMoveResult();
870 static void NavUpdateInitResult();
871 static float NavUpdatePageUpPageDown();
872 static inline void NavUpdateAnyRequestFlag();
873 static void NavEndFrame();
874 static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand);
875 static void NavApplyItemToResult(ImGuiNavMoveResult* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel);
876 static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
877 static ImVec2 NavCalcPreferredRefPos();
878 static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
879 static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
880 static int FindWindowFocusIndex(ImGuiWindow* window);
881
882 // Error Checking
883 static void ErrorCheckNewFrameSanityChecks();
884 static void ErrorCheckEndFrameSanityChecks();
885
886 // Misc
887 static void UpdateSettings();
888 static void UpdateMouseInputs();
889 static void UpdateMouseWheel();
890 static void UpdateTabFocus();
891 static void UpdateDebugToolItemPicker();
892 static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
893 static void RenderWindowOuterBorders(ImGuiWindow* window);
894 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);
895 static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
896
897 }
898
899 //-----------------------------------------------------------------------------
900 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
901 //-----------------------------------------------------------------------------
902
903 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
904 // ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
905 // 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call
906 // SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading.
907 // In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into.
908 // 2) Important: Dear ImGui functions are not thread-safe because of this pointer.
909 // If you want thread-safety to allow N threads to access N different contexts, you can:
910 // - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h:
911 // struct ImGuiContext;
912 // extern thread_local ImGuiContext* MyImGuiTLS;
913 // #define GImGui MyImGuiTLS
914 // 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.
915 // - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
916 // - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace.
917 #ifndef GImGui
918 ImGuiContext* GImGui = NULL;
919 #endif
920
921 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
922 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
923 // 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.
924 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)925 static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); }
FreeWrapper(void * ptr,void * user_data)926 static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); }
927 #else
MallocWrapper(size_t size,void * user_data)928 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)929 static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
930 #endif
931
932 static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
933 static void (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
934 static void* GImAllocatorUserData = NULL;
935
936 //-----------------------------------------------------------------------------
937 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
938 //-----------------------------------------------------------------------------
939
ImGuiStyle()940 ImGuiStyle::ImGuiStyle()
941 {
942 Alpha = 1.0f; // Global alpha applies to everything in ImGui
943 WindowPadding = ImVec2(8,8); // Padding within a window
944 WindowRounding = 9.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.
945 WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
946 WindowMinSize = ImVec2(32,32); // Minimum window size
947 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
948 WindowMenuButtonPosition= ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
949 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
950 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
951 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
952 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
953 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
954 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
955 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
956 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
957 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
958 CellPadding = ImVec2(4,2); // Padding within a table cell
959 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!
960 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
961 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
962 ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
963 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
964 GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
965 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
966 LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
967 TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
968 TabBorderSize = 0.0f; // Thickness of border around tabs.
969 TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appears on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected.
970 ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
971 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
972 SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
973 DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows.
974 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.
975 MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
976 AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
977 AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering.
978 AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
979 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.
980 CircleSegmentMaxError = 1.60f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
981
982 // Default theme
983 ImGui::StyleColorsClassic(this);
984 }
985
986 // 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.
987 // 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)988 void ImGuiStyle::ScaleAllSizes(float scale_factor)
989 {
990 WindowPadding = ImFloor(WindowPadding * scale_factor);
991 WindowRounding = ImFloor(WindowRounding * scale_factor);
992 WindowMinSize = ImFloor(WindowMinSize * scale_factor);
993 ChildRounding = ImFloor(ChildRounding * scale_factor);
994 PopupRounding = ImFloor(PopupRounding * scale_factor);
995 FramePadding = ImFloor(FramePadding * scale_factor);
996 FrameRounding = ImFloor(FrameRounding * scale_factor);
997 ItemSpacing = ImFloor(ItemSpacing * scale_factor);
998 ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
999 CellPadding = ImFloor(CellPadding * scale_factor);
1000 TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
1001 IndentSpacing = ImFloor(IndentSpacing * scale_factor);
1002 ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
1003 ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
1004 ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
1005 GrabMinSize = ImFloor(GrabMinSize * scale_factor);
1006 GrabRounding = ImFloor(GrabRounding * scale_factor);
1007 LogSliderDeadzone = ImFloor(LogSliderDeadzone * scale_factor);
1008 TabRounding = ImFloor(TabRounding * scale_factor);
1009 TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImFloor(TabMinWidthForCloseButton * scale_factor) : FLT_MAX;
1010 DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
1011 DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
1012 MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
1013 }
1014
ImGuiIO()1015 ImGuiIO::ImGuiIO()
1016 {
1017 // Most fields are initialized with zero
1018 memset(this, 0, sizeof(*this));
1019 IM_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT); // Our pre-C++11 IM_STATIC_ASSERT() macros triggers warning on modern compilers so we don't use it here.
1020
1021 // Settings
1022 ConfigFlags = ImGuiConfigFlags_None;
1023 BackendFlags = ImGuiBackendFlags_None;
1024 DisplaySize = ImVec2(-1.0f, -1.0f);
1025 DeltaTime = 1.0f / 60.0f;
1026 IniSavingRate = 5.0f;
1027 IniFilename = "imgui.ini";
1028 LogFilename = "imgui_log.txt";
1029 MouseDoubleClickTime = 0.30f;
1030 MouseDoubleClickMaxDist = 6.0f;
1031 for (int i = 0; i < ImGuiKey_COUNT; i++)
1032 KeyMap[i] = -1;
1033 KeyRepeatDelay = 0.275f;
1034 KeyRepeatRate = 0.050f;
1035 UserData = NULL;
1036
1037 Fonts = NULL;
1038 FontGlobalScale = 1.0f;
1039 FontDefault = NULL;
1040 FontAllowUserScaling = false;
1041 DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1042
1043 // Miscellaneous options
1044 MouseDrawCursor = false;
1045 #ifdef __APPLE__
1046 ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
1047 #else
1048 ConfigMacOSXBehaviors = false;
1049 #endif
1050 ConfigInputTextCursorBlink = true;
1051 ConfigWindowsResizeFromEdges = true;
1052 ConfigWindowsMoveFromTitleBarOnly = false;
1053 ConfigMemoryCompactTimer = 60.0f;
1054
1055 // Platform Functions
1056 BackendPlatformName = BackendRendererName = NULL;
1057 BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1058 GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
1059 SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1060 ClipboardUserData = NULL;
1061 ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1062 ImeWindowHandle = NULL;
1063
1064 // Input (NB: we already have memset zero the entire structure!)
1065 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1066 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1067 MouseDragThreshold = 6.0f;
1068 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1069 for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
1070 for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1071 }
1072
1073 // Pass in translated ASCII characters for text input.
1074 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1075 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(unsigned int c)1076 void ImGuiIO::AddInputCharacter(unsigned int c)
1077 {
1078 if (c != 0)
1079 InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
1080 }
1081
1082 // UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
1083 // we should save the high surrogate.
AddInputCharacterUTF16(ImWchar16 c)1084 void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
1085 {
1086 if (c == 0 && InputQueueSurrogate == 0)
1087 return;
1088
1089 if ((c & 0xFC00) == 0xD800) // High surrogate, must save
1090 {
1091 if (InputQueueSurrogate != 0)
1092 InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1093 InputQueueSurrogate = c;
1094 return;
1095 }
1096
1097 ImWchar cp = c;
1098 if (InputQueueSurrogate != 0)
1099 {
1100 if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
1101 InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1102 else if (IM_UNICODE_CODEPOINT_MAX == (0xFFFF)) // Codepoint will not fit in ImWchar (extra parenthesis around 0xFFFF somehow fixes -Wunreachable-code with Clang)
1103 cp = IM_UNICODE_CODEPOINT_INVALID;
1104 else
1105 cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
1106 InputQueueSurrogate = 0;
1107 }
1108 InputQueueCharacters.push_back(cp);
1109 }
1110
AddInputCharactersUTF8(const char * utf8_chars)1111 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1112 {
1113 while (*utf8_chars != 0)
1114 {
1115 unsigned int c = 0;
1116 utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1117 if (c != 0)
1118 InputQueueCharacters.push_back((ImWchar)c);
1119 }
1120 }
1121
ClearInputCharacters()1122 void ImGuiIO::ClearInputCharacters()
1123 {
1124 InputQueueCharacters.resize(0);
1125 }
1126
1127 //-----------------------------------------------------------------------------
1128 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1129 //-----------------------------------------------------------------------------
1130
ImBezierCubicClosestPoint(const ImVec2 & p1,const ImVec2 & p2,const ImVec2 & p3,const ImVec2 & p4,const ImVec2 & p,int num_segments)1131 ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1132 {
1133 IM_ASSERT(num_segments > 0); // Use ImBezierClosestPointCasteljau()
1134 ImVec2 p_last = p1;
1135 ImVec2 p_closest;
1136 float p_closest_dist2 = FLT_MAX;
1137 float t_step = 1.0f / (float)num_segments;
1138 for (int i_step = 1; i_step <= num_segments; i_step++)
1139 {
1140 ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step);
1141 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1142 float dist2 = ImLengthSqr(p - p_line);
1143 if (dist2 < p_closest_dist2)
1144 {
1145 p_closest = p_line;
1146 p_closest_dist2 = dist2;
1147 }
1148 p_last = p_current;
1149 }
1150 return p_closest;
1151 }
1152
1153 // Closely mimics PathBezierToCasteljau() in imgui_draw.cpp
ImBezierCubicClosestPointCasteljauStep(const ImVec2 & p,ImVec2 & p_closest,ImVec2 & p_last,float & p_closest_dist2,float x1,float y1,float x2,float y2,float x3,float y3,float x4,float y4,float tess_tol,int level)1154 static void ImBezierCubicClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level)
1155 {
1156 float dx = x4 - x1;
1157 float dy = y4 - y1;
1158 float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
1159 float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
1160 d2 = (d2 >= 0) ? d2 : -d2;
1161 d3 = (d3 >= 0) ? d3 : -d3;
1162 if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
1163 {
1164 ImVec2 p_current(x4, y4);
1165 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1166 float dist2 = ImLengthSqr(p - p_line);
1167 if (dist2 < p_closest_dist2)
1168 {
1169 p_closest = p_line;
1170 p_closest_dist2 = dist2;
1171 }
1172 p_last = p_current;
1173 }
1174 else if (level < 10)
1175 {
1176 float x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f;
1177 float x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f;
1178 float x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f;
1179 float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f;
1180 float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f;
1181 float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
1182 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
1183 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
1184 }
1185 }
1186
1187 // tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
1188 // Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.
ImBezierCubicClosestPointCasteljau(const ImVec2 & p1,const ImVec2 & p2,const ImVec2 & p3,const ImVec2 & p4,const ImVec2 & p,float tess_tol)1189 ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
1190 {
1191 IM_ASSERT(tess_tol > 0.0f);
1192 ImVec2 p_last = p1;
1193 ImVec2 p_closest;
1194 float p_closest_dist2 = FLT_MAX;
1195 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0);
1196 return p_closest;
1197 }
1198
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1199 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1200 {
1201 ImVec2 ap = p - a;
1202 ImVec2 ab_dir = b - a;
1203 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1204 if (dot < 0.0f)
1205 return a;
1206 float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1207 if (dot > ab_len_sqr)
1208 return b;
1209 return a + ab_dir * dot / ab_len_sqr;
1210 }
1211
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1212 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1213 {
1214 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1215 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1216 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1217 return ((b1 == b2) && (b2 == b3));
1218 }
1219
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1220 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1221 {
1222 ImVec2 v0 = b - a;
1223 ImVec2 v1 = c - a;
1224 ImVec2 v2 = p - a;
1225 const float denom = v0.x * v1.y - v1.x * v0.y;
1226 out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1227 out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1228 out_u = 1.0f - out_v - out_w;
1229 }
1230
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1231 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1232 {
1233 ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1234 ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1235 ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1236 float dist2_ab = ImLengthSqr(p - proj_ab);
1237 float dist2_bc = ImLengthSqr(p - proj_bc);
1238 float dist2_ca = ImLengthSqr(p - proj_ca);
1239 float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1240 if (m == dist2_ab)
1241 return proj_ab;
1242 if (m == dist2_bc)
1243 return proj_bc;
1244 return proj_ca;
1245 }
1246
1247 //-----------------------------------------------------------------------------
1248 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
1249 //-----------------------------------------------------------------------------
1250
1251 // 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)1252 int ImStricmp(const char* str1, const char* str2)
1253 {
1254 int d;
1255 while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1256 return d;
1257 }
1258
ImStrnicmp(const char * str1,const char * str2,size_t count)1259 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1260 {
1261 int d = 0;
1262 while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1263 return d;
1264 }
1265
ImStrncpy(char * dst,const char * src,size_t count)1266 void ImStrncpy(char* dst, const char* src, size_t count)
1267 {
1268 if (count < 1)
1269 return;
1270 if (count > 1)
1271 strncpy(dst, src, count - 1);
1272 dst[count - 1] = 0;
1273 }
1274
ImStrdup(const char * str)1275 char* ImStrdup(const char* str)
1276 {
1277 size_t len = strlen(str);
1278 void* buf = IM_ALLOC(len + 1);
1279 return (char*)memcpy(buf, (const void*)str, len + 1);
1280 }
1281
ImStrdupcpy(char * dst,size_t * p_dst_size,const char * src)1282 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1283 {
1284 size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1285 size_t src_size = strlen(src) + 1;
1286 if (dst_buf_size < src_size)
1287 {
1288 IM_FREE(dst);
1289 dst = (char*)IM_ALLOC(src_size);
1290 if (p_dst_size)
1291 *p_dst_size = src_size;
1292 }
1293 return (char*)memcpy(dst, (const void*)src, src_size);
1294 }
1295
ImStrchrRange(const char * str,const char * str_end,char c)1296 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1297 {
1298 const char* p = (const char*)memchr(str, (int)c, str_end - str);
1299 return p;
1300 }
1301
ImStrlenW(const ImWchar * str)1302 int ImStrlenW(const ImWchar* str)
1303 {
1304 //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bit
1305 int n = 0;
1306 while (*str++) n++;
1307 return n;
1308 }
1309
1310 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1311 const char* ImStreolRange(const char* str, const char* str_end)
1312 {
1313 const char* p = (const char*)memchr(str, '\n', str_end - str);
1314 return p ? p : str_end;
1315 }
1316
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1317 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1318 {
1319 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1320 buf_mid_line--;
1321 return buf_mid_line;
1322 }
1323
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1324 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1325 {
1326 if (!needle_end)
1327 needle_end = needle + strlen(needle);
1328
1329 const char un0 = (char)toupper(*needle);
1330 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1331 {
1332 if (toupper(*haystack) == un0)
1333 {
1334 const char* b = needle + 1;
1335 for (const char* a = haystack + 1; b < needle_end; a++, b++)
1336 if (toupper(*a) != toupper(*b))
1337 break;
1338 if (b == needle_end)
1339 return haystack;
1340 }
1341 haystack++;
1342 }
1343 return NULL;
1344 }
1345
1346 // 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)1347 void ImStrTrimBlanks(char* buf)
1348 {
1349 char* p = buf;
1350 while (p[0] == ' ' || p[0] == '\t') // Leading blanks
1351 p++;
1352 char* p_start = p;
1353 while (*p != 0) // Find end of string
1354 p++;
1355 while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks
1356 p--;
1357 if (p_start != buf) // Copy memory if we had leading blanks
1358 memmove(buf, p_start, p - p_start);
1359 buf[p - p_start] = 0; // Zero terminate
1360 }
1361
ImStrSkipBlank(const char * str)1362 const char* ImStrSkipBlank(const char* str)
1363 {
1364 while (str[0] == ' ' || str[0] == '\t')
1365 str++;
1366 return str;
1367 }
1368
1369 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1370 // 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.
1371 // B) When buf==NULL vsnprintf() will return the output size.
1372 #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1373
1374 // We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
1375 // You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1376 // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
1377 // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
1378 #ifdef IMGUI_USE_STB_SPRINTF
1379 #define STB_SPRINTF_IMPLEMENTATION
1380 #include "stb_sprintf.h"
1381 #endif
1382
1383 #if defined(_MSC_VER) && !defined(vsnprintf)
1384 #define vsnprintf _vsnprintf
1385 #endif
1386
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1387 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1388 {
1389 va_list args;
1390 va_start(args, fmt);
1391 #ifdef IMGUI_USE_STB_SPRINTF
1392 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1393 #else
1394 int w = vsnprintf(buf, buf_size, fmt, args);
1395 #endif
1396 va_end(args);
1397 if (buf == NULL)
1398 return w;
1399 if (w == -1 || w >= (int)buf_size)
1400 w = (int)buf_size - 1;
1401 buf[w] = 0;
1402 return w;
1403 }
1404
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1405 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1406 {
1407 #ifdef IMGUI_USE_STB_SPRINTF
1408 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1409 #else
1410 int w = vsnprintf(buf, buf_size, fmt, args);
1411 #endif
1412 if (buf == NULL)
1413 return w;
1414 if (w == -1 || w >= (int)buf_size)
1415 w = (int)buf_size - 1;
1416 buf[w] = 0;
1417 return w;
1418 }
1419 #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1420
1421 // CRC32 needs a 1KB lookup table (not cache friendly)
1422 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1423 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1424 static const ImU32 GCrc32LookupTable[256] =
1425 {
1426 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1427 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1428 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1429 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1430 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1431 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1432 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1433 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1434 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1435 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1436 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1437 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1438 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1439 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1440 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1441 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1442 };
1443
1444 // Known size hash
1445 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1446 // 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)1447 ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed)
1448 {
1449 ImU32 crc = ~seed;
1450 const unsigned char* data = (const unsigned char*)data_p;
1451 const ImU32* crc32_lut = GCrc32LookupTable;
1452 while (data_size-- != 0)
1453 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1454 return ~crc;
1455 }
1456
1457 // Zero-terminated string hash, with support for ### to reset back to seed value
1458 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1459 // Because this syntax is rarely used we are optimizing for the common case.
1460 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1461 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1462 // 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)1463 ImGuiID ImHashStr(const char* data_p, size_t data_size, ImU32 seed)
1464 {
1465 seed = ~seed;
1466 ImU32 crc = seed;
1467 const unsigned char* data = (const unsigned char*)data_p;
1468 const ImU32* crc32_lut = GCrc32LookupTable;
1469 if (data_size != 0)
1470 {
1471 while (data_size-- != 0)
1472 {
1473 unsigned char c = *data++;
1474 if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
1475 crc = seed;
1476 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1477 }
1478 }
1479 else
1480 {
1481 while (unsigned char c = *data++)
1482 {
1483 if (c == '#' && data[0] == '#' && data[1] == '#')
1484 crc = seed;
1485 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1486 }
1487 }
1488 return ~crc;
1489 }
1490
1491 //-----------------------------------------------------------------------------
1492 // [SECTION] MISC HELPERS/UTILITIES (File functions)
1493 //-----------------------------------------------------------------------------
1494
1495 // Default file functions
1496 #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1497
ImFileOpen(const char * filename,const char * mode)1498 ImFileHandle ImFileOpen(const char* filename, const char* mode)
1499 {
1500 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
1501 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
1502 // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
1503 const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
1504 const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
1505 ImVector<ImWchar> buf;
1506 buf.resize(filename_wsize + mode_wsize);
1507 ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, (wchar_t*)&buf[0], filename_wsize);
1508 ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, (wchar_t*)&buf[filename_wsize], mode_wsize);
1509 return ::_wfopen((const wchar_t*)&buf[0], (const wchar_t*)&buf[filename_wsize]);
1510 #else
1511 return fopen(filename, mode);
1512 #endif
1513 }
1514
1515 // We should in theory be using fseeko()/ftello() with off_t and _fseeki64()/_ftelli64() with __int64, waiting for the PR that does that in a very portable pre-C++11 zero-warnings way.
ImFileClose(ImFileHandle f)1516 bool ImFileClose(ImFileHandle f) { return fclose(f) == 0; }
ImFileGetSize(ImFileHandle f)1517 ImU64 ImFileGetSize(ImFileHandle f) { long off = 0, sz = 0; return ((off = ftell(f)) != -1 && !fseek(f, 0, SEEK_END) && (sz = ftell(f)) != -1 && !fseek(f, off, SEEK_SET)) ? (ImU64)sz : (ImU64)-1; }
ImFileRead(void * data,ImU64 sz,ImU64 count,ImFileHandle f)1518 ImU64 ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fread(data, (size_t)sz, (size_t)count, f); }
ImFileWrite(const void * data,ImU64 sz,ImU64 count,ImFileHandle f)1519 ImU64 ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fwrite(data, (size_t)sz, (size_t)count, f); }
1520 #endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1521
1522 // Helper: Load file content into memory
1523 // Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
1524 // This can't really be used with "rt" because fseek size won't match read size.
ImFileLoadToMemory(const char * filename,const char * mode,size_t * out_file_size,int padding_bytes)1525 void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
1526 {
1527 IM_ASSERT(filename && mode);
1528 if (out_file_size)
1529 *out_file_size = 0;
1530
1531 ImFileHandle f;
1532 if ((f = ImFileOpen(filename, mode)) == NULL)
1533 return NULL;
1534
1535 size_t file_size = (size_t)ImFileGetSize(f);
1536 if (file_size == (size_t)-1)
1537 {
1538 ImFileClose(f);
1539 return NULL;
1540 }
1541
1542 void* file_data = IM_ALLOC(file_size + padding_bytes);
1543 if (file_data == NULL)
1544 {
1545 ImFileClose(f);
1546 return NULL;
1547 }
1548 if (ImFileRead(file_data, 1, file_size, f) != file_size)
1549 {
1550 ImFileClose(f);
1551 IM_FREE(file_data);
1552 return NULL;
1553 }
1554 if (padding_bytes > 0)
1555 memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1556
1557 ImFileClose(f);
1558 if (out_file_size)
1559 *out_file_size = file_size;
1560
1561 return file_data;
1562 }
1563
1564 //-----------------------------------------------------------------------------
1565 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1566 //-----------------------------------------------------------------------------
1567
1568 // Convert UTF-8 to 32-bit character, process single character input.
1569 // A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).
1570 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1571 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1572 {
1573 static const char lengths[32] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 };
1574 static const int masks[] = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 };
1575 static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 };
1576 static const int shiftc[] = { 0, 18, 12, 6, 0 };
1577 static const int shifte[] = { 0, 6, 4, 2, 0 };
1578 int len = lengths[*(const unsigned char*)in_text >> 3];
1579 int wanted = len + !len;
1580
1581 if (in_text_end == NULL)
1582 in_text_end = in_text + wanted; // Max length, nulls will be taken into account.
1583
1584 // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here,
1585 // so it is fast even with excessive branching.
1586 unsigned char s[4];
1587 s[0] = in_text + 0 < in_text_end ? in_text[0] : 0;
1588 s[1] = in_text + 1 < in_text_end ? in_text[1] : 0;
1589 s[2] = in_text + 2 < in_text_end ? in_text[2] : 0;
1590 s[3] = in_text + 3 < in_text_end ? in_text[3] : 0;
1591
1592 // Assume a four-byte character and load four bytes. Unused bits are shifted out.
1593 *out_char = (uint32_t)(s[0] & masks[len]) << 18;
1594 *out_char |= (uint32_t)(s[1] & 0x3f) << 12;
1595 *out_char |= (uint32_t)(s[2] & 0x3f) << 6;
1596 *out_char |= (uint32_t)(s[3] & 0x3f) << 0;
1597 *out_char >>= shiftc[len];
1598
1599 // Accumulate the various error conditions.
1600 int e = 0;
1601 e = (*out_char < mins[len]) << 6; // non-canonical encoding
1602 e |= ((*out_char >> 11) == 0x1b) << 7; // surrogate half?
1603 e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8; // out of range?
1604 e |= (s[1] & 0xc0) >> 2;
1605 e |= (s[2] & 0xc0) >> 4;
1606 e |= (s[3] ) >> 6;
1607 e ^= 0x2a; // top two bits of each tail byte correct?
1608 e >>= shifte[len];
1609
1610 if (e)
1611 {
1612 // No bytes are consumed when *in_text == 0 || in_text == in_text_end.
1613 // One byte is consumed in case of invalid first byte of in_text.
1614 // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes.
1615 // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s.
1616 wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]);
1617 *out_char = IM_UNICODE_CODEPOINT_INVALID;
1618 }
1619
1620 return wanted;
1621 }
1622
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1623 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1624 {
1625 ImWchar* buf_out = buf;
1626 ImWchar* buf_end = buf + buf_size;
1627 while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1628 {
1629 unsigned int c;
1630 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1631 if (c == 0)
1632 break;
1633 *buf_out++ = (ImWchar)c;
1634 }
1635 *buf_out = 0;
1636 if (in_text_remaining)
1637 *in_text_remaining = in_text;
1638 return (int)(buf_out - buf);
1639 }
1640
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1641 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1642 {
1643 int char_count = 0;
1644 while ((!in_text_end || in_text < in_text_end) && *in_text)
1645 {
1646 unsigned int c;
1647 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1648 if (c == 0)
1649 break;
1650 char_count++;
1651 }
1652 return char_count;
1653 }
1654
1655 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1656 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1657 {
1658 if (c < 0x80)
1659 {
1660 buf[0] = (char)c;
1661 return 1;
1662 }
1663 if (c < 0x800)
1664 {
1665 if (buf_size < 2) return 0;
1666 buf[0] = (char)(0xc0 + (c >> 6));
1667 buf[1] = (char)(0x80 + (c & 0x3f));
1668 return 2;
1669 }
1670 if (c < 0x10000)
1671 {
1672 if (buf_size < 3) return 0;
1673 buf[0] = (char)(0xe0 + (c >> 12));
1674 buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
1675 buf[2] = (char)(0x80 + ((c ) & 0x3f));
1676 return 3;
1677 }
1678 if (c <= 0x10FFFF)
1679 {
1680 if (buf_size < 4) return 0;
1681 buf[0] = (char)(0xf0 + (c >> 18));
1682 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1683 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1684 buf[3] = (char)(0x80 + ((c ) & 0x3f));
1685 return 4;
1686 }
1687 // Invalid code point, the max unicode is 0x10FFFF
1688 return 0;
1689 }
1690
1691 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1692 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1693 {
1694 unsigned int unused = 0;
1695 return ImTextCharFromUtf8(&unused, in_text, in_text_end);
1696 }
1697
ImTextCountUtf8BytesFromChar(unsigned int c)1698 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1699 {
1700 if (c < 0x80) return 1;
1701 if (c < 0x800) return 2;
1702 if (c < 0x10000) return 3;
1703 if (c <= 0x10FFFF) return 4;
1704 return 3;
1705 }
1706
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1707 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1708 {
1709 char* buf_out = buf;
1710 const char* buf_end = buf + buf_size;
1711 while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1712 {
1713 unsigned int c = (unsigned int)(*in_text++);
1714 if (c < 0x80)
1715 *buf_out++ = (char)c;
1716 else
1717 buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end - buf_out - 1), c);
1718 }
1719 *buf_out = 0;
1720 return (int)(buf_out - buf);
1721 }
1722
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1723 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1724 {
1725 int bytes_count = 0;
1726 while ((!in_text_end || in_text < in_text_end) && *in_text)
1727 {
1728 unsigned int c = (unsigned int)(*in_text++);
1729 if (c < 0x80)
1730 bytes_count++;
1731 else
1732 bytes_count += ImTextCountUtf8BytesFromChar(c);
1733 }
1734 return bytes_count;
1735 }
1736
1737 //-----------------------------------------------------------------------------
1738 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
1739 // Note: The Convert functions are early design which are not consistent with other API.
1740 //-----------------------------------------------------------------------------
1741
ImAlphaBlendColors(ImU32 col_a,ImU32 col_b)1742 IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
1743 {
1744 float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
1745 int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
1746 int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
1747 int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
1748 return IM_COL32(r, g, b, 0xFF);
1749 }
1750
ColorConvertU32ToFloat4(ImU32 in)1751 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1752 {
1753 float s = 1.0f / 255.0f;
1754 return ImVec4(
1755 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1756 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1757 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1758 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1759 }
1760
ColorConvertFloat4ToU32(const ImVec4 & in)1761 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1762 {
1763 ImU32 out;
1764 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1765 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1766 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1767 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1768 return out;
1769 }
1770
1771 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1772 // 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)1773 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1774 {
1775 float K = 0.f;
1776 if (g < b)
1777 {
1778 ImSwap(g, b);
1779 K = -1.f;
1780 }
1781 if (r < g)
1782 {
1783 ImSwap(r, g);
1784 K = -2.f / 6.f - K;
1785 }
1786
1787 const float chroma = r - (g < b ? g : b);
1788 out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1789 out_s = chroma / (r + 1e-20f);
1790 out_v = r;
1791 }
1792
1793 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1794 // 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)1795 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1796 {
1797 if (s == 0.0f)
1798 {
1799 // gray
1800 out_r = out_g = out_b = v;
1801 return;
1802 }
1803
1804 h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
1805 int i = (int)h;
1806 float f = h - (float)i;
1807 float p = v * (1.0f - s);
1808 float q = v * (1.0f - s * f);
1809 float t = v * (1.0f - s * (1.0f - f));
1810
1811 switch (i)
1812 {
1813 case 0: out_r = v; out_g = t; out_b = p; break;
1814 case 1: out_r = q; out_g = v; out_b = p; break;
1815 case 2: out_r = p; out_g = v; out_b = t; break;
1816 case 3: out_r = p; out_g = q; out_b = v; break;
1817 case 4: out_r = t; out_g = p; out_b = v; break;
1818 case 5: default: out_r = v; out_g = p; out_b = q; break;
1819 }
1820 }
1821
1822 //-----------------------------------------------------------------------------
1823 // [SECTION] ImGuiStorage
1824 // Helper: Key->value storage
1825 //-----------------------------------------------------------------------------
1826
1827 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair> & data,ImGuiID key)1828 static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key)
1829 {
1830 ImGuiStorage::ImGuiStoragePair* first = data.Data;
1831 ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size;
1832 size_t count = (size_t)(last - first);
1833 while (count > 0)
1834 {
1835 size_t count2 = count >> 1;
1836 ImGuiStorage::ImGuiStoragePair* mid = first + count2;
1837 if (mid->key < key)
1838 {
1839 first = ++mid;
1840 count -= count2 + 1;
1841 }
1842 else
1843 {
1844 count = count2;
1845 }
1846 }
1847 return first;
1848 }
1849
1850 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1851 void ImGuiStorage::BuildSortByKey()
1852 {
1853 struct StaticFunc
1854 {
1855 static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1856 {
1857 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1858 if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1;
1859 if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1;
1860 return 0;
1861 }
1862 };
1863 if (Data.Size > 1)
1864 ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID);
1865 }
1866
GetInt(ImGuiID key,int default_val) const1867 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1868 {
1869 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1870 if (it == Data.end() || it->key != key)
1871 return default_val;
1872 return it->val_i;
1873 }
1874
GetBool(ImGuiID key,bool default_val) const1875 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1876 {
1877 return GetInt(key, default_val ? 1 : 0) != 0;
1878 }
1879
GetFloat(ImGuiID key,float default_val) const1880 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1881 {
1882 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1883 if (it == Data.end() || it->key != key)
1884 return default_val;
1885 return it->val_f;
1886 }
1887
GetVoidPtr(ImGuiID key) const1888 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1889 {
1890 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1891 if (it == Data.end() || it->key != key)
1892 return NULL;
1893 return it->val_p;
1894 }
1895
1896 // 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)1897 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1898 {
1899 ImGuiStoragePair* it = LowerBound(Data, key);
1900 if (it == Data.end() || it->key != key)
1901 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1902 return &it->val_i;
1903 }
1904
GetBoolRef(ImGuiID key,bool default_val)1905 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1906 {
1907 return (bool*)GetIntRef(key, default_val ? 1 : 0);
1908 }
1909
GetFloatRef(ImGuiID key,float default_val)1910 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1911 {
1912 ImGuiStoragePair* it = LowerBound(Data, key);
1913 if (it == Data.end() || it->key != key)
1914 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1915 return &it->val_f;
1916 }
1917
GetVoidPtrRef(ImGuiID key,void * default_val)1918 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1919 {
1920 ImGuiStoragePair* it = LowerBound(Data, key);
1921 if (it == Data.end() || it->key != key)
1922 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1923 return &it->val_p;
1924 }
1925
1926 // 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)1927 void ImGuiStorage::SetInt(ImGuiID key, int val)
1928 {
1929 ImGuiStoragePair* it = LowerBound(Data, key);
1930 if (it == Data.end() || it->key != key)
1931 {
1932 Data.insert(it, ImGuiStoragePair(key, val));
1933 return;
1934 }
1935 it->val_i = val;
1936 }
1937
SetBool(ImGuiID key,bool val)1938 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1939 {
1940 SetInt(key, val ? 1 : 0);
1941 }
1942
SetFloat(ImGuiID key,float val)1943 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1944 {
1945 ImGuiStoragePair* it = LowerBound(Data, key);
1946 if (it == Data.end() || it->key != key)
1947 {
1948 Data.insert(it, ImGuiStoragePair(key, val));
1949 return;
1950 }
1951 it->val_f = val;
1952 }
1953
SetVoidPtr(ImGuiID key,void * val)1954 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1955 {
1956 ImGuiStoragePair* it = LowerBound(Data, key);
1957 if (it == Data.end() || it->key != key)
1958 {
1959 Data.insert(it, ImGuiStoragePair(key, val));
1960 return;
1961 }
1962 it->val_p = val;
1963 }
1964
SetAllInt(int v)1965 void ImGuiStorage::SetAllInt(int v)
1966 {
1967 for (int i = 0; i < Data.Size; i++)
1968 Data[i].val_i = v;
1969 }
1970
1971 //-----------------------------------------------------------------------------
1972 // [SECTION] ImGuiTextFilter
1973 //-----------------------------------------------------------------------------
1974
1975 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1976 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1977 {
1978 if (default_filter)
1979 {
1980 ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1981 Build();
1982 }
1983 else
1984 {
1985 InputBuf[0] = 0;
1986 CountGrep = 0;
1987 }
1988 }
1989
Draw(const char * label,float width)1990 bool ImGuiTextFilter::Draw(const char* label, float width)
1991 {
1992 if (width != 0.0f)
1993 ImGui::SetNextItemWidth(width);
1994 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1995 if (value_changed)
1996 Build();
1997 return value_changed;
1998 }
1999
split(char separator,ImVector<ImGuiTextRange> * out) const2000 void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
2001 {
2002 out->resize(0);
2003 const char* wb = b;
2004 const char* we = wb;
2005 while (we < e)
2006 {
2007 if (*we == separator)
2008 {
2009 out->push_back(ImGuiTextRange(wb, we));
2010 wb = we + 1;
2011 }
2012 we++;
2013 }
2014 if (wb != we)
2015 out->push_back(ImGuiTextRange(wb, we));
2016 }
2017
Build()2018 void ImGuiTextFilter::Build()
2019 {
2020 Filters.resize(0);
2021 ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf));
2022 input_range.split(',', &Filters);
2023
2024 CountGrep = 0;
2025 for (int i = 0; i != Filters.Size; i++)
2026 {
2027 ImGuiTextRange& f = Filters[i];
2028 while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2029 f.b++;
2030 while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2031 f.e--;
2032 if (f.empty())
2033 continue;
2034 if (Filters[i].b[0] != '-')
2035 CountGrep += 1;
2036 }
2037 }
2038
PassFilter(const char * text,const char * text_end) const2039 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2040 {
2041 if (Filters.empty())
2042 return true;
2043
2044 if (text == NULL)
2045 text = "";
2046
2047 for (int i = 0; i != Filters.Size; i++)
2048 {
2049 const ImGuiTextRange& f = Filters[i];
2050 if (f.empty())
2051 continue;
2052 if (f.b[0] == '-')
2053 {
2054 // Subtract
2055 if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
2056 return false;
2057 }
2058 else
2059 {
2060 // Grep
2061 if (ImStristr(text, text_end, f.b, f.e) != NULL)
2062 return true;
2063 }
2064 }
2065
2066 // Implicit * grep
2067 if (CountGrep == 0)
2068 return true;
2069
2070 return false;
2071 }
2072
2073 //-----------------------------------------------------------------------------
2074 // [SECTION] ImGuiTextBuffer
2075 //-----------------------------------------------------------------------------
2076
2077 // On some platform vsnprintf() takes va_list by reference and modifies it.
2078 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2079 #ifndef va_copy
2080 #if defined(__GNUC__) || defined(__clang__)
2081 #define va_copy(dest, src) __builtin_va_copy(dest, src)
2082 #else
2083 #define va_copy(dest, src) (dest = src)
2084 #endif
2085 #endif
2086
2087 char ImGuiTextBuffer::EmptyString[1] = { 0 };
2088
append(const char * str,const char * str_end)2089 void ImGuiTextBuffer::append(const char* str, const char* str_end)
2090 {
2091 int len = str_end ? (int)(str_end - str) : (int)strlen(str);
2092
2093 // Add zero-terminator the first time
2094 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2095 const int needed_sz = write_off + len;
2096 if (write_off + len >= Buf.Capacity)
2097 {
2098 int new_capacity = Buf.Capacity * 2;
2099 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2100 }
2101
2102 Buf.resize(needed_sz);
2103 memcpy(&Buf[write_off - 1], str, (size_t)len);
2104 Buf[write_off - 1 + len] = 0;
2105 }
2106
appendf(const char * fmt,...)2107 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2108 {
2109 va_list args;
2110 va_start(args, fmt);
2111 appendfv(fmt, args);
2112 va_end(args);
2113 }
2114
2115 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)2116 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2117 {
2118 va_list args_copy;
2119 va_copy(args_copy, args);
2120
2121 int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2122 if (len <= 0)
2123 {
2124 va_end(args_copy);
2125 return;
2126 }
2127
2128 // Add zero-terminator the first time
2129 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2130 const int needed_sz = write_off + len;
2131 if (write_off + len >= Buf.Capacity)
2132 {
2133 int new_capacity = Buf.Capacity * 2;
2134 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2135 }
2136
2137 Buf.resize(needed_sz);
2138 ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2139 va_end(args_copy);
2140 }
2141
2142 //-----------------------------------------------------------------------------
2143 // [SECTION] ImGuiListClipper
2144 // This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed
2145 // the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO)
2146 //-----------------------------------------------------------------------------
2147
2148 // FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.
2149 // The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.
GetSkipItemForListClipping()2150 static bool GetSkipItemForListClipping()
2151 {
2152 ImGuiContext& g = *GImGui;
2153 return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);
2154 }
2155
2156 // Helper to calculate coarse clipping of large list of evenly sized items.
2157 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
2158 // 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)2159 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
2160 {
2161 ImGuiContext& g = *GImGui;
2162 ImGuiWindow* window = g.CurrentWindow;
2163 if (g.LogEnabled)
2164 {
2165 // If logging is active, do not perform any clipping
2166 *out_items_display_start = 0;
2167 *out_items_display_end = items_count;
2168 return;
2169 }
2170 if (GetSkipItemForListClipping())
2171 {
2172 *out_items_display_start = *out_items_display_end = 0;
2173 return;
2174 }
2175
2176 // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
2177 ImRect unclipped_rect = window->ClipRect;
2178 if (g.NavMoveRequest)
2179 unclipped_rect.Add(g.NavScoringRect);
2180 if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId)
2181 unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max));
2182
2183 const ImVec2 pos = window->DC.CursorPos;
2184 int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
2185 int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
2186
2187 // When performing a navigation request, ensure we have one item extra in the direction we are moving to
2188 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
2189 start--;
2190 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
2191 end++;
2192
2193 start = ImClamp(start, 0, items_count);
2194 end = ImClamp(end + 1, start, items_count);
2195 *out_items_display_start = start;
2196 *out_items_display_end = end;
2197 }
2198
SetCursorPosYAndSetupForPrevLine(float pos_y,float line_height)2199 static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height)
2200 {
2201 // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2202 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2203 // The clipper should probably have a 4th step to display the last item in a regular manner.
2204 ImGuiContext& g = *GImGui;
2205 ImGuiWindow* window = g.CurrentWindow;
2206 float off_y = pos_y - window->DC.CursorPos.y;
2207 window->DC.CursorPos.y = pos_y;
2208 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y);
2209 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.
2210 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.
2211 if (ImGuiOldColumns* columns = window->DC.CurrentColumns)
2212 columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
2213 if (ImGuiTable* table = g.CurrentTable)
2214 {
2215 if (table->IsInsideRow)
2216 ImGui::TableEndRow(table);
2217 table->RowPosY2 = window->DC.CursorPos.y;
2218 const int row_increase = (int)((off_y / line_height) + 0.5f);
2219 //table->CurrentRow += row_increase; // Can't do without fixing TableEndRow()
2220 table->RowBgColorCounter += row_increase;
2221 }
2222 }
2223
ImGuiListClipper()2224 ImGuiListClipper::ImGuiListClipper()
2225 {
2226 memset(this, 0, sizeof(*this));
2227 ItemsCount = -1;
2228 }
2229
~ImGuiListClipper()2230 ImGuiListClipper::~ImGuiListClipper()
2231 {
2232 IM_ASSERT(ItemsCount == -1 && "Forgot to call End(), or to Step() until false?");
2233 }
2234
2235 // Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1
2236 // Use case B: Begin() called from constructor with items_height>0
2237 // 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 items_count,float items_height)2238 void ImGuiListClipper::Begin(int items_count, float items_height)
2239 {
2240 ImGuiContext& g = *GImGui;
2241 ImGuiWindow* window = g.CurrentWindow;
2242
2243 if (ImGuiTable* table = g.CurrentTable)
2244 if (table->IsInsideRow)
2245 ImGui::TableEndRow(table);
2246
2247 StartPosY = window->DC.CursorPos.y;
2248 ItemsHeight = items_height;
2249 ItemsCount = items_count;
2250 ItemsFrozen = 0;
2251 StepNo = 0;
2252 DisplayStart = -1;
2253 DisplayEnd = 0;
2254 }
2255
End()2256 void ImGuiListClipper::End()
2257 {
2258 if (ItemsCount < 0) // Already ended
2259 return;
2260
2261 // 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.
2262 if (ItemsCount < INT_MAX && DisplayStart >= 0)
2263 SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight);
2264 ItemsCount = -1;
2265 StepNo = 3;
2266 }
2267
Step()2268 bool ImGuiListClipper::Step()
2269 {
2270 ImGuiContext& g = *GImGui;
2271 ImGuiWindow* window = g.CurrentWindow;
2272
2273 ImGuiTable* table = g.CurrentTable;
2274 if (table && table->IsInsideRow)
2275 ImGui::TableEndRow(table);
2276
2277 // No items
2278 if (ItemsCount == 0 || GetSkipItemForListClipping())
2279 {
2280 End();
2281 return false;
2282 }
2283
2284 // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
2285 if (StepNo == 0)
2286 {
2287 // While we are in frozen row state, keep displaying items one by one, unclipped
2288 // FIXME: Could be stored as a table-agnostic state.
2289 if (table != NULL && !table->IsUnfrozenRows)
2290 {
2291 DisplayStart = ItemsFrozen;
2292 DisplayEnd = ItemsFrozen + 1;
2293 ItemsFrozen++;
2294 return true;
2295 }
2296
2297 StartPosY = window->DC.CursorPos.y;
2298 if (ItemsHeight <= 0.0f)
2299 {
2300 // Submit the first item so we can measure its height (generally it is 0..1)
2301 DisplayStart = ItemsFrozen;
2302 DisplayEnd = ItemsFrozen + 1;
2303 StepNo = 1;
2304 return true;
2305 }
2306
2307 // Already has item height (given by user in Begin): skip to calculating step
2308 DisplayStart = DisplayEnd;
2309 StepNo = 2;
2310 }
2311
2312 // Step 1: the clipper infer height from first element
2313 if (StepNo == 1)
2314 {
2315 IM_ASSERT(ItemsHeight <= 0.0f);
2316 if (table)
2317 {
2318 const float pos_y1 = table->RowPosY1; // Using this instead of StartPosY to handle clipper straddling the frozen row
2319 const float pos_y2 = table->RowPosY2; // Using this instead of CursorPos.y to take account of tallest cell.
2320 ItemsHeight = pos_y2 - pos_y1;
2321 window->DC.CursorPos.y = pos_y2;
2322 }
2323 else
2324 {
2325 ItemsHeight = window->DC.CursorPos.y - StartPosY;
2326 }
2327 IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
2328 StepNo = 2;
2329 }
2330
2331 // Reached end of list
2332 if (DisplayEnd >= ItemsCount)
2333 {
2334 End();
2335 return false;
2336 }
2337
2338 // Step 2: calculate the actual range of elements to display, and position the cursor before the first element
2339 if (StepNo == 2)
2340 {
2341 IM_ASSERT(ItemsHeight > 0.0f);
2342
2343 int already_submitted = DisplayEnd;
2344 ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &DisplayStart, &DisplayEnd);
2345 DisplayStart += already_submitted;
2346 DisplayEnd += already_submitted;
2347
2348 // Seek cursor
2349 if (DisplayStart > already_submitted)
2350 SetCursorPosYAndSetupForPrevLine(StartPosY + (DisplayStart - ItemsFrozen) * ItemsHeight, ItemsHeight);
2351
2352 StepNo = 3;
2353 return true;
2354 }
2355
2356 // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),
2357 // Advance the cursor to the end of the list and then returns 'false' to end the loop.
2358 if (StepNo == 3)
2359 {
2360 // Seek cursor
2361 if (ItemsCount < INT_MAX)
2362 SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); // advance cursor
2363 ItemsCount = -1;
2364 return false;
2365 }
2366
2367 IM_ASSERT(0);
2368 return false;
2369 }
2370
2371 //-----------------------------------------------------------------------------
2372 // [SECTION] STYLING
2373 //-----------------------------------------------------------------------------
2374
GetStyle()2375 ImGuiStyle& ImGui::GetStyle()
2376 {
2377 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
2378 return GImGui->Style;
2379 }
2380
GetColorU32(ImGuiCol idx,float alpha_mul)2381 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
2382 {
2383 ImGuiStyle& style = GImGui->Style;
2384 ImVec4 c = style.Colors[idx];
2385 c.w *= style.Alpha * alpha_mul;
2386 return ColorConvertFloat4ToU32(c);
2387 }
2388
GetColorU32(const ImVec4 & col)2389 ImU32 ImGui::GetColorU32(const ImVec4& col)
2390 {
2391 ImGuiStyle& style = GImGui->Style;
2392 ImVec4 c = col;
2393 c.w *= style.Alpha;
2394 return ColorConvertFloat4ToU32(c);
2395 }
2396
GetStyleColorVec4(ImGuiCol idx)2397 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
2398 {
2399 ImGuiStyle& style = GImGui->Style;
2400 return style.Colors[idx];
2401 }
2402
GetColorU32(ImU32 col)2403 ImU32 ImGui::GetColorU32(ImU32 col)
2404 {
2405 ImGuiStyle& style = GImGui->Style;
2406 if (style.Alpha >= 1.0f)
2407 return col;
2408 ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
2409 a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
2410 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
2411 }
2412
2413 // 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)2414 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
2415 {
2416 ImGuiContext& g = *GImGui;
2417 ImGuiColorMod backup;
2418 backup.Col = idx;
2419 backup.BackupValue = g.Style.Colors[idx];
2420 g.ColorStack.push_back(backup);
2421 g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
2422 }
2423
PushStyleColor(ImGuiCol idx,const ImVec4 & col)2424 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
2425 {
2426 ImGuiContext& g = *GImGui;
2427 ImGuiColorMod backup;
2428 backup.Col = idx;
2429 backup.BackupValue = g.Style.Colors[idx];
2430 g.ColorStack.push_back(backup);
2431 g.Style.Colors[idx] = col;
2432 }
2433
PopStyleColor(int count)2434 void ImGui::PopStyleColor(int count)
2435 {
2436 ImGuiContext& g = *GImGui;
2437 while (count > 0)
2438 {
2439 ImGuiColorMod& backup = g.ColorStack.back();
2440 g.Style.Colors[backup.Col] = backup.BackupValue;
2441 g.ColorStack.pop_back();
2442 count--;
2443 }
2444 }
2445
2446 struct ImGuiStyleVarInfo
2447 {
2448 ImGuiDataType Type;
2449 ImU32 Count;
2450 ImU32 Offset;
GetVarPtrImGuiStyleVarInfo2451 void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
2452 };
2453
2454 static const ImGuiStyleVarInfo GStyleVarInfo[] =
2455 {
2456 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
2457 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
2458 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
2459 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
2460 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
2461 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
2462 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
2463 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
2464 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
2465 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
2466 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
2467 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
2468 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
2469 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
2470 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
2471 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
2472 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding
2473 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
2474 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
2475 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
2476 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
2477 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
2478 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
2479 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
2480 };
2481
GetStyleVarInfo(ImGuiStyleVar idx)2482 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
2483 {
2484 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
2485 IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
2486 return &GStyleVarInfo[idx];
2487 }
2488
PushStyleVar(ImGuiStyleVar idx,float val)2489 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
2490 {
2491 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2492 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
2493 {
2494 ImGuiContext& g = *GImGui;
2495 float* pvar = (float*)var_info->GetVarPtr(&g.Style);
2496 g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
2497 *pvar = val;
2498 return;
2499 }
2500 IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!");
2501 }
2502
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)2503 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
2504 {
2505 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2506 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
2507 {
2508 ImGuiContext& g = *GImGui;
2509 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
2510 g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
2511 *pvar = val;
2512 return;
2513 }
2514 IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!");
2515 }
2516
PopStyleVar(int count)2517 void ImGui::PopStyleVar(int count)
2518 {
2519 ImGuiContext& g = *GImGui;
2520 while (count > 0)
2521 {
2522 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
2523 ImGuiStyleMod& backup = g.StyleVarStack.back();
2524 const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
2525 void* data = info->GetVarPtr(&g.Style);
2526 if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; }
2527 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
2528 g.StyleVarStack.pop_back();
2529 count--;
2530 }
2531 }
2532
GetStyleColorName(ImGuiCol idx)2533 const char* ImGui::GetStyleColorName(ImGuiCol idx)
2534 {
2535 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
2536 switch (idx)
2537 {
2538 case ImGuiCol_Text: return "Text";
2539 case ImGuiCol_TextDisabled: return "TextDisabled";
2540 case ImGuiCol_WindowBg: return "WindowBg";
2541 case ImGuiCol_ChildBg: return "ChildBg";
2542 case ImGuiCol_PopupBg: return "PopupBg";
2543 case ImGuiCol_Border: return "Border";
2544 case ImGuiCol_BorderShadow: return "BorderShadow";
2545 case ImGuiCol_FrameBg: return "FrameBg";
2546 case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
2547 case ImGuiCol_FrameBgActive: return "FrameBgActive";
2548 case ImGuiCol_TitleBg: return "TitleBg";
2549 case ImGuiCol_TitleBgActive: return "TitleBgActive";
2550 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
2551 case ImGuiCol_MenuBarBg: return "MenuBarBg";
2552 case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
2553 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
2554 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
2555 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
2556 case ImGuiCol_CheckMark: return "CheckMark";
2557 case ImGuiCol_SliderGrab: return "SliderGrab";
2558 case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
2559 case ImGuiCol_Button: return "Button";
2560 case ImGuiCol_ButtonHovered: return "ButtonHovered";
2561 case ImGuiCol_ButtonActive: return "ButtonActive";
2562 case ImGuiCol_Header: return "Header";
2563 case ImGuiCol_HeaderHovered: return "HeaderHovered";
2564 case ImGuiCol_HeaderActive: return "HeaderActive";
2565 case ImGuiCol_Separator: return "Separator";
2566 case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
2567 case ImGuiCol_SeparatorActive: return "SeparatorActive";
2568 case ImGuiCol_ResizeGrip: return "ResizeGrip";
2569 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
2570 case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
2571 case ImGuiCol_Tab: return "Tab";
2572 case ImGuiCol_TabHovered: return "TabHovered";
2573 case ImGuiCol_TabActive: return "TabActive";
2574 case ImGuiCol_TabUnfocused: return "TabUnfocused";
2575 case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
2576 case ImGuiCol_PlotLines: return "PlotLines";
2577 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
2578 case ImGuiCol_PlotHistogram: return "PlotHistogram";
2579 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
2580 case ImGuiCol_TableHeaderBg: return "TableHeaderBg";
2581 case ImGuiCol_TableBorderStrong: return "TableBorderStrong";
2582 case ImGuiCol_TableBorderLight: return "TableBorderLight";
2583 case ImGuiCol_TableRowBg: return "TableRowBg";
2584 case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt";
2585 case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
2586 case ImGuiCol_DragDropTarget: return "DragDropTarget";
2587 case ImGuiCol_NavHighlight: return "NavHighlight";
2588 case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
2589 case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
2590 case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
2591 }
2592 IM_ASSERT(0);
2593 return "Unknown";
2594 }
2595
2596
2597 //-----------------------------------------------------------------------------
2598 // [SECTION] RENDER HELPERS
2599 // Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
2600 // we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
2601 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
2602 //-----------------------------------------------------------------------------
2603
FindRenderedTextEnd(const char * text,const char * text_end)2604 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2605 {
2606 const char* text_display_end = text;
2607 if (!text_end)
2608 text_end = (const char*)-1;
2609
2610 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2611 text_display_end++;
2612 return text_display_end;
2613 }
2614
2615 // Internal ImGui functions to render text
2616 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2617 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2618 {
2619 ImGuiContext& g = *GImGui;
2620 ImGuiWindow* window = g.CurrentWindow;
2621
2622 // Hide anything after a '##' string
2623 const char* text_display_end;
2624 if (hide_text_after_hash)
2625 {
2626 text_display_end = FindRenderedTextEnd(text, text_end);
2627 }
2628 else
2629 {
2630 if (!text_end)
2631 text_end = text + strlen(text); // FIXME-OPT
2632 text_display_end = text_end;
2633 }
2634
2635 if (text != text_display_end)
2636 {
2637 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2638 if (g.LogEnabled)
2639 LogRenderedText(&pos, text, text_display_end);
2640 }
2641 }
2642
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2643 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2644 {
2645 ImGuiContext& g = *GImGui;
2646 ImGuiWindow* window = g.CurrentWindow;
2647
2648 if (!text_end)
2649 text_end = text + strlen(text); // FIXME-OPT
2650
2651 if (text != text_end)
2652 {
2653 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2654 if (g.LogEnabled)
2655 LogRenderedText(&pos, text, text_end);
2656 }
2657 }
2658
2659 // Default clip_rect uses (pos_min,pos_max)
2660 // 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)2661 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)
2662 {
2663 // Perform CPU side clipping for single clipped element to avoid using scissor state
2664 ImVec2 pos = pos_min;
2665 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2666
2667 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2668 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2669 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2670 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2671 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2672
2673 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2674 if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2675 if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2676
2677 // Render
2678 if (need_clipping)
2679 {
2680 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2681 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2682 }
2683 else
2684 {
2685 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2686 }
2687 }
2688
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)2689 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)
2690 {
2691 // Hide anything after a '##' string
2692 const char* text_display_end = FindRenderedTextEnd(text, text_end);
2693 const int text_len = (int)(text_display_end - text);
2694 if (text_len == 0)
2695 return;
2696
2697 ImGuiContext& g = *GImGui;
2698 ImGuiWindow* window = g.CurrentWindow;
2699 RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2700 if (g.LogEnabled)
2701 LogRenderedText(&pos_min, text, text_display_end);
2702 }
2703
2704
2705 // Another overly complex function until we reorganize everything into a nice all-in-one helper.
2706 // 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.
2707 // 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)2708 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)
2709 {
2710 ImGuiContext& g = *GImGui;
2711 if (text_end_full == NULL)
2712 text_end_full = FindRenderedTextEnd(text);
2713 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
2714
2715 //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));
2716 //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));
2717 //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
2718 // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
2719 if (text_size.x > pos_max.x - pos_min.x)
2720 {
2721 // Hello wo...
2722 // | | |
2723 // min max ellipsis_max
2724 // <-> this is generally some padding value
2725
2726 const ImFont* font = draw_list->_Data->Font;
2727 const float font_size = draw_list->_Data->FontSize;
2728 const char* text_end_ellipsis = NULL;
2729
2730 ImWchar ellipsis_char = font->EllipsisChar;
2731 int ellipsis_char_count = 1;
2732 if (ellipsis_char == (ImWchar)-1)
2733 {
2734 ellipsis_char = (ImWchar)'.';
2735 ellipsis_char_count = 3;
2736 }
2737 const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char);
2738
2739 float ellipsis_glyph_width = glyph->X1; // Width of the glyph with no padding on either side
2740 float ellipsis_total_width = ellipsis_glyph_width; // Full width of entire ellipsis
2741
2742 if (ellipsis_char_count > 1)
2743 {
2744 // Full ellipsis size without free spacing after it.
2745 const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize);
2746 ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots;
2747 ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots;
2748 }
2749
2750 // We can now claim the space between pos_max.x and ellipsis_max.x
2751 const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f);
2752 float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
2753 if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
2754 {
2755 // Always display at least 1 character if there's no room for character + ellipsis
2756 text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
2757 text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
2758 }
2759 while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
2760 {
2761 // 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)
2762 text_end_ellipsis--;
2763 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
2764 }
2765
2766 // Render text, render ellipsis
2767 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
2768 float ellipsis_x = pos_min.x + text_size_clipped_x;
2769 if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x)
2770 for (int i = 0; i < ellipsis_char_count; i++)
2771 {
2772 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char);
2773 ellipsis_x += ellipsis_glyph_width;
2774 }
2775 }
2776 else
2777 {
2778 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
2779 }
2780
2781 if (g.LogEnabled)
2782 LogRenderedText(&pos_min, text, text_end_full);
2783 }
2784
2785 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2786 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2787 {
2788 ImGuiContext& g = *GImGui;
2789 ImGuiWindow* window = g.CurrentWindow;
2790 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2791 const float border_size = g.Style.FrameBorderSize;
2792 if (border && border_size > 0.0f)
2793 {
2794 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2795 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2796 }
2797 }
2798
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2799 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2800 {
2801 ImGuiContext& g = *GImGui;
2802 ImGuiWindow* window = g.CurrentWindow;
2803 const float border_size = g.Style.FrameBorderSize;
2804 if (border_size > 0.0f)
2805 {
2806 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2807 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2808 }
2809 }
2810
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2811 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2812 {
2813 ImGuiContext& g = *GImGui;
2814 if (id != g.NavId)
2815 return;
2816 if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2817 return;
2818 ImGuiWindow* window = g.CurrentWindow;
2819 if (window->DC.NavHideHighlightOneFrame)
2820 return;
2821
2822 float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2823 ImRect display_rect = bb;
2824 display_rect.ClipWith(window->ClipRect);
2825 if (flags & ImGuiNavHighlightFlags_TypeDefault)
2826 {
2827 const float THICKNESS = 2.0f;
2828 const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2829 display_rect.Expand(ImVec2(DISTANCE, DISTANCE));
2830 bool fully_visible = window->ClipRect.Contains(display_rect);
2831 if (!fully_visible)
2832 window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2833 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);
2834 if (!fully_visible)
2835 window->DrawList->PopClipRect();
2836 }
2837 if (flags & ImGuiNavHighlightFlags_TypeThin)
2838 {
2839 window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2840 }
2841 }
2842
2843 //-----------------------------------------------------------------------------
2844 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2845 //-----------------------------------------------------------------------------
2846
2847 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2848 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst(NULL)
2849 {
2850 memset(this, 0, sizeof(*this));
2851 Name = ImStrdup(name);
2852 NameBufLen = (int)strlen(name) + 1;
2853 ID = ImHashStr(name);
2854 IDStack.push_back(ID);
2855 MoveId = GetID("#MOVE");
2856 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2857 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2858 AutoFitFramesX = AutoFitFramesY = -1;
2859 AutoPosLastDirection = ImGuiDir_None;
2860 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2861 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2862 LastFrameActive = -1;
2863 LastTimeActive = -1.0f;
2864 FontWindowScale = 1.0f;
2865 SettingsOffset = -1;
2866 DrawList = &DrawListInst;
2867 DrawList->_Data = &context->DrawListSharedData;
2868 DrawList->_OwnerName = Name;
2869 }
2870
~ImGuiWindow()2871 ImGuiWindow::~ImGuiWindow()
2872 {
2873 IM_ASSERT(DrawList == &DrawListInst);
2874 IM_DELETE(Name);
2875 for (int i = 0; i != ColumnsStorage.Size; i++)
2876 ColumnsStorage[i].~ImGuiOldColumns();
2877 }
2878
GetID(const char * str,const char * str_end)2879 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2880 {
2881 ImGuiID seed = IDStack.back();
2882 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2883 ImGui::KeepAliveID(id);
2884 #ifdef IMGUI_ENABLE_TEST_ENGINE
2885 ImGuiContext& g = *GImGui;
2886 IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
2887 #endif
2888 return id;
2889 }
2890
GetID(const void * ptr)2891 ImGuiID ImGuiWindow::GetID(const void* ptr)
2892 {
2893 ImGuiID seed = IDStack.back();
2894 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2895 ImGui::KeepAliveID(id);
2896 #ifdef IMGUI_ENABLE_TEST_ENGINE
2897 ImGuiContext& g = *GImGui;
2898 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
2899 #endif
2900 return id;
2901 }
2902
GetID(int n)2903 ImGuiID ImGuiWindow::GetID(int n)
2904 {
2905 ImGuiID seed = IDStack.back();
2906 ImGuiID id = ImHashData(&n, sizeof(n), seed);
2907 ImGui::KeepAliveID(id);
2908 #ifdef IMGUI_ENABLE_TEST_ENGINE
2909 ImGuiContext& g = *GImGui;
2910 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
2911 #endif
2912 return id;
2913 }
2914
GetIDNoKeepAlive(const char * str,const char * str_end)2915 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2916 {
2917 ImGuiID seed = IDStack.back();
2918 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2919 #ifdef IMGUI_ENABLE_TEST_ENGINE
2920 ImGuiContext& g = *GImGui;
2921 IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
2922 #endif
2923 return id;
2924 }
2925
GetIDNoKeepAlive(const void * ptr)2926 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2927 {
2928 ImGuiID seed = IDStack.back();
2929 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2930 #ifdef IMGUI_ENABLE_TEST_ENGINE
2931 ImGuiContext& g = *GImGui;
2932 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
2933 #endif
2934 return id;
2935 }
2936
GetIDNoKeepAlive(int n)2937 ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
2938 {
2939 ImGuiID seed = IDStack.back();
2940 ImGuiID id = ImHashData(&n, sizeof(n), seed);
2941 #ifdef IMGUI_ENABLE_TEST_ENGINE
2942 ImGuiContext& g = *GImGui;
2943 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
2944 #endif
2945 return id;
2946 }
2947
2948 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2949 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2950 {
2951 ImGuiID seed = IDStack.back();
2952 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) };
2953 ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
2954 ImGui::KeepAliveID(id);
2955 return id;
2956 }
2957
SetCurrentWindow(ImGuiWindow * window)2958 static void SetCurrentWindow(ImGuiWindow* window)
2959 {
2960 ImGuiContext& g = *GImGui;
2961 g.CurrentWindow = window;
2962 g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
2963 if (window)
2964 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2965 }
2966
GcCompactTransientMiscBuffers()2967 void ImGui::GcCompactTransientMiscBuffers()
2968 {
2969 ImGuiContext& g = *GImGui;
2970 g.ItemFlagsStack.clear();
2971 g.GroupStack.clear();
2972 TableGcCompactSettings();
2973 }
2974
2975 // Free up/compact internal window buffers, we can use this when a window becomes unused.
2976 // Not freed:
2977 // - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)
2978 // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
GcCompactTransientWindowBuffers(ImGuiWindow * window)2979 void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
2980 {
2981 window->MemoryCompacted = true;
2982 window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
2983 window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
2984 window->IDStack.clear();
2985 window->DrawList->_ClearFreeMemory();
2986 window->DC.ChildWindows.clear();
2987 window->DC.ItemWidthStack.clear();
2988 window->DC.TextWrapPosStack.clear();
2989 }
2990
GcAwakeTransientWindowBuffers(ImGuiWindow * window)2991 void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
2992 {
2993 // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
2994 // The other buffers tends to amortize much faster.
2995 window->MemoryCompacted = false;
2996 window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
2997 window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
2998 window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
2999 }
3000
SetActiveID(ImGuiID id,ImGuiWindow * window)3001 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
3002 {
3003 ImGuiContext& g = *GImGui;
3004 g.ActiveIdIsJustActivated = (g.ActiveId != id);
3005 if (g.ActiveIdIsJustActivated)
3006 {
3007 g.ActiveIdTimer = 0.0f;
3008 g.ActiveIdHasBeenPressedBefore = false;
3009 g.ActiveIdHasBeenEditedBefore = false;
3010 if (id != 0)
3011 {
3012 g.LastActiveId = id;
3013 g.LastActiveIdTimer = 0.0f;
3014 }
3015 }
3016 g.ActiveId = id;
3017 g.ActiveIdAllowOverlap = false;
3018 g.ActiveIdNoClearOnFocusLoss = false;
3019 g.ActiveIdWindow = window;
3020 g.ActiveIdHasBeenEditedThisFrame = false;
3021 if (id)
3022 {
3023 g.ActiveIdIsAlive = id;
3024 g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
3025 }
3026
3027 // Clear declaration of inputs claimed by the widget
3028 // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
3029 g.ActiveIdUsingMouseWheel = false;
3030 g.ActiveIdUsingNavDirMask = 0x00;
3031 g.ActiveIdUsingNavInputMask = 0x00;
3032 g.ActiveIdUsingKeyInputMask = 0x00;
3033 }
3034
ClearActiveID()3035 void ImGui::ClearActiveID()
3036 {
3037 SetActiveID(0, NULL); // g.ActiveId = 0;
3038 }
3039
SetHoveredID(ImGuiID id)3040 void ImGui::SetHoveredID(ImGuiID id)
3041 {
3042 ImGuiContext& g = *GImGui;
3043 g.HoveredId = id;
3044 g.HoveredIdAllowOverlap = false;
3045 g.HoveredIdUsingMouseWheel = false;
3046 if (id != 0 && g.HoveredIdPreviousFrame != id)
3047 g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
3048 }
3049
GetHoveredID()3050 ImGuiID ImGui::GetHoveredID()
3051 {
3052 ImGuiContext& g = *GImGui;
3053 return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
3054 }
3055
KeepAliveID(ImGuiID id)3056 void ImGui::KeepAliveID(ImGuiID id)
3057 {
3058 ImGuiContext& g = *GImGui;
3059 if (g.ActiveId == id)
3060 g.ActiveIdIsAlive = id;
3061 if (g.ActiveIdPreviousFrame == id)
3062 g.ActiveIdPreviousFrameIsAlive = true;
3063 }
3064
MarkItemEdited(ImGuiID id)3065 void ImGui::MarkItemEdited(ImGuiID id)
3066 {
3067 // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
3068 // 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.
3069 ImGuiContext& g = *GImGui;
3070 IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
3071 IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out.
3072 //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
3073 g.ActiveIdHasBeenEditedThisFrame = true;
3074 g.ActiveIdHasBeenEditedBefore = true;
3075 g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
3076 }
3077
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)3078 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
3079 {
3080 // An active popup disable hovering on other windows (apart from its own children)
3081 // FIXME-OPT: This could be cached/stored within the window.
3082 ImGuiContext& g = *GImGui;
3083 if (g.NavWindow)
3084 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
3085 if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
3086 {
3087 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
3088 // NB: The order of those two tests is important because Modal windows are also Popups.
3089 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
3090 return false;
3091 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
3092 return false;
3093 }
3094 return true;
3095 }
3096
3097 // This is roughly matching the behavior of internal-facing ItemHoverable()
3098 // - 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()
3099 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)3100 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
3101 {
3102 ImGuiContext& g = *GImGui;
3103 ImGuiWindow* window = g.CurrentWindow;
3104 if (g.NavDisableMouseHover && !g.NavDisableHighlight)
3105 return IsItemFocused();
3106
3107 // Test for bounding box overlap, as updated as ItemAdd()
3108 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
3109 return false;
3110 IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function
3111
3112 // Test if we are hovering the right window (our window could be behind another window)
3113 // [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.
3114 // 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.
3115 //if (g.HoveredWindow != window)
3116 // return false;
3117 if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
3118 return false;
3119
3120 // Test if another item is active (e.g. being dragged)
3121 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
3122 if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
3123 return false;
3124
3125 // Test if interactions on this window are blocked by an active popup or modal.
3126 // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
3127 if (!IsWindowContentHoverable(window, flags))
3128 return false;
3129
3130 // Test if the item is disabled
3131 if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
3132 return false;
3133
3134 // Special handling for calling after Begin() which represent the title bar or tab.
3135 // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
3136 if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
3137 return false;
3138 return true;
3139 }
3140
3141 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)3142 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
3143 {
3144 ImGuiContext& g = *GImGui;
3145 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
3146 return false;
3147
3148 ImGuiWindow* window = g.CurrentWindow;
3149 if (g.HoveredWindow != window)
3150 return false;
3151 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
3152 return false;
3153 if (!IsMouseHoveringRect(bb.Min, bb.Max))
3154 return false;
3155 if (g.NavDisableMouseHover)
3156 return false;
3157 if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None) || (window->DC.ItemFlags & ImGuiItemFlags_Disabled))
3158 {
3159 g.HoveredIdDisabled = true;
3160 return false;
3161 }
3162
3163 // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
3164 // hover test in widgets code. We could also decide to split this function is two.
3165 if (id != 0)
3166 {
3167 SetHoveredID(id);
3168
3169 // [DEBUG] Item Picker tool!
3170 // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
3171 // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
3172 // items if we perform the test in ItemAdd(), but that would incur a small runtime cost.
3173 // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd().
3174 if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
3175 GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
3176 if (g.DebugItemPickerBreakId == id)
3177 IM_DEBUG_BREAK();
3178 }
3179
3180 return true;
3181 }
3182
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)3183 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
3184 {
3185 ImGuiContext& g = *GImGui;
3186 ImGuiWindow* window = g.CurrentWindow;
3187 if (!bb.Overlaps(window->ClipRect))
3188 if (id == 0 || (id != g.ActiveId && id != g.NavId))
3189 if (clip_even_when_logged || !g.LogEnabled)
3190 return true;
3191 return false;
3192 }
3193
3194 // This is also inlined in ItemAdd()
3195 // Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set window->DC.LastItemDisplayRect!
SetLastItemData(ImGuiWindow * window,ImGuiID item_id,ImGuiItemStatusFlags item_flags,const ImRect & item_rect)3196 void ImGui::SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags item_flags, const ImRect& item_rect)
3197 {
3198 window->DC.LastItemId = item_id;
3199 window->DC.LastItemStatusFlags = item_flags;
3200 window->DC.LastItemRect = item_rect;
3201 }
3202
3203 // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
FocusableItemRegister(ImGuiWindow * window,ImGuiID id)3204 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id)
3205 {
3206 ImGuiContext& g = *GImGui;
3207
3208 // Increment counters
3209 const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
3210 window->DC.FocusCounterRegular++;
3211 if (is_tab_stop)
3212 window->DC.FocusCounterTabStop++;
3213
3214 // Process TAB/Shift-TAB to tab *OUT* of the currently focused item.
3215 // (Note that we can always TAB out of a widget that doesn't allow tabbing in)
3216 if (g.ActiveId == id && g.FocusTabPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.FocusRequestNextWindow == NULL)
3217 {
3218 g.FocusRequestNextWindow = window;
3219 g.FocusRequestNextCounterTabStop = window->DC.FocusCounterTabStop + (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.
3220 }
3221
3222 // Handle focus requests
3223 if (g.FocusRequestCurrWindow == window)
3224 {
3225 if (window->DC.FocusCounterRegular == g.FocusRequestCurrCounterRegular)
3226 return true;
3227 if (is_tab_stop && window->DC.FocusCounterTabStop == g.FocusRequestCurrCounterTabStop)
3228 {
3229 g.NavJustTabbedId = id;
3230 return true;
3231 }
3232
3233 // If another item is about to be focused, we clear our own active id
3234 if (g.ActiveId == id)
3235 ClearActiveID();
3236 }
3237
3238 return false;
3239 }
3240
FocusableItemUnregister(ImGuiWindow * window)3241 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
3242 {
3243 window->DC.FocusCounterRegular--;
3244 window->DC.FocusCounterTabStop--;
3245 }
3246
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)3247 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
3248 {
3249 if (wrap_pos_x < 0.0f)
3250 return 0.0f;
3251
3252 ImGuiContext& g = *GImGui;
3253 ImGuiWindow* window = g.CurrentWindow;
3254 if (wrap_pos_x == 0.0f)
3255 {
3256 // We could decide to setup a default wrapping max point for auto-resizing windows,
3257 // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
3258 //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
3259 // wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
3260 //else
3261 wrap_pos_x = window->WorkRect.Max.x;
3262 }
3263 else if (wrap_pos_x > 0.0f)
3264 {
3265 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
3266 }
3267
3268 return ImMax(wrap_pos_x - pos.x, 1.0f);
3269 }
3270
3271 // IM_ALLOC() == ImGui::MemAlloc()
MemAlloc(size_t size)3272 void* ImGui::MemAlloc(size_t size)
3273 {
3274 if (ImGuiContext* ctx = GImGui)
3275 ctx->IO.MetricsActiveAllocations++;
3276 return GImAllocatorAllocFunc(size, GImAllocatorUserData);
3277 }
3278
3279 // IM_FREE() == ImGui::MemFree()
MemFree(void * ptr)3280 void ImGui::MemFree(void* ptr)
3281 {
3282 if (ptr)
3283 if (ImGuiContext* ctx = GImGui)
3284 ctx->IO.MetricsActiveAllocations--;
3285 return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
3286 }
3287
GetClipboardText()3288 const char* ImGui::GetClipboardText()
3289 {
3290 ImGuiContext& g = *GImGui;
3291 return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : "";
3292 }
3293
SetClipboardText(const char * text)3294 void ImGui::SetClipboardText(const char* text)
3295 {
3296 ImGuiContext& g = *GImGui;
3297 if (g.IO.SetClipboardTextFn)
3298 g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);
3299 }
3300
GetVersion()3301 const char* ImGui::GetVersion()
3302 {
3303 return IMGUI_VERSION;
3304 }
3305
3306 // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3307 // 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()3308 ImGuiContext* ImGui::GetCurrentContext()
3309 {
3310 return GImGui;
3311 }
3312
SetCurrentContext(ImGuiContext * ctx)3313 void ImGui::SetCurrentContext(ImGuiContext* ctx)
3314 {
3315 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3316 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3317 #else
3318 GImGui = ctx;
3319 #endif
3320 }
3321
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)3322 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
3323 {
3324 GImAllocatorAllocFunc = alloc_func;
3325 GImAllocatorFreeFunc = free_func;
3326 GImAllocatorUserData = user_data;
3327 }
3328
CreateContext(ImFontAtlas * shared_font_atlas)3329 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3330 {
3331 ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3332 if (GImGui == NULL)
3333 SetCurrentContext(ctx);
3334 Initialize(ctx);
3335 return ctx;
3336 }
3337
DestroyContext(ImGuiContext * ctx)3338 void ImGui::DestroyContext(ImGuiContext* ctx)
3339 {
3340 if (ctx == NULL)
3341 ctx = GImGui;
3342 Shutdown(ctx);
3343 if (GImGui == ctx)
3344 SetCurrentContext(NULL);
3345 IM_DELETE(ctx);
3346 }
3347
3348 // No specific ordering/dependency support, will see as needed
AddContextHook(ImGuiContext * ctx,const ImGuiContextHook * hook)3349 void ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook)
3350 {
3351 ImGuiContext& g = *ctx;
3352 IM_ASSERT(hook->Callback != NULL);
3353 g.Hooks.push_back(*hook);
3354 }
3355
3356 // Call context hooks (used by e.g. test engine)
3357 // We assume a small number of hooks so all stored in same array
CallContextHooks(ImGuiContext * ctx,ImGuiContextHookType hook_type)3358 void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)
3359 {
3360 ImGuiContext& g = *ctx;
3361 for (int n = 0; n < g.Hooks.Size; n++)
3362 if (g.Hooks[n].Type == hook_type)
3363 g.Hooks[n].Callback(&g, &g.Hooks[n]);
3364 }
3365
GetIO()3366 ImGuiIO& ImGui::GetIO()
3367 {
3368 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3369 return GImGui->IO;
3370 }
3371
3372 // Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
GetDrawData()3373 ImDrawData* ImGui::GetDrawData()
3374 {
3375 ImGuiContext& g = *GImGui;
3376 return g.DrawData.Valid ? &g.DrawData : NULL;
3377 }
3378
GetTime()3379 double ImGui::GetTime()
3380 {
3381 return GImGui->Time;
3382 }
3383
GetFrameCount()3384 int ImGui::GetFrameCount()
3385 {
3386 return GImGui->FrameCount;
3387 }
3388
GetBackgroundDrawList()3389 ImDrawList* ImGui::GetBackgroundDrawList()
3390 {
3391 return &GImGui->BackgroundDrawList;
3392 }
3393
GetForegroundDrawList()3394 ImDrawList* ImGui::GetForegroundDrawList()
3395 {
3396 return &GImGui->ForegroundDrawList;
3397 }
3398
GetDrawListSharedData()3399 ImDrawListSharedData* ImGui::GetDrawListSharedData()
3400 {
3401 return &GImGui->DrawListSharedData;
3402 }
3403
StartMouseMovingWindow(ImGuiWindow * window)3404 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3405 {
3406 // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3407 // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3408 // This is because we want ActiveId to be set even when the window is not permitted to move.
3409 ImGuiContext& g = *GImGui;
3410 FocusWindow(window);
3411 SetActiveID(window->MoveId, window);
3412 g.NavDisableHighlight = true;
3413 g.ActiveIdNoClearOnFocusLoss = true;
3414 g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
3415
3416 bool can_move_window = true;
3417 if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3418 can_move_window = false;
3419 if (can_move_window)
3420 g.MovingWindow = window;
3421 }
3422
3423 // Handle mouse moving window
3424 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
3425 // FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId.
3426 // This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
3427 // but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
UpdateMouseMovingWindowNewFrame()3428 void ImGui::UpdateMouseMovingWindowNewFrame()
3429 {
3430 ImGuiContext& g = *GImGui;
3431 if (g.MovingWindow != NULL)
3432 {
3433 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3434 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3435 KeepAliveID(g.ActiveId);
3436 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3437 ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3438 if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3439 {
3440 ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3441 if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3442 {
3443 MarkIniSettingsDirty(moving_window);
3444 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3445 }
3446 FocusWindow(g.MovingWindow);
3447 }
3448 else
3449 {
3450 ClearActiveID();
3451 g.MovingWindow = NULL;
3452 }
3453 }
3454 else
3455 {
3456 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3457 if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3458 {
3459 KeepAliveID(g.ActiveId);
3460 if (!g.IO.MouseDown[0])
3461 ClearActiveID();
3462 }
3463 }
3464 }
3465
3466 // Initiate moving window when clicking on empty space or title bar.
3467 // Handle left-click and right-click focus.
UpdateMouseMovingWindowEndFrame()3468 void ImGui::UpdateMouseMovingWindowEndFrame()
3469 {
3470 ImGuiContext& g = *GImGui;
3471 if (g.ActiveId != 0 || g.HoveredId != 0)
3472 return;
3473
3474 // Unless we just made a window/popup appear
3475 if (g.NavWindow && g.NavWindow->Appearing)
3476 return;
3477
3478 // Click on empty space to focus window and start moving (after we're done with all our widgets)
3479 if (g.IO.MouseClicked[0])
3480 {
3481 // Handle the edge case of a popup being closed while clicking in its empty space.
3482 // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
3483 ImGuiWindow* root_window = g.HoveredRootWindow;
3484 const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel);
3485
3486 if (root_window != NULL && !is_closed_popup)
3487 {
3488 StartMouseMovingWindow(g.HoveredWindow); //-V595
3489
3490 // Cancel moving if clicked outside of title bar
3491 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(root_window->Flags & ImGuiWindowFlags_NoTitleBar))
3492 if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3493 g.MovingWindow = NULL;
3494
3495 // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already)
3496 if (g.HoveredIdDisabled)
3497 g.MovingWindow = NULL;
3498 }
3499 else if (root_window == NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
3500 {
3501 // Clicking on void disable focus
3502 FocusWindow(NULL);
3503 }
3504 }
3505
3506 // With right mouse button we close popups without changing focus based on where the mouse is aimed
3507 // Instead, focus will be restored to the window under the bottom-most closed popup.
3508 // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
3509 if (g.IO.MouseClicked[1])
3510 {
3511 // Find the top-most window between HoveredWindow and the top-most Modal Window.
3512 // This is where we can trim the popup stack.
3513 ImGuiWindow* modal = GetTopMostPopupModal();
3514 bool hovered_window_above_modal = g.HoveredWindow && IsWindowAbove(g.HoveredWindow, modal);
3515 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
3516 }
3517 }
3518
IsWindowActiveAndVisible(ImGuiWindow * window)3519 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3520 {
3521 return (window->Active) && (!window->Hidden);
3522 }
3523
UpdateMouseInputs()3524 static void ImGui::UpdateMouseInputs()
3525 {
3526 ImGuiContext& g = *GImGui;
3527
3528 // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3529 if (IsMousePosValid(&g.IO.MousePos))
3530 g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3531
3532 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3533 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3534 g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3535 else
3536 g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3537 if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3538 g.NavDisableMouseHover = false;
3539
3540 g.IO.MousePosPrev = g.IO.MousePos;
3541 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3542 {
3543 g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3544 g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3545 g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3546 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;
3547 g.IO.MouseDoubleClicked[i] = false;
3548 if (g.IO.MouseClicked[i])
3549 {
3550 if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3551 {
3552 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3553 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3554 g.IO.MouseDoubleClicked[i] = true;
3555 g.IO.MouseClickedTime[i] = -g.IO.MouseDoubleClickTime * 2.0f; // Mark as "old enough" so the third click isn't turned into a double-click
3556 }
3557 else
3558 {
3559 g.IO.MouseClickedTime[i] = g.Time;
3560 }
3561 g.IO.MouseClickedPos[i] = g.IO.MousePos;
3562 g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i];
3563 g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3564 g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3565 }
3566 else if (g.IO.MouseDown[i])
3567 {
3568 // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3569 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3570 g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3571 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);
3572 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);
3573 }
3574 if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i])
3575 g.IO.MouseDownWasDoubleClick[i] = false;
3576 if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3577 g.NavDisableMouseHover = false;
3578 }
3579 }
3580
StartLockWheelingWindow(ImGuiWindow * window)3581 static void StartLockWheelingWindow(ImGuiWindow* window)
3582 {
3583 ImGuiContext& g = *GImGui;
3584 if (g.WheelingWindow == window)
3585 return;
3586 g.WheelingWindow = window;
3587 g.WheelingWindowRefMousePos = g.IO.MousePos;
3588 g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER;
3589 }
3590
UpdateMouseWheel()3591 void ImGui::UpdateMouseWheel()
3592 {
3593 ImGuiContext& g = *GImGui;
3594
3595 // Reset the locked window if we move the mouse or after the timer elapses
3596 if (g.WheelingWindow != NULL)
3597 {
3598 g.WheelingWindowTimer -= g.IO.DeltaTime;
3599 if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
3600 g.WheelingWindowTimer = 0.0f;
3601 if (g.WheelingWindowTimer <= 0.0f)
3602 {
3603 g.WheelingWindow = NULL;
3604 g.WheelingWindowTimer = 0.0f;
3605 }
3606 }
3607
3608 if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3609 return;
3610
3611 if ((g.ActiveId != 0 && g.ActiveIdUsingMouseWheel) || (g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrameUsingMouseWheel))
3612 return;
3613
3614 ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
3615 if (!window || window->Collapsed)
3616 return;
3617
3618 // Zoom / Scale window
3619 // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
3620 if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3621 {
3622 StartLockWheelingWindow(window);
3623 const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3624 const float scale = new_font_scale / window->FontWindowScale;
3625 window->FontWindowScale = new_font_scale;
3626 if (!(window->Flags & ImGuiWindowFlags_ChildWindow))
3627 {
3628 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3629 SetWindowPos(window, window->Pos + offset, 0);
3630 window->Size = ImFloor(window->Size * scale);
3631 window->SizeFull = ImFloor(window->SizeFull * scale);
3632 }
3633 return;
3634 }
3635
3636 // Mouse wheel scrolling
3637 // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent
3638
3639 // Vertical Mouse Wheel scrolling
3640 const float wheel_y = (g.IO.MouseWheel != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
3641 if (wheel_y != 0.0f && !g.IO.KeyCtrl)
3642 {
3643 StartLockWheelingWindow(window);
3644 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3645 window = window->ParentWindow;
3646 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3647 {
3648 float max_step = window->InnerRect.GetHeight() * 0.67f;
3649 float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
3650 SetScrollY(window, window->Scroll.y - wheel_y * scroll_step);
3651 }
3652 }
3653
3654 // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held
3655 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;
3656 if (wheel_x != 0.0f && !g.IO.KeyCtrl)
3657 {
3658 StartLockWheelingWindow(window);
3659 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3660 window = window->ParentWindow;
3661 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3662 {
3663 float max_step = window->InnerRect.GetWidth() * 0.67f;
3664 float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
3665 SetScrollX(window, window->Scroll.x - wheel_x * scroll_step);
3666 }
3667 }
3668 }
3669
UpdateTabFocus()3670 void ImGui::UpdateTabFocus()
3671 {
3672 ImGuiContext& g = *GImGui;
3673
3674 // Pressing TAB activate widget focus
3675 g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab));
3676 if (g.ActiveId == 0 && g.FocusTabPressed)
3677 {
3678 // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also
3679 // manipulate the Next fields even, even though they will be turned into Curr fields by the code below.
3680 g.FocusRequestNextWindow = g.NavWindow;
3681 g.FocusRequestNextCounterRegular = INT_MAX;
3682 if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3683 g.FocusRequestNextCounterTabStop = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3684 else
3685 g.FocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0;
3686 }
3687
3688 // Turn queued focus request into current one
3689 g.FocusRequestCurrWindow = NULL;
3690 g.FocusRequestCurrCounterRegular = g.FocusRequestCurrCounterTabStop = INT_MAX;
3691 if (g.FocusRequestNextWindow != NULL)
3692 {
3693 ImGuiWindow* window = g.FocusRequestNextWindow;
3694 g.FocusRequestCurrWindow = window;
3695 if (g.FocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1)
3696 g.FocusRequestCurrCounterRegular = ImModPositive(g.FocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1);
3697 if (g.FocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1)
3698 g.FocusRequestCurrCounterTabStop = ImModPositive(g.FocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1);
3699 g.FocusRequestNextWindow = NULL;
3700 g.FocusRequestNextCounterRegular = g.FocusRequestNextCounterTabStop = INT_MAX;
3701 }
3702
3703 g.NavIdTabCounter = INT_MAX;
3704 }
3705
3706 // 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()3707 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3708 {
3709 ImGuiContext& g = *GImGui;
3710
3711 // Find the window hovered by mouse:
3712 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3713 // - 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.
3714 // - 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.
3715 bool clear_hovered_windows = false;
3716 FindHoveredWindow();
3717
3718 // Modal windows prevents mouse from hovering behind them.
3719 ImGuiWindow* modal_window = GetTopMostPopupModal();
3720 if (modal_window && g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3721 clear_hovered_windows = true;
3722
3723 // Disabled mouse?
3724 if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3725 clear_hovered_windows = true;
3726
3727 // 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.
3728 int mouse_earliest_button_down = -1;
3729 bool mouse_any_down = false;
3730 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3731 {
3732 if (g.IO.MouseClicked[i])
3733 g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0);
3734 mouse_any_down |= g.IO.MouseDown[i];
3735 if (g.IO.MouseDown[i])
3736 if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3737 mouse_earliest_button_down = i;
3738 }
3739 const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3740
3741 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3742 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3743 const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3744 if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3745 clear_hovered_windows = true;
3746
3747 if (clear_hovered_windows)
3748 g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL;
3749
3750 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app)
3751 if (g.WantCaptureMouseNextFrame != -1)
3752 g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3753 else
3754 g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0);
3755
3756 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
3757 if (g.WantCaptureKeyboardNextFrame != -1)
3758 g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3759 else
3760 g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3761 if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3762 g.IO.WantCaptureKeyboard = true;
3763
3764 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3765 g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3766 }
3767
GetMergedKeyModFlags()3768 ImGuiKeyModFlags ImGui::GetMergedKeyModFlags()
3769 {
3770 ImGuiContext& g = *GImGui;
3771 ImGuiKeyModFlags key_mod_flags = ImGuiKeyModFlags_None;
3772 if (g.IO.KeyCtrl) { key_mod_flags |= ImGuiKeyModFlags_Ctrl; }
3773 if (g.IO.KeyShift) { key_mod_flags |= ImGuiKeyModFlags_Shift; }
3774 if (g.IO.KeyAlt) { key_mod_flags |= ImGuiKeyModFlags_Alt; }
3775 if (g.IO.KeySuper) { key_mod_flags |= ImGuiKeyModFlags_Super; }
3776 return key_mod_flags;
3777 }
3778
NewFrame()3779 void ImGui::NewFrame()
3780 {
3781 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3782 ImGuiContext& g = *GImGui;
3783
3784 CallContextHooks(&g, ImGuiContextHookType_NewFramePre);
3785
3786 // Check and assert for various common IO and Configuration mistakes
3787 ErrorCheckNewFrameSanityChecks();
3788
3789 // Load settings on first frame, save settings when modified (after a delay)
3790 UpdateSettings();
3791
3792 g.Time += g.IO.DeltaTime;
3793 g.WithinFrameScope = true;
3794 g.FrameCount += 1;
3795 g.TooltipOverrideCount = 0;
3796 g.WindowsActiveCount = 0;
3797 g.MenusIdSubmittedThisFrame.resize(0);
3798
3799 // Calculate frame-rate for the user, as a purely luxurious feature
3800 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3801 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3802 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3803 g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3804
3805 // Setup current font and draw list shared data
3806 g.IO.Fonts->Locked = true;
3807 SetCurrentFont(GetDefaultFont());
3808 IM_ASSERT(g.Font->IsLoaded());
3809 g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3810 g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3811 g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError);
3812 g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
3813 if (g.Style.AntiAliasedLines)
3814 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
3815 if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines))
3816 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
3817 if (g.Style.AntiAliasedFill)
3818 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
3819 if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
3820 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
3821
3822 g.BackgroundDrawList._ResetForNewFrame();
3823 g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3824 g.BackgroundDrawList.PushClipRectFullScreen();
3825
3826 g.ForegroundDrawList._ResetForNewFrame();
3827 g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3828 g.ForegroundDrawList.PushClipRectFullScreen();
3829
3830 // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
3831 g.DrawData.Clear();
3832
3833 // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3834 if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3835 KeepAliveID(g.DragDropPayload.SourceId);
3836
3837 // Update HoveredId data
3838 if (!g.HoveredIdPreviousFrame)
3839 g.HoveredIdTimer = 0.0f;
3840 if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
3841 g.HoveredIdNotActiveTimer = 0.0f;
3842 if (g.HoveredId)
3843 g.HoveredIdTimer += g.IO.DeltaTime;
3844 if (g.HoveredId && g.ActiveId != g.HoveredId)
3845 g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
3846 g.HoveredIdPreviousFrame = g.HoveredId;
3847 g.HoveredIdPreviousFrameUsingMouseWheel = g.HoveredIdUsingMouseWheel;
3848 g.HoveredId = 0;
3849 g.HoveredIdAllowOverlap = false;
3850 g.HoveredIdUsingMouseWheel = false;
3851 g.HoveredIdDisabled = false;
3852
3853 // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
3854 if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3855 ClearActiveID();
3856 if (g.ActiveId)
3857 g.ActiveIdTimer += g.IO.DeltaTime;
3858 g.LastActiveIdTimer += g.IO.DeltaTime;
3859 g.ActiveIdPreviousFrame = g.ActiveId;
3860 g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3861 g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
3862 g.ActiveIdIsAlive = 0;
3863 g.ActiveIdHasBeenEditedThisFrame = false;
3864 g.ActiveIdPreviousFrameIsAlive = false;
3865 g.ActiveIdIsJustActivated = false;
3866 if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)
3867 g.TempInputId = 0;
3868 if (g.ActiveId == 0)
3869 {
3870 g.ActiveIdUsingNavDirMask = 0x00;
3871 g.ActiveIdUsingNavInputMask = 0x00;
3872 g.ActiveIdUsingKeyInputMask = 0x00;
3873 }
3874
3875 // Drag and drop
3876 g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3877 g.DragDropAcceptIdCurr = 0;
3878 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3879 g.DragDropWithinSource = false;
3880 g.DragDropWithinTarget = false;
3881 g.DragDropHoldJustPressedId = 0;
3882
3883 // Update keyboard input state
3884 // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools
3885 g.IO.KeyMods = GetMergedKeyModFlags();
3886 memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3887 for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3888 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;
3889
3890 // Update gamepad/keyboard navigation
3891 NavUpdate();
3892
3893 // Update mouse input state
3894 UpdateMouseInputs();
3895
3896 // Find hovered window
3897 // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
3898 UpdateHoveredWindowAndCaptureFlags();
3899
3900 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3901 UpdateMouseMovingWindowNewFrame();
3902
3903 // Background darkening/whitening
3904 if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3905 g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3906 else
3907 g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
3908
3909 g.MouseCursor = ImGuiMouseCursor_Arrow;
3910 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3911 g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3912
3913 // Mouse wheel scrolling, scale
3914 UpdateMouseWheel();
3915
3916 // Update legacy TAB focus
3917 UpdateTabFocus();
3918
3919 // Mark all windows as not visible and compact unused memory.
3920 IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
3921 const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
3922 for (int i = 0; i != g.Windows.Size; i++)
3923 {
3924 ImGuiWindow* window = g.Windows[i];
3925 window->WasActive = window->Active;
3926 window->BeginCount = 0;
3927 window->Active = false;
3928 window->WriteAccessed = false;
3929
3930 // Garbage collect transient buffers of recently unused windows
3931 if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
3932 GcCompactTransientWindowBuffers(window);
3933 }
3934
3935 // Garbage collect transient buffers of recently unused tables
3936 for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
3937 if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
3938 TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));
3939 if (g.GcCompactAll)
3940 GcCompactTransientMiscBuffers();
3941 g.GcCompactAll = false;
3942
3943 // Closing the focused window restore focus to the first active root window in descending z-order
3944 if (g.NavWindow && !g.NavWindow->WasActive)
3945 FocusTopMostWindowUnderOne(NULL, NULL);
3946
3947 // No window should be open at the beginning of the frame.
3948 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3949 g.CurrentWindowStack.resize(0);
3950 g.BeginPopupStack.resize(0);
3951 g.ItemFlagsStack.resize(0);
3952 g.ItemFlagsStack.push_back(ImGuiItemFlags_Default_);
3953 g.GroupStack.resize(0);
3954 ClosePopupsOverWindow(g.NavWindow, false);
3955
3956 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
3957 UpdateDebugToolItemPicker();
3958
3959 // Create implicit/fallback window - which we will only render it if the user has added something to it.
3960 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3961 // This fallback is particularly important as it avoid ImGui:: calls from crashing.
3962 g.WithinFrameScopeWithImplicitWindow = true;
3963 SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
3964 Begin("Debug##Default");
3965 IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
3966
3967 CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
3968 }
3969
3970 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
UpdateDebugToolItemPicker()3971 void ImGui::UpdateDebugToolItemPicker()
3972 {
3973 ImGuiContext& g = *GImGui;
3974 g.DebugItemPickerBreakId = 0;
3975 if (g.DebugItemPickerActive)
3976 {
3977 const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
3978 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
3979 if (ImGui::IsKeyPressedMap(ImGuiKey_Escape))
3980 g.DebugItemPickerActive = false;
3981 if (ImGui::IsMouseClicked(0) && hovered_id)
3982 {
3983 g.DebugItemPickerBreakId = hovered_id;
3984 g.DebugItemPickerActive = false;
3985 }
3986 ImGui::SetNextWindowBgAlpha(0.60f);
3987 ImGui::BeginTooltip();
3988 ImGui::Text("HoveredId: 0x%08X", hovered_id);
3989 ImGui::Text("Press ESC to abort picking.");
3990 ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
3991 ImGui::EndTooltip();
3992 }
3993 }
3994
Initialize(ImGuiContext * context)3995 void ImGui::Initialize(ImGuiContext* context)
3996 {
3997 ImGuiContext& g = *context;
3998 IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3999
4000 // Add .ini handle for ImGuiWindow type
4001 {
4002 ImGuiSettingsHandler ini_handler;
4003 ini_handler.TypeName = "Window";
4004 ini_handler.TypeHash = ImHashStr("Window");
4005 ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
4006 ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
4007 ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
4008 ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
4009 ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
4010 g.SettingsHandlers.push_back(ini_handler);
4011 }
4012
4013 #ifdef IMGUI_HAS_TABLE
4014 // Add .ini handle for ImGuiTable type
4015 TableSettingsInstallHandler(context);
4016 #endif // #ifdef IMGUI_HAS_TABLE
4017
4018 #ifdef IMGUI_HAS_DOCK
4019 #endif // #ifdef IMGUI_HAS_DOCK
4020
4021 g.Initialized = true;
4022 }
4023
4024 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)4025 void ImGui::Shutdown(ImGuiContext* context)
4026 {
4027 // 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)
4028 ImGuiContext& g = *context;
4029 if (g.IO.Fonts && g.FontAtlasOwnedByContext)
4030 {
4031 g.IO.Fonts->Locked = false;
4032 IM_DELETE(g.IO.Fonts);
4033 }
4034 g.IO.Fonts = NULL;
4035
4036 // Cleanup of other data are conditional on actually having initialized Dear ImGui.
4037 if (!g.Initialized)
4038 return;
4039
4040 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
4041 if (g.SettingsLoaded && g.IO.IniFilename != NULL)
4042 {
4043 ImGuiContext* backup_context = GImGui;
4044 SetCurrentContext(&g);
4045 SaveIniSettingsToDisk(g.IO.IniFilename);
4046 SetCurrentContext(backup_context);
4047 }
4048
4049 CallContextHooks(&g, ImGuiContextHookType_Shutdown);
4050
4051 // Clear everything else
4052 for (int i = 0; i < g.Windows.Size; i++)
4053 IM_DELETE(g.Windows[i]);
4054 g.Windows.clear();
4055 g.WindowsFocusOrder.clear();
4056 g.WindowsTempSortBuffer.clear();
4057 g.CurrentWindow = NULL;
4058 g.CurrentWindowStack.clear();
4059 g.WindowsById.Clear();
4060 g.NavWindow = NULL;
4061 g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL;
4062 g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
4063 g.MovingWindow = NULL;
4064 g.ColorStack.clear();
4065 g.StyleVarStack.clear();
4066 g.FontStack.clear();
4067 g.OpenPopupStack.clear();
4068 g.BeginPopupStack.clear();
4069 g.DrawDataBuilder.ClearFreeMemory();
4070 g.BackgroundDrawList._ClearFreeMemory();
4071 g.ForegroundDrawList._ClearFreeMemory();
4072
4073 g.TabBars.Clear();
4074 g.CurrentTabBarStack.clear();
4075 g.ShrinkWidthBuffer.clear();
4076
4077 g.Tables.Clear();
4078 g.CurrentTableStack.clear();
4079 g.DrawChannelsTempMergeBuffer.clear();
4080
4081 g.ClipboardHandlerData.clear();
4082 g.MenusIdSubmittedThisFrame.clear();
4083 g.InputTextState.ClearFreeMemory();
4084
4085 g.SettingsWindows.clear();
4086 g.SettingsHandlers.clear();
4087
4088 if (g.LogFile)
4089 {
4090 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
4091 if (g.LogFile != stdout)
4092 #endif
4093 ImFileClose(g.LogFile);
4094 g.LogFile = NULL;
4095 }
4096 g.LogBuffer.clear();
4097
4098 g.Initialized = false;
4099 }
4100
4101 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)4102 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
4103 {
4104 const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
4105 const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
4106 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
4107 return d;
4108 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
4109 return d;
4110 return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
4111 }
4112
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)4113 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
4114 {
4115 out_sorted_windows->push_back(window);
4116 if (window->Active)
4117 {
4118 int count = window->DC.ChildWindows.Size;
4119 if (count > 1)
4120 ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
4121 for (int i = 0; i < count; i++)
4122 {
4123 ImGuiWindow* child = window->DC.ChildWindows[i];
4124 if (child->Active)
4125 AddWindowToSortBuffer(out_sorted_windows, child);
4126 }
4127 }
4128 }
4129
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)4130 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
4131 {
4132 // Remove trailing command if unused.
4133 // Technically we could return directly instead of popping, but this make things looks neat in Metrics/Debugger window as well.
4134 draw_list->_PopUnusedDrawCmd();
4135 if (draw_list->CmdBuffer.Size == 0)
4136 return;
4137
4138 // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
4139 // May trigger for you if you are using PrimXXX functions incorrectly.
4140 IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
4141 IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
4142 if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
4143 IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
4144
4145 // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
4146 // If this assert triggers because you are drawing lots of stuff manually:
4147 // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
4148 // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents.
4149 // - If you want large meshes with more than 64K vertices, you can either:
4150 // (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
4151 // Most example backends already support this from 1.71. Pre-1.71 backends won't.
4152 // 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.
4153 // (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
4154 // Most example backends already support this. For example, the OpenGL example code detect index size at compile-time:
4155 // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
4156 // Your own engine or render API may use different parameters or function calls to specify index sizes.
4157 // 2 and 4 bytes indices are generally supported by most graphics API.
4158 // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
4159 // the 64K limit to split your draw commands in multiple draw lists.
4160 if (sizeof(ImDrawIdx) == 2)
4161 IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
4162
4163 out_list->push_back(draw_list);
4164 }
4165
AddWindowToDrawData(ImVector<ImDrawList * > * out_render_list,ImGuiWindow * window)4166 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
4167 {
4168 ImGuiContext& g = *GImGui;
4169 g.IO.MetricsRenderWindows++;
4170 AddDrawListToDrawData(out_render_list, window->DrawList);
4171 for (int i = 0; i < window->DC.ChildWindows.Size; i++)
4172 {
4173 ImGuiWindow* child = window->DC.ChildWindows[i];
4174 if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active
4175 AddWindowToDrawData(out_render_list, child);
4176 }
4177 }
4178
4179 // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
AddRootWindowToDrawData(ImGuiWindow * window)4180 static void AddRootWindowToDrawData(ImGuiWindow* window)
4181 {
4182 ImGuiContext& g = *GImGui;
4183 int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
4184 AddWindowToDrawData(&g.DrawDataBuilder.Layers[layer], window);
4185 }
4186
FlattenIntoSingleLayer()4187 void ImDrawDataBuilder::FlattenIntoSingleLayer()
4188 {
4189 int n = Layers[0].Size;
4190 int size = n;
4191 for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
4192 size += Layers[i].Size;
4193 Layers[0].resize(size);
4194 for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
4195 {
4196 ImVector<ImDrawList*>& layer = Layers[layer_n];
4197 if (layer.empty())
4198 continue;
4199 memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
4200 n += layer.Size;
4201 layer.resize(0);
4202 }
4203 }
4204
SetupDrawData(ImVector<ImDrawList * > * draw_lists,ImDrawData * draw_data)4205 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
4206 {
4207 ImGuiIO& io = ImGui::GetIO();
4208 draw_data->Valid = true;
4209 draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
4210 draw_data->CmdListsCount = draw_lists->Size;
4211 draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
4212 draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
4213 draw_data->DisplaySize = io.DisplaySize;
4214 draw_data->FramebufferScale = io.DisplayFramebufferScale;
4215 for (int n = 0; n < draw_lists->Size; n++)
4216 {
4217 draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
4218 draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
4219 }
4220 }
4221
4222 // Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
4223 // - When using this function it is sane to ensure that float are perfectly rounded to integer values,
4224 // so that e.g. (int)(max.x-min.x) in user's render produce correct result.
4225 // - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
4226 // some frequently called functions which to modify both channels and clipping simultaneously tend to use the
4227 // more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.
PushClipRect(const ImVec2 & clip_rect_min,const ImVec2 & clip_rect_max,bool intersect_with_current_clip_rect)4228 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
4229 {
4230 ImGuiWindow* window = GetCurrentWindow();
4231 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
4232 window->ClipRect = window->DrawList->_ClipRectStack.back();
4233 }
4234
PopClipRect()4235 void ImGui::PopClipRect()
4236 {
4237 ImGuiWindow* window = GetCurrentWindow();
4238 window->DrawList->PopClipRect();
4239 window->ClipRect = window->DrawList->_ClipRectStack.back();
4240 }
4241
4242 // 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()4243 void ImGui::EndFrame()
4244 {
4245 ImGuiContext& g = *GImGui;
4246 IM_ASSERT(g.Initialized);
4247
4248 // Don't process EndFrame() multiple times.
4249 if (g.FrameCountEnded == g.FrameCount)
4250 return;
4251 IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
4252
4253 CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
4254
4255 ErrorCheckEndFrameSanityChecks();
4256
4257 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
4258 if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f))
4259 {
4260 g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
4261 g.PlatformImeLastPos = g.PlatformImePos;
4262 }
4263
4264 // Hide implicit/fallback "Debug" window if it hasn't been used
4265 g.WithinFrameScopeWithImplicitWindow = false;
4266 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
4267 g.CurrentWindow->Active = false;
4268 End();
4269
4270 // Update navigation: CTRL+Tab, wrap-around requests
4271 NavEndFrame();
4272
4273 // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
4274 if (g.DragDropActive)
4275 {
4276 bool is_delivered = g.DragDropPayload.Delivery;
4277 bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
4278 if (is_delivered || is_elapsed)
4279 ClearDragDrop();
4280 }
4281
4282 // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
4283 if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
4284 {
4285 g.DragDropWithinSource = true;
4286 SetTooltip("...");
4287 g.DragDropWithinSource = false;
4288 }
4289
4290 // End frame
4291 g.WithinFrameScope = false;
4292 g.FrameCountEnded = g.FrameCount;
4293
4294 // Initiate moving window + handle left-click and right-click focus
4295 UpdateMouseMovingWindowEndFrame();
4296
4297 // Sort the window list so that all child windows are after their parent
4298 // We cannot do that on FocusWindow() because children may not exist yet
4299 g.WindowsTempSortBuffer.resize(0);
4300 g.WindowsTempSortBuffer.reserve(g.Windows.Size);
4301 for (int i = 0; i != g.Windows.Size; i++)
4302 {
4303 ImGuiWindow* window = g.Windows[i];
4304 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
4305 continue;
4306 AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
4307 }
4308
4309 // 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.
4310 IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
4311 g.Windows.swap(g.WindowsTempSortBuffer);
4312 g.IO.MetricsActiveWindows = g.WindowsActiveCount;
4313
4314 // Unlock font atlas
4315 g.IO.Fonts->Locked = false;
4316
4317 // Clear Input data for next frame
4318 g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
4319 g.IO.InputQueueCharacters.resize(0);
4320 memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
4321
4322 CallContextHooks(&g, ImGuiContextHookType_EndFramePost);
4323 }
4324
Render()4325 void ImGui::Render()
4326 {
4327 ImGuiContext& g = *GImGui;
4328 IM_ASSERT(g.Initialized);
4329
4330 if (g.FrameCountEnded != g.FrameCount)
4331 EndFrame();
4332 g.FrameCountRendered = g.FrameCount;
4333 g.IO.MetricsRenderWindows = 0;
4334 g.DrawDataBuilder.Clear();
4335
4336 CallContextHooks(&g, ImGuiContextHookType_RenderPre);
4337
4338 // Add background ImDrawList
4339 if (!g.BackgroundDrawList.VtxBuffer.empty())
4340 AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.BackgroundDrawList);
4341
4342 // Add ImDrawList to render
4343 ImGuiWindow* windows_to_render_top_most[2];
4344 windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
4345 windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
4346 for (int n = 0; n != g.Windows.Size; n++)
4347 {
4348 ImGuiWindow* window = g.Windows[n];
4349 if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
4350 AddRootWindowToDrawData(window);
4351 }
4352 for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
4353 if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
4354 AddRootWindowToDrawData(windows_to_render_top_most[n]);
4355 g.DrawDataBuilder.FlattenIntoSingleLayer();
4356
4357 // Draw software mouse cursor if requested
4358 if (g.IO.MouseDrawCursor)
4359 RenderMouseCursor(&g.ForegroundDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
4360
4361 // Add foreground ImDrawList
4362 if (!g.ForegroundDrawList.VtxBuffer.empty())
4363 AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.ForegroundDrawList);
4364
4365 // Setup ImDrawData structure for end-user
4366 SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
4367 g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
4368 g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
4369
4370 CallContextHooks(&g, ImGuiContextHookType_RenderPost);
4371 }
4372
4373 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4374 // 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)4375 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
4376 {
4377 ImGuiContext& g = *GImGui;
4378
4379 const char* text_display_end;
4380 if (hide_text_after_double_hash)
4381 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
4382 else
4383 text_display_end = text_end;
4384
4385 ImFont* font = g.Font;
4386 const float font_size = g.FontSize;
4387 if (text == text_display_end)
4388 return ImVec2(0.0f, font_size);
4389 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
4390
4391 // Round
4392 text_size.x = IM_FLOOR(text_size.x + 0.95f);
4393
4394 return text_size;
4395 }
4396
4397 // Find window given position, search front-to-back
4398 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
4399 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
4400 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()4401 static void FindHoveredWindow()
4402 {
4403 ImGuiContext& g = *GImGui;
4404
4405 ImGuiWindow* hovered_window = NULL;
4406 ImGuiWindow* hovered_window_ignoring_moving_window = NULL;
4407 if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
4408 hovered_window = g.MovingWindow;
4409
4410 ImVec2 padding_regular = g.Style.TouchExtraPadding;
4411 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;
4412 for (int i = g.Windows.Size - 1; i >= 0; i--)
4413 {
4414 ImGuiWindow* window = g.Windows[i];
4415 if (!window->Active || window->Hidden)
4416 continue;
4417 if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
4418 continue;
4419
4420 // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4421 ImRect bb(window->OuterRectClipped);
4422 if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
4423 bb.Expand(padding_regular);
4424 else
4425 bb.Expand(padding_for_resize_from_edges);
4426 if (!bb.Contains(g.IO.MousePos))
4427 continue;
4428
4429 // Support for one rectangular hole in any given window
4430 // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
4431 if (window->HitTestHoleSize.x != 0)
4432 {
4433 ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
4434 ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
4435 if (ImRect(hole_pos, hole_pos + hole_size).Contains(g.IO.MousePos))
4436 continue;
4437 }
4438
4439 if (hovered_window == NULL)
4440 hovered_window = window;
4441 if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow))
4442 hovered_window_ignoring_moving_window = window;
4443 if (hovered_window && hovered_window_ignoring_moving_window)
4444 break;
4445 }
4446
4447 g.HoveredWindow = hovered_window;
4448 g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
4449 g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window;
4450 }
4451
4452 // Test if mouse cursor is hovering given rectangle
4453 // NB- Rectangle is clipped by our current clip setting
4454 // 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)4455 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4456 {
4457 ImGuiContext& g = *GImGui;
4458
4459 // Clip
4460 ImRect rect_clipped(r_min, r_max);
4461 if (clip)
4462 rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
4463
4464 // Expand for touch input
4465 const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4466 if (!rect_for_touch.Contains(g.IO.MousePos))
4467 return false;
4468 return true;
4469 }
4470
GetKeyIndex(ImGuiKey imgui_key)4471 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4472 {
4473 IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4474 ImGuiContext& g = *GImGui;
4475 return g.IO.KeyMap[imgui_key];
4476 }
4477
4478 // Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]!
4479 // Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]!
IsKeyDown(int user_key_index)4480 bool ImGui::IsKeyDown(int user_key_index)
4481 {
4482 if (user_key_index < 0)
4483 return false;
4484 ImGuiContext& g = *GImGui;
4485 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4486 return g.IO.KeysDown[user_key_index];
4487 }
4488
4489 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
4490 // t1 = current time (e.g.: g.Time)
4491 // An event is triggered at:
4492 // t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N
CalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate)4493 int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
4494 {
4495 if (t1 == 0.0f)
4496 return 1;
4497 if (t0 >= t1)
4498 return 0;
4499 if (repeat_rate <= 0.0f)
4500 return (t0 < repeat_delay) && (t1 >= repeat_delay);
4501 const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
4502 const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
4503 const int count = count_t1 - count_t0;
4504 return count;
4505 }
4506
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)4507 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4508 {
4509 ImGuiContext& g = *GImGui;
4510 if (key_index < 0)
4511 return 0;
4512 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4513 const float t = g.IO.KeysDownDuration[key_index];
4514 return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
4515 }
4516
IsKeyPressed(int user_key_index,bool repeat)4517 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4518 {
4519 ImGuiContext& g = *GImGui;
4520 if (user_key_index < 0)
4521 return false;
4522 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4523 const float t = g.IO.KeysDownDuration[user_key_index];
4524 if (t == 0.0f)
4525 return true;
4526 if (repeat && t > g.IO.KeyRepeatDelay)
4527 return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4528 return false;
4529 }
4530
IsKeyReleased(int user_key_index)4531 bool ImGui::IsKeyReleased(int user_key_index)
4532 {
4533 ImGuiContext& g = *GImGui;
4534 if (user_key_index < 0) return false;
4535 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4536 return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4537 }
4538
IsMouseDown(ImGuiMouseButton button)4539 bool ImGui::IsMouseDown(ImGuiMouseButton button)
4540 {
4541 ImGuiContext& g = *GImGui;
4542 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4543 return g.IO.MouseDown[button];
4544 }
4545
IsMouseClicked(ImGuiMouseButton button,bool repeat)4546 bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
4547 {
4548 ImGuiContext& g = *GImGui;
4549 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4550 const float t = g.IO.MouseDownDuration[button];
4551 if (t == 0.0f)
4552 return true;
4553
4554 if (repeat && t > g.IO.KeyRepeatDelay)
4555 {
4556 // 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.
4557 int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
4558 if (amount > 0)
4559 return true;
4560 }
4561 return false;
4562 }
4563
IsMouseReleased(ImGuiMouseButton button)4564 bool ImGui::IsMouseReleased(ImGuiMouseButton button)
4565 {
4566 ImGuiContext& g = *GImGui;
4567 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4568 return g.IO.MouseReleased[button];
4569 }
4570
IsMouseDoubleClicked(ImGuiMouseButton button)4571 bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
4572 {
4573 ImGuiContext& g = *GImGui;
4574 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4575 return g.IO.MouseDoubleClicked[button];
4576 }
4577
4578 // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
4579 // [Internal] This doesn't test if the button is pressed
IsMouseDragPastThreshold(ImGuiMouseButton button,float lock_threshold)4580 bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
4581 {
4582 ImGuiContext& g = *GImGui;
4583 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4584 if (lock_threshold < 0.0f)
4585 lock_threshold = g.IO.MouseDragThreshold;
4586 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4587 }
4588
IsMouseDragging(ImGuiMouseButton button,float lock_threshold)4589 bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
4590 {
4591 ImGuiContext& g = *GImGui;
4592 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4593 if (!g.IO.MouseDown[button])
4594 return false;
4595 return IsMouseDragPastThreshold(button, lock_threshold);
4596 }
4597
GetMousePos()4598 ImVec2 ImGui::GetMousePos()
4599 {
4600 ImGuiContext& g = *GImGui;
4601 return g.IO.MousePos;
4602 }
4603
4604 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4605 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4606 {
4607 ImGuiContext& g = *GImGui;
4608 if (g.BeginPopupStack.Size > 0)
4609 return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
4610 return g.IO.MousePos;
4611 }
4612
4613 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
IsMousePosValid(const ImVec2 * mouse_pos)4614 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4615 {
4616 // The assert is only to silence a false-positive in XCode Static Analysis.
4617 // 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).
4618 IM_ASSERT(GImGui != NULL);
4619 const float MOUSE_INVALID = -256000.0f;
4620 ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
4621 return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
4622 }
4623
IsAnyMouseDown()4624 bool ImGui::IsAnyMouseDown()
4625 {
4626 ImGuiContext& g = *GImGui;
4627 for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4628 if (g.IO.MouseDown[n])
4629 return true;
4630 return false;
4631 }
4632
4633 // Return the delta from the initial clicking position while the mouse button is clicked or was just released.
4634 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4635 // NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.
GetMouseDragDelta(ImGuiMouseButton button,float lock_threshold)4636 ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
4637 {
4638 ImGuiContext& g = *GImGui;
4639 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4640 if (lock_threshold < 0.0f)
4641 lock_threshold = g.IO.MouseDragThreshold;
4642 if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
4643 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4644 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
4645 return g.IO.MousePos - g.IO.MouseClickedPos[button];
4646 return ImVec2(0.0f, 0.0f);
4647 }
4648
ResetMouseDragDelta(ImGuiMouseButton button)4649 void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
4650 {
4651 ImGuiContext& g = *GImGui;
4652 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4653 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4654 g.IO.MouseClickedPos[button] = g.IO.MousePos;
4655 }
4656
GetMouseCursor()4657 ImGuiMouseCursor ImGui::GetMouseCursor()
4658 {
4659 return GImGui->MouseCursor;
4660 }
4661
SetMouseCursor(ImGuiMouseCursor cursor_type)4662 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4663 {
4664 GImGui->MouseCursor = cursor_type;
4665 }
4666
CaptureKeyboardFromApp(bool capture)4667 void ImGui::CaptureKeyboardFromApp(bool capture)
4668 {
4669 GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4670 }
4671
CaptureMouseFromApp(bool capture)4672 void ImGui::CaptureMouseFromApp(bool capture)
4673 {
4674 GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4675 }
4676
IsItemActive()4677 bool ImGui::IsItemActive()
4678 {
4679 ImGuiContext& g = *GImGui;
4680 if (g.ActiveId)
4681 {
4682 ImGuiWindow* window = g.CurrentWindow;
4683 return g.ActiveId == window->DC.LastItemId;
4684 }
4685 return false;
4686 }
4687
IsItemActivated()4688 bool ImGui::IsItemActivated()
4689 {
4690 ImGuiContext& g = *GImGui;
4691 if (g.ActiveId)
4692 {
4693 ImGuiWindow* window = g.CurrentWindow;
4694 if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId)
4695 return true;
4696 }
4697 return false;
4698 }
4699
IsItemDeactivated()4700 bool ImGui::IsItemDeactivated()
4701 {
4702 ImGuiContext& g = *GImGui;
4703 ImGuiWindow* window = g.CurrentWindow;
4704 if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated)
4705 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
4706 return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
4707 }
4708
IsItemDeactivatedAfterEdit()4709 bool ImGui::IsItemDeactivatedAfterEdit()
4710 {
4711 ImGuiContext& g = *GImGui;
4712 return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
4713 }
4714
4715 // == GetItemID() == GetFocusID()
IsItemFocused()4716 bool ImGui::IsItemFocused()
4717 {
4718 ImGuiContext& g = *GImGui;
4719 ImGuiWindow* window = g.CurrentWindow;
4720
4721 if (g.NavId != window->DC.LastItemId || g.NavId == 0)
4722 return false;
4723 return true;
4724 }
4725
IsItemClicked(ImGuiMouseButton mouse_button)4726 bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
4727 {
4728 return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4729 }
4730
IsItemToggledOpen()4731 bool ImGui::IsItemToggledOpen()
4732 {
4733 ImGuiContext& g = *GImGui;
4734 return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
4735 }
4736
IsItemToggledSelection()4737 bool ImGui::IsItemToggledSelection()
4738 {
4739 ImGuiContext& g = *GImGui;
4740 return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
4741 }
4742
IsAnyItemHovered()4743 bool ImGui::IsAnyItemHovered()
4744 {
4745 ImGuiContext& g = *GImGui;
4746 return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4747 }
4748
IsAnyItemActive()4749 bool ImGui::IsAnyItemActive()
4750 {
4751 ImGuiContext& g = *GImGui;
4752 return g.ActiveId != 0;
4753 }
4754
IsAnyItemFocused()4755 bool ImGui::IsAnyItemFocused()
4756 {
4757 ImGuiContext& g = *GImGui;
4758 return g.NavId != 0 && !g.NavDisableHighlight;
4759 }
4760
IsItemVisible()4761 bool ImGui::IsItemVisible()
4762 {
4763 ImGuiWindow* window = GetCurrentWindowRead();
4764 return window->ClipRect.Overlaps(window->DC.LastItemRect);
4765 }
4766
IsItemEdited()4767 bool ImGui::IsItemEdited()
4768 {
4769 ImGuiWindow* window = GetCurrentWindowRead();
4770 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4771 }
4772
4773 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
4774 // FIXME: Although this is exposed, its interaction and ideal idiom with using ImGuiButtonFlags_AllowItemOverlap flag are extremely confusing, need rework.
SetItemAllowOverlap()4775 void ImGui::SetItemAllowOverlap()
4776 {
4777 ImGuiContext& g = *GImGui;
4778 ImGuiID id = g.CurrentWindow->DC.LastItemId;
4779 if (g.HoveredId == id)
4780 g.HoveredIdAllowOverlap = true;
4781 if (g.ActiveId == id)
4782 g.ActiveIdAllowOverlap = true;
4783 }
4784
SetItemUsingMouseWheel()4785 void ImGui::SetItemUsingMouseWheel()
4786 {
4787 ImGuiContext& g = *GImGui;
4788 ImGuiID id = g.CurrentWindow->DC.LastItemId;
4789 if (g.HoveredId == id)
4790 g.HoveredIdUsingMouseWheel = true;
4791 if (g.ActiveId == id)
4792 g.ActiveIdUsingMouseWheel = true;
4793 }
4794
GetItemRectMin()4795 ImVec2 ImGui::GetItemRectMin()
4796 {
4797 ImGuiWindow* window = GetCurrentWindowRead();
4798 return window->DC.LastItemRect.Min;
4799 }
4800
GetItemRectMax()4801 ImVec2 ImGui::GetItemRectMax()
4802 {
4803 ImGuiWindow* window = GetCurrentWindowRead();
4804 return window->DC.LastItemRect.Max;
4805 }
4806
GetItemRectSize()4807 ImVec2 ImGui::GetItemRectSize()
4808 {
4809 ImGuiWindow* window = GetCurrentWindowRead();
4810 return window->DC.LastItemRect.GetSize();
4811 }
4812
GetViewportRect()4813 static ImRect GetViewportRect()
4814 {
4815 ImGuiContext& g = *GImGui;
4816 return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4817 }
4818
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)4819 bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4820 {
4821 ImGuiContext& g = *GImGui;
4822 ImGuiWindow* parent_window = g.CurrentWindow;
4823
4824 flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow;
4825 flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
4826
4827 // Size
4828 const ImVec2 content_avail = GetContentRegionAvail();
4829 ImVec2 size = ImFloor(size_arg);
4830 const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
4831 if (size.x <= 0.0f)
4832 size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
4833 if (size.y <= 0.0f)
4834 size.y = ImMax(content_avail.y + size.y, 4.0f);
4835 SetNextWindowSize(size);
4836
4837 // 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.
4838 if (name)
4839 ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%s_%08X", parent_window->Name, name, id);
4840 else
4841 ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%08X", parent_window->Name, id);
4842
4843 const float backup_border_size = g.Style.ChildBorderSize;
4844 if (!border)
4845 g.Style.ChildBorderSize = 0.0f;
4846 bool ret = Begin(g.TempBuffer, NULL, flags);
4847 g.Style.ChildBorderSize = backup_border_size;
4848
4849 ImGuiWindow* child_window = g.CurrentWindow;
4850 child_window->ChildId = id;
4851 child_window->AutoFitChildAxises = (ImS8)auto_fit_axises;
4852
4853 // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
4854 // While this is not really documented/defined, it seems that the expected thing to do.
4855 if (child_window->BeginCount == 1)
4856 parent_window->DC.CursorPos = child_window->Pos;
4857
4858 // Process navigation-in immediately so NavInit can run on first frame
4859 if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
4860 {
4861 FocusWindow(child_window);
4862 NavInitWindow(child_window, false);
4863 SetActiveID(id + 1, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
4864 g.ActiveIdSource = ImGuiInputSource_Nav;
4865 }
4866 return ret;
4867 }
4868
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4869 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4870 {
4871 ImGuiWindow* window = GetCurrentWindow();
4872 return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
4873 }
4874
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4875 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4876 {
4877 IM_ASSERT(id != 0);
4878 return BeginChildEx(NULL, id, size_arg, border, extra_flags);
4879 }
4880
EndChild()4881 void ImGui::EndChild()
4882 {
4883 ImGuiContext& g = *GImGui;
4884 ImGuiWindow* window = g.CurrentWindow;
4885
4886 IM_ASSERT(g.WithinEndChild == false);
4887 IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls
4888
4889 g.WithinEndChild = true;
4890 if (window->BeginCount > 1)
4891 {
4892 End();
4893 }
4894 else
4895 {
4896 ImVec2 sz = window->Size;
4897 if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
4898 sz.x = ImMax(4.0f, sz.x);
4899 if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
4900 sz.y = ImMax(4.0f, sz.y);
4901 End();
4902
4903 ImGuiWindow* parent_window = g.CurrentWindow;
4904 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
4905 ItemSize(sz);
4906 if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
4907 {
4908 ItemAdd(bb, window->ChildId);
4909 RenderNavHighlight(bb, window->ChildId);
4910
4911 // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
4912 if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
4913 RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
4914 }
4915 else
4916 {
4917 // Not navigable into
4918 ItemAdd(bb, 0);
4919 }
4920 }
4921 g.WithinEndChild = false;
4922 }
4923
4924 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)4925 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
4926 {
4927 ImGuiContext& g = *GImGui;
4928 const ImGuiStyle& style = g.Style;
4929 PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4930 PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4931 PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4932 PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
4933 bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
4934 PopStyleVar(3);
4935 PopStyleColor();
4936 return ret;
4937 }
4938
EndChildFrame()4939 void ImGui::EndChildFrame()
4940 {
4941 EndChild();
4942 }
4943
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)4944 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
4945 {
4946 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
4947 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
4948 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
4949 }
4950
FindWindowByID(ImGuiID id)4951 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
4952 {
4953 ImGuiContext& g = *GImGui;
4954 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
4955 }
4956
FindWindowByName(const char * name)4957 ImGuiWindow* ImGui::FindWindowByName(const char* name)
4958 {
4959 ImGuiID id = ImHashStr(name);
4960 return FindWindowByID(id);
4961 }
4962
ApplyWindowSettings(ImGuiWindow * window,ImGuiWindowSettings * settings)4963 static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
4964 {
4965 window->Pos = ImFloor(ImVec2(settings->Pos.x, settings->Pos.y));
4966 if (settings->Size.x > 0 && settings->Size.y > 0)
4967 window->Size = window->SizeFull = ImFloor(ImVec2(settings->Size.x, settings->Size.y));
4968 window->Collapsed = settings->Collapsed;
4969 }
4970
CreateNewWindow(const char * name,ImGuiWindowFlags flags)4971 static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
4972 {
4973 ImGuiContext& g = *GImGui;
4974 //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
4975
4976 // Create window the first time
4977 ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
4978 window->Flags = flags;
4979 g.WindowsById.SetVoidPtr(window->ID, window);
4980
4981 // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
4982 window->Pos = ImVec2(60, 60);
4983
4984 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
4985 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4986 if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
4987 {
4988 // Retrieve settings from .ini file
4989 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
4990 SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
4991 ApplyWindowSettings(window, settings);
4992 }
4993 window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values
4994
4995 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4996 {
4997 window->AutoFitFramesX = window->AutoFitFramesY = 2;
4998 window->AutoFitOnlyGrows = false;
4999 }
5000 else
5001 {
5002 if (window->Size.x <= 0.0f)
5003 window->AutoFitFramesX = 2;
5004 if (window->Size.y <= 0.0f)
5005 window->AutoFitFramesY = 2;
5006 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
5007 }
5008
5009 g.WindowsFocusOrder.push_back(window);
5010 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
5011 g.Windows.push_front(window); // Quite slow but rare and only once
5012 else
5013 g.Windows.push_back(window);
5014 return window;
5015 }
5016
CalcWindowSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)5017 static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
5018 {
5019 ImGuiContext& g = *GImGui;
5020 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
5021 {
5022 // Using -1,-1 on either X/Y axis to preserve the current size.
5023 ImRect cr = g.NextWindowData.SizeConstraintRect;
5024 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
5025 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
5026 if (g.NextWindowData.SizeCallback)
5027 {
5028 ImGuiSizeCallbackData data;
5029 data.UserData = g.NextWindowData.SizeCallbackUserData;
5030 data.Pos = window->Pos;
5031 data.CurrentSize = window->SizeFull;
5032 data.DesiredSize = new_size;
5033 g.NextWindowData.SizeCallback(&data);
5034 new_size = data.DesiredSize;
5035 }
5036 new_size.x = IM_FLOOR(new_size.x);
5037 new_size.y = IM_FLOOR(new_size.y);
5038 }
5039
5040 // Minimum size
5041 if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
5042 {
5043 ImGuiWindow* window_for_height = window;
5044 new_size = ImMax(new_size, g.Style.WindowMinSize);
5045 new_size.y = ImMax(new_size.y, window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
5046 }
5047 return new_size;
5048 }
5049
CalcWindowContentSizes(ImGuiWindow * window,ImVec2 * content_size_current,ImVec2 * content_size_ideal)5050 static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)
5051 {
5052 bool preserve_old_content_sizes = false;
5053 if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5054 preserve_old_content_sizes = true;
5055 else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
5056 preserve_old_content_sizes = true;
5057 if (preserve_old_content_sizes)
5058 {
5059 *content_size_current = window->ContentSize;
5060 *content_size_ideal = window->ContentSizeIdeal;
5061 return;
5062 }
5063
5064 content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
5065 content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
5066 content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x);
5067 content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y);
5068 }
5069
CalcWindowAutoFitSize(ImGuiWindow * window,const ImVec2 & size_contents)5070 static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
5071 {
5072 ImGuiContext& g = *GImGui;
5073 ImGuiStyle& style = g.Style;
5074 ImVec2 size_decorations = ImVec2(0.0f, window->TitleBarHeight() + window->MenuBarHeight());
5075 ImVec2 size_pad = window->WindowPadding * 2.0f;
5076 ImVec2 size_desired = size_contents + size_pad + size_decorations;
5077 if (window->Flags & ImGuiWindowFlags_Tooltip)
5078 {
5079 // Tooltip always resize
5080 return size_desired;
5081 }
5082 else
5083 {
5084 // Maximum window size is determined by the viewport size or monitor size
5085 const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
5086 const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
5087 ImVec2 size_min = style.WindowMinSize;
5088 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)
5089 size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
5090 ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
5091
5092 // When the window cannot fit all contents (either because of constraints, either because screen is too small),
5093 // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
5094 ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5095 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);
5096 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);
5097 if (will_have_scrollbar_x)
5098 size_auto_fit.y += style.ScrollbarSize;
5099 if (will_have_scrollbar_y)
5100 size_auto_fit.x += style.ScrollbarSize;
5101 return size_auto_fit;
5102 }
5103 }
5104
CalcWindowNextAutoFitSize(ImGuiWindow * window)5105 ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window)
5106 {
5107 ImVec2 size_contents_current;
5108 ImVec2 size_contents_ideal;
5109 CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal);
5110 ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal);
5111 ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5112 return size_final;
5113 }
5114
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)5115 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
5116 {
5117 if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
5118 return ImGuiCol_PopupBg;
5119 if (flags & ImGuiWindowFlags_ChildWindow)
5120 return ImGuiCol_ChildBg;
5121 return ImGuiCol_WindowBg;
5122 }
5123
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)5124 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
5125 {
5126 ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left
5127 ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
5128 ImVec2 size_expected = pos_max - pos_min;
5129 ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
5130 *out_pos = pos_min;
5131 if (corner_norm.x == 0.0f)
5132 out_pos->x -= (size_constrained.x - size_expected.x);
5133 if (corner_norm.y == 0.0f)
5134 out_pos->y -= (size_constrained.y - size_expected.y);
5135 *out_size = size_constrained;
5136 }
5137
5138 struct ImGuiResizeGripDef
5139 {
5140 ImVec2 CornerPosN;
5141 ImVec2 InnerDir;
5142 int AngleMin12, AngleMax12;
5143 };
5144
5145 static const ImGuiResizeGripDef resize_grip_def[4] =
5146 {
5147 { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right
5148 { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left
5149 { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused)
5150 { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 }, // Upper-right (Unused)
5151 };
5152
5153 struct ImGuiResizeBorderDef
5154 {
5155 ImVec2 InnerDir;
5156 ImVec2 CornerPosN1, CornerPosN2;
5157 float OuterAngle;
5158 };
5159
5160 static const ImGuiResizeBorderDef resize_border_def[4] =
5161 {
5162 { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Top
5163 { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right
5164 { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f }, // Bottom
5165 { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f } // Left
5166 };
5167
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)5168 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
5169 {
5170 ImRect rect = window->Rect();
5171 if (thickness == 0.0f) rect.Max -= ImVec2(1, 1);
5172 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
5173 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
5174 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
5175 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
5176 IM_ASSERT(0);
5177 return ImRect();
5178 }
5179
5180 // 0..3: corners (Lower-right, Lower-left, Unused, Unused)
5181 // 4..7: borders (Top, Right, Bottom, Left)
GetWindowResizeID(ImGuiWindow * window,int n)5182 ImGuiID ImGui::GetWindowResizeID(ImGuiWindow* window, int n)
5183 {
5184 IM_ASSERT(n >= 0 && n <= 7);
5185 ImGuiID id = window->ID;
5186 id = ImHashStr("#RESIZE", 0, id);
5187 id = ImHashData(&n, sizeof(int), id);
5188 return id;
5189 }
5190
5191 // Handle resize for: Resize Grips, Borders, Gamepad
5192 // Return true when using auto-fit (double click on resize grip)
UpdateWindowManualResize(ImGuiWindow * window,const ImVec2 & size_auto_fit,int * border_held,int resize_grip_count,ImU32 resize_grip_col[4],const ImRect & visibility_rect)5193 static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect)
5194 {
5195 ImGuiContext& g = *GImGui;
5196 ImGuiWindowFlags flags = window->Flags;
5197
5198 if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5199 return false;
5200 if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
5201 return false;
5202
5203 bool ret_auto_fit = false;
5204 const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
5205 const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5206 const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f);
5207 const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f;
5208
5209 ImVec2 pos_target(FLT_MAX, FLT_MAX);
5210 ImVec2 size_target(FLT_MAX, FLT_MAX);
5211
5212 // Resize grips and borders are on layer 1
5213 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5214
5215 // Manual resize grips
5216 PushID("#RESIZE");
5217 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5218 {
5219 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5220 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5221
5222 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
5223 ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size);
5224 if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
5225 if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
5226 bool hovered, held;
5227 ButtonBehavior(resize_rect, window->GetID(resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
5228 //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
5229 if (hovered || held)
5230 g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
5231
5232 if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
5233 {
5234 // Manual auto-fit when double-clicking
5235 size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5236 ret_auto_fit = true;
5237 ClearActiveID();
5238 }
5239 else if (held)
5240 {
5241 // Resize from any of the four corners
5242 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
5243 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
5244 ImVec2 clamp_min = ImVec2(grip.CornerPosN.x == 1.0f ? visibility_rect.Min.x : -FLT_MAX, grip.CornerPosN.y == 1.0f ? visibility_rect.Min.y : -FLT_MAX);
5245 ImVec2 clamp_max = ImVec2(grip.CornerPosN.x == 0.0f ? visibility_rect.Max.x : +FLT_MAX, grip.CornerPosN.y == 0.0f ? visibility_rect.Max.y : +FLT_MAX);
5246 corner_target = ImClamp(corner_target, clamp_min, clamp_max);
5247 CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target);
5248 }
5249 if (resize_grip_n == 0 || held || hovered)
5250 resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
5251 }
5252 for (int border_n = 0; border_n < resize_border_count; border_n++)
5253 {
5254 bool hovered, held;
5255 ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS);
5256 ButtonBehavior(border_rect, window->GetID(border_n + 4), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
5257 //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
5258 if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
5259 {
5260 g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
5261 if (held)
5262 *border_held = border_n;
5263 }
5264 if (held)
5265 {
5266 ImVec2 border_target = window->Pos;
5267 ImVec2 border_posn;
5268 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
5269 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
5270 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
5271 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
5272 ImVec2 clamp_min = ImVec2(border_n == 1 ? visibility_rect.Min.x : -FLT_MAX, border_n == 2 ? visibility_rect.Min.y : -FLT_MAX);
5273 ImVec2 clamp_max = ImVec2(border_n == 3 ? visibility_rect.Max.x : +FLT_MAX, border_n == 0 ? visibility_rect.Max.y : +FLT_MAX);
5274 border_target = ImClamp(border_target, clamp_min, clamp_max);
5275 CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
5276 }
5277 }
5278 PopID();
5279
5280 // Restore nav layer
5281 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5282
5283 // Navigation resize (keyboard/gamepad)
5284 if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
5285 {
5286 ImVec2 nav_resize_delta;
5287 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
5288 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
5289 if (g.NavInputSource == ImGuiInputSource_NavGamepad)
5290 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
5291 if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
5292 {
5293 const float NAV_RESIZE_SPEED = 600.0f;
5294 nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
5295 nav_resize_delta = ImMax(nav_resize_delta, visibility_rect.Min - window->Pos - window->Size);
5296 g.NavWindowingToggleLayer = false;
5297 g.NavDisableMouseHover = true;
5298 resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
5299 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
5300 size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
5301 }
5302 }
5303
5304 // Apply back modified position/size to window
5305 if (size_target.x != FLT_MAX)
5306 {
5307 window->SizeFull = size_target;
5308 MarkIniSettingsDirty(window);
5309 }
5310 if (pos_target.x != FLT_MAX)
5311 {
5312 window->Pos = ImFloor(pos_target);
5313 MarkIniSettingsDirty(window);
5314 }
5315
5316 window->Size = window->SizeFull;
5317 return ret_auto_fit;
5318 }
5319
ClampWindowRect(ImGuiWindow * window,const ImRect & visibility_rect)5320 static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& visibility_rect)
5321 {
5322 ImGuiContext& g = *GImGui;
5323 ImVec2 size_for_clamping = window->Size;
5324 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5325 size_for_clamping.y = window->TitleBarHeight();
5326 window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
5327 }
5328
RenderWindowOuterBorders(ImGuiWindow * window)5329 static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
5330 {
5331 ImGuiContext& g = *GImGui;
5332 float rounding = window->WindowRounding;
5333 float border_size = window->WindowBorderSize;
5334 if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
5335 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
5336
5337 int border_held = window->ResizeBorderHeld;
5338 if (border_held != -1)
5339 {
5340 const ImGuiResizeBorderDef& def = resize_border_def[border_held];
5341 ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
5342 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);
5343 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);
5344 window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual
5345 }
5346 if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5347 {
5348 float y = window->Pos.y + window->TitleBarHeight() - 1;
5349 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);
5350 }
5351 }
5352
5353 // Draw background and borders
5354 // Draw and handle scrollbars
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)5355 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)
5356 {
5357 ImGuiContext& g = *GImGui;
5358 ImGuiStyle& style = g.Style;
5359 ImGuiWindowFlags flags = window->Flags;
5360
5361 // Ensure that ScrollBar doesn't read last frame's SkipItems
5362 IM_ASSERT(window->BeginCount == 0);
5363 window->SkipItems = false;
5364
5365 // Draw window + handle manual resize
5366 // 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.
5367 const float window_rounding = window->WindowRounding;
5368 const float window_border_size = window->WindowBorderSize;
5369 if (window->Collapsed)
5370 {
5371 // Title bar only
5372 float backup_border_size = style.FrameBorderSize;
5373 g.Style.FrameBorderSize = window->WindowBorderSize;
5374 ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5375 RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5376 g.Style.FrameBorderSize = backup_border_size;
5377 }
5378 else
5379 {
5380 // Window background
5381 if (!(flags & ImGuiWindowFlags_NoBackground))
5382 {
5383 ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5384 bool override_alpha = false;
5385 float alpha = 1.0f;
5386 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
5387 {
5388 alpha = g.NextWindowData.BgAlphaVal;
5389 override_alpha = true;
5390 }
5391 if (override_alpha)
5392 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
5393 window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
5394 }
5395
5396 // Title bar
5397 if (!(flags & ImGuiWindowFlags_NoTitleBar))
5398 {
5399 ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5400 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
5401 }
5402
5403 // Menu bar
5404 if (flags & ImGuiWindowFlags_MenuBar)
5405 {
5406 ImRect menu_bar_rect = window->MenuBarRect();
5407 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.
5408 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);
5409 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5410 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5411 }
5412
5413 // Scrollbars
5414 if (window->ScrollbarX)
5415 Scrollbar(ImGuiAxis_X);
5416 if (window->ScrollbarY)
5417 Scrollbar(ImGuiAxis_Y);
5418
5419 // Render resize grips (after their input handling so we don't have a frame of latency)
5420 if (!(flags & ImGuiWindowFlags_NoResize))
5421 {
5422 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5423 {
5424 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5425 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5426 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)));
5427 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)));
5428 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);
5429 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5430 }
5431 }
5432
5433 // Borders
5434 RenderWindowOuterBorders(window);
5435 }
5436 }
5437
5438 // Render title text, collapse button, close button
RenderWindowTitleBarContents(ImGuiWindow * window,const ImRect & title_bar_rect,const char * name,bool * p_open)5439 void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
5440 {
5441 ImGuiContext& g = *GImGui;
5442 ImGuiStyle& style = g.Style;
5443 ImGuiWindowFlags flags = window->Flags;
5444
5445 const bool has_close_button = (p_open != NULL);
5446 const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
5447
5448 // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
5449 const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
5450 window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5451 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5452
5453 // Layout buttons
5454 // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
5455 float pad_l = style.FramePadding.x;
5456 float pad_r = style.FramePadding.x;
5457 float button_sz = g.FontSize;
5458 ImVec2 close_button_pos;
5459 ImVec2 collapse_button_pos;
5460 if (has_close_button)
5461 {
5462 pad_r += button_sz;
5463 close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5464 }
5465 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
5466 {
5467 pad_r += button_sz;
5468 collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5469 }
5470 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
5471 {
5472 collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y);
5473 pad_l += button_sz;
5474 }
5475
5476 // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
5477 if (has_collapse_button)
5478 if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos))
5479 window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
5480
5481 // Close button
5482 if (has_close_button)
5483 if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
5484 *p_open = false;
5485
5486 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5487 window->DC.ItemFlags = item_flags_backup;
5488
5489 // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5490 // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
5491 const char* UNSAVED_DOCUMENT_MARKER = "*";
5492 const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f;
5493 const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5494
5495 // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
5496 // while uncentered title text will still reach edges correct.
5497 if (pad_l > style.FramePadding.x)
5498 pad_l += g.Style.ItemInnerSpacing.x;
5499 if (pad_r > style.FramePadding.x)
5500 pad_r += g.Style.ItemInnerSpacing.x;
5501 if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
5502 {
5503 float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
5504 float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
5505 pad_l = ImMax(pad_l, pad_extend * centerness);
5506 pad_r = ImMax(pad_r, pad_extend * centerness);
5507 }
5508
5509 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);
5510 ImRect clip_r(layout_r.Min.x, layout_r.Min.y, layout_r.Max.x + g.Style.ItemInnerSpacing.x, layout_r.Max.y);
5511 //if (g.IO.KeyCtrl) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
5512 RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
5513 if (flags & ImGuiWindowFlags_UnsavedDocument)
5514 {
5515 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);
5516 ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f));
5517 RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r);
5518 }
5519 }
5520
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)5521 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
5522 {
5523 window->ParentWindow = parent_window;
5524 window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
5525 if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
5526 window->RootWindow = parent_window->RootWindow;
5527 if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
5528 window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
5529 while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
5530 {
5531 IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
5532 window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
5533 }
5534 }
5535
5536 // Push a new Dear ImGui window to add widgets to.
5537 // - 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.
5538 // - Begin/End can be called multiple times during the frame with the same window name to append content.
5539 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
5540 // 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.
5541 // - 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.
5542 // - 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)5543 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
5544 {
5545 ImGuiContext& g = *GImGui;
5546 const ImGuiStyle& style = g.Style;
5547 IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required
5548 IM_ASSERT(g.WithinFrameScope); // Forgot to call ImGui::NewFrame()
5549 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
5550
5551 // Find or create
5552 ImGuiWindow* window = FindWindowByName(name);
5553 const bool window_just_created = (window == NULL);
5554 if (window_just_created)
5555 window = CreateNewWindow(name, flags);
5556
5557 // Automatically disable manual moving/resizing when NoInputs is set
5558 if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
5559 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
5560
5561 if (flags & ImGuiWindowFlags_NavFlattened)
5562 IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
5563
5564 const int current_frame = g.FrameCount;
5565 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
5566 window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
5567
5568 // Update the Appearing flag
5569 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
5570 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
5571 if (flags & ImGuiWindowFlags_Popup)
5572 {
5573 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5574 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
5575 window_just_activated_by_user |= (window != popup_ref.Window);
5576 }
5577 window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
5578 if (window->Appearing)
5579 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
5580
5581 // Update Flags, LastFrameActive, BeginOrderXXX fields
5582 if (first_begin_of_the_frame)
5583 {
5584 window->Flags = (ImGuiWindowFlags)flags;
5585 window->LastFrameActive = current_frame;
5586 window->LastTimeActive = (float)g.Time;
5587 window->BeginOrderWithinParent = 0;
5588 window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
5589 }
5590 else
5591 {
5592 flags = window->Flags;
5593 }
5594
5595 // 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
5596 ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
5597 ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
5598 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
5599
5600 // We allow window memory to be compacted so recreate the base stack when needed.
5601 if (window->IDStack.Size == 0)
5602 window->IDStack.push_back(window->ID);
5603
5604 // Add to stack
5605 // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
5606 g.CurrentWindowStack.push_back(window);
5607 g.CurrentWindow = window;
5608 window->DC.StackSizesOnBegin.SetToCurrentState();
5609 g.CurrentWindow = NULL;
5610
5611 if (flags & ImGuiWindowFlags_Popup)
5612 {
5613 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5614 popup_ref.Window = window;
5615 g.BeginPopupStack.push_back(popup_ref);
5616 window->PopupId = popup_ref.PopupId;
5617 }
5618
5619 if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
5620 window->NavLastIds[0] = 0;
5621
5622 // Update ->RootWindow and others pointers (before any possible call to FocusWindow)
5623 if (first_begin_of_the_frame)
5624 UpdateWindowParentAndRootLinks(window, flags, parent_window);
5625
5626 // Process SetNextWindow***() calls
5627 // (FIXME: Consider splitting the HasXXX flags into X/Y components
5628 bool window_pos_set_by_api = false;
5629 bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
5630 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
5631 {
5632 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
5633 if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
5634 {
5635 // May be processed on the next frame if this is our first frame and we are measuring size
5636 // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
5637 window->SetWindowPosVal = g.NextWindowData.PosVal;
5638 window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
5639 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5640 }
5641 else
5642 {
5643 SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
5644 }
5645 }
5646 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
5647 {
5648 window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
5649 window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
5650 SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
5651 }
5652 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll)
5653 {
5654 if (g.NextWindowData.ScrollVal.x >= 0.0f)
5655 {
5656 window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
5657 window->ScrollTargetCenterRatio.x = 0.0f;
5658 }
5659 if (g.NextWindowData.ScrollVal.y >= 0.0f)
5660 {
5661 window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
5662 window->ScrollTargetCenterRatio.y = 0.0f;
5663 }
5664 }
5665 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
5666 window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
5667 else if (first_begin_of_the_frame)
5668 window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
5669 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
5670 SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
5671 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
5672 FocusWindow(window);
5673 if (window->Appearing)
5674 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
5675
5676 // When reusing window again multiple times a frame, just append content (don't need to setup again)
5677 if (first_begin_of_the_frame)
5678 {
5679 // Initialize
5680 const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
5681 window->Active = true;
5682 window->HasCloseButton = (p_open != NULL);
5683 window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
5684 window->IDStack.resize(1);
5685 window->DrawList->_ResetForNewFrame();
5686 window->DC.CurrentTableIdx = -1;
5687
5688 // Restore buffer capacity when woken from a compacted state, to avoid
5689 if (window->MemoryCompacted)
5690 GcAwakeTransientWindowBuffers(window);
5691
5692 // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
5693 // 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.
5694 bool window_title_visible_elsewhere = false;
5695 if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
5696 window_title_visible_elsewhere = true;
5697 if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
5698 {
5699 size_t buf_len = (size_t)window->NameBufLen;
5700 window->Name = ImStrdupcpy(window->Name, &buf_len, name);
5701 window->NameBufLen = (int)buf_len;
5702 }
5703
5704 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
5705
5706 // Update contents size from last frame for auto-fitting (or use explicit size)
5707 CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal);
5708 if (window->HiddenFramesCanSkipItems > 0)
5709 window->HiddenFramesCanSkipItems--;
5710 if (window->HiddenFramesCannotSkipItems > 0)
5711 window->HiddenFramesCannotSkipItems--;
5712 if (window->HiddenFramesForRenderOnly > 0)
5713 window->HiddenFramesForRenderOnly--;
5714
5715 // Hide new windows for one frame until they calculate their size
5716 if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
5717 window->HiddenFramesCannotSkipItems = 1;
5718
5719 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
5720 // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
5721 if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
5722 {
5723 window->HiddenFramesCannotSkipItems = 1;
5724 if (flags & ImGuiWindowFlags_AlwaysAutoResize)
5725 {
5726 if (!window_size_x_set_by_api)
5727 window->Size.x = window->SizeFull.x = 0.f;
5728 if (!window_size_y_set_by_api)
5729 window->Size.y = window->SizeFull.y = 0.f;
5730 window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f);
5731 }
5732 }
5733
5734 // SELECT VIEWPORT
5735 // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)
5736 SetCurrentWindow(window);
5737
5738 // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
5739
5740 if (flags & ImGuiWindowFlags_ChildWindow)
5741 window->WindowBorderSize = style.ChildBorderSize;
5742 else
5743 window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
5744 window->WindowPadding = style.WindowPadding;
5745 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
5746 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
5747
5748 // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
5749 window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
5750 window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
5751
5752 // Collapse window by double-clicking on title bar
5753 // 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
5754 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
5755 {
5756 // 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.
5757 ImRect title_bar_rect = window->TitleBarRect();
5758 if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
5759 window->WantCollapseToggle = true;
5760 if (window->WantCollapseToggle)
5761 {
5762 window->Collapsed = !window->Collapsed;
5763 MarkIniSettingsDirty(window);
5764 FocusWindow(window);
5765 }
5766 }
5767 else
5768 {
5769 window->Collapsed = false;
5770 }
5771 window->WantCollapseToggle = false;
5772
5773 // SIZE
5774
5775 // Calculate auto-fit size, handle automatic resize
5776 const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal);
5777 bool use_current_size_for_scrollbar_x = window_just_created;
5778 bool use_current_size_for_scrollbar_y = window_just_created;
5779 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
5780 {
5781 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
5782 if (!window_size_x_set_by_api)
5783 {
5784 window->SizeFull.x = size_auto_fit.x;
5785 use_current_size_for_scrollbar_x = true;
5786 }
5787 if (!window_size_y_set_by_api)
5788 {
5789 window->SizeFull.y = size_auto_fit.y;
5790 use_current_size_for_scrollbar_y = true;
5791 }
5792 }
5793 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5794 {
5795 // Auto-fit may only grow window during the first few frames
5796 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
5797 if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
5798 {
5799 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
5800 use_current_size_for_scrollbar_x = true;
5801 }
5802 if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
5803 {
5804 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
5805 use_current_size_for_scrollbar_y = true;
5806 }
5807 if (!window->Collapsed)
5808 MarkIniSettingsDirty(window);
5809 }
5810
5811 // Apply minimum/maximum window size constraints and final size
5812 window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
5813 window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
5814
5815 // Decoration size
5816 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
5817
5818 // POSITION
5819
5820 // Popup latch its initial position, will position itself when it appears next frame
5821 if (window_just_activated_by_user)
5822 {
5823 window->AutoPosLastDirection = ImGuiDir_None;
5824 if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
5825 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
5826 }
5827
5828 // Position child window
5829 if (flags & ImGuiWindowFlags_ChildWindow)
5830 {
5831 IM_ASSERT(parent_window && parent_window->Active);
5832 window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
5833 parent_window->DC.ChildWindows.push_back(window);
5834 if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
5835 window->Pos = parent_window->DC.CursorPos;
5836 }
5837
5838 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
5839 if (window_pos_with_pivot)
5840 SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
5841 else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
5842 window->Pos = FindBestWindowPosForPopup(window);
5843 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
5844 window->Pos = FindBestWindowPosForPopup(window);
5845 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
5846 window->Pos = FindBestWindowPosForPopup(window);
5847
5848 // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
5849 // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
5850 ImRect viewport_rect(GetViewportRect());
5851 ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
5852 ImRect visibility_rect(viewport_rect.Min + visibility_padding, viewport_rect.Max - visibility_padding);
5853
5854 // Clamp position/size so window stays visible within its viewport or monitor
5855 // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
5856 if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5857 if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f)
5858 ClampWindowRect(window, visibility_rect);
5859 window->Pos = ImFloor(window->Pos);
5860
5861 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
5862 // Large values tend to lead to variety of artifacts and are not recommended.
5863 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
5864
5865 // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
5866 //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5867 // window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
5868
5869 // Apply window focus (new and reactivated windows are moved to front)
5870 bool want_focus = false;
5871 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
5872 {
5873 if (flags & ImGuiWindowFlags_Popup)
5874 want_focus = true;
5875 else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
5876 want_focus = true;
5877 }
5878
5879 // Handle manual resize: Resize Grips, Borders, Gamepad
5880 int border_held = -1;
5881 ImU32 resize_grip_col[4] = {};
5882 const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
5883 const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5884 if (!window->Collapsed)
5885 if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
5886 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true;
5887 window->ResizeBorderHeld = (signed char)border_held;
5888
5889 // SCROLLBAR VISIBILITY
5890
5891 // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
5892 if (!window->Collapsed)
5893 {
5894 // When reading the current size we need to read it after size constraints have been applied.
5895 // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again.
5896 ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height);
5897 ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes;
5898 ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
5899 float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
5900 float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
5901 //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
5902 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
5903 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));
5904 if (window->ScrollbarX && !window->ScrollbarY)
5905 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar);
5906 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
5907 }
5908
5909 // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
5910 // Update various regions. Variables they depends on should be set above in this function.
5911 // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
5912
5913 // Outer rectangle
5914 // Not affected by window border size. Used by:
5915 // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
5916 // - Begin() initial clipping rect for drawing window background and borders.
5917 // - Begin() clipping whole child
5918 const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
5919 const ImRect outer_rect = window->Rect();
5920 const ImRect title_bar_rect = window->TitleBarRect();
5921 window->OuterRectClipped = outer_rect;
5922 window->OuterRectClipped.ClipWith(host_rect);
5923
5924 // Inner rectangle
5925 // Not affected by window border size. Used by:
5926 // - InnerClipRect
5927 // - ScrollToBringRectIntoView()
5928 // - NavUpdatePageUpPageDown()
5929 // - Scrollbar()
5930 window->InnerRect.Min.x = window->Pos.x;
5931 window->InnerRect.Min.y = window->Pos.y + decoration_up_height;
5932 window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
5933 window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
5934
5935 // Inner clipping rectangle.
5936 // Will extend a little bit outside the normal work region.
5937 // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
5938 // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
5939 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
5940 // Affected by window/frame border size. Used by:
5941 // - Begin() initial clip rect
5942 float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5943 window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5944 window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
5945 window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5946 window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
5947 window->InnerClipRect.ClipWithFull(host_rect);
5948
5949 // Default item width. Make it proportional to window size if window manually resizes
5950 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
5951 window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f);
5952 else
5953 window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f);
5954
5955 // SCROLLING
5956
5957 // Lock down maximum scrolling
5958 // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
5959 // for right/bottom aligned items without creating a scrollbar.
5960 window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
5961 window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
5962
5963 // Apply scrolling
5964 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
5965 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
5966
5967 // DRAWING
5968
5969 // Setup draw list and outer clipping rectangle
5970 IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
5971 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
5972 PushClipRect(host_rect.Min, host_rect.Max, false);
5973
5974 // Draw modal window background (darkens what is behind them, all viewports)
5975 const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0;
5976 const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
5977 if (dim_bg_for_modal || dim_bg_for_window_list)
5978 {
5979 const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
5980 window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
5981 }
5982
5983 // Draw navigation selection/windowing rectangle background
5984 if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
5985 {
5986 ImRect bb = window->Rect();
5987 bb.Expand(g.FontSize);
5988 if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
5989 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
5990 }
5991
5992 // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call.
5993 // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
5994 // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child.
5995 // We also disabled this when we have dimming overlay behind this specific one child.
5996 // 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.
5997 {
5998 bool render_decorations_in_parent = false;
5999 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
6000 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0)
6001 render_decorations_in_parent = true;
6002 if (render_decorations_in_parent)
6003 window->DrawList = parent_window->DrawList;
6004
6005 // Handle title bar, scrollbar, resize grips and resize borders
6006 const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
6007 const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
6008 RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size);
6009
6010 if (render_decorations_in_parent)
6011 window->DrawList = &window->DrawListInst;
6012 }
6013
6014 // Draw navigation selection/windowing rectangle border
6015 if (g.NavWindowingTargetAnim == window)
6016 {
6017 float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
6018 ImRect bb = window->Rect();
6019 bb.Expand(g.FontSize);
6020 if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
6021 {
6022 bb.Expand(-g.FontSize - 1.0f);
6023 rounding = window->WindowRounding;
6024 }
6025 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
6026 }
6027
6028 // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
6029
6030 // Work rectangle.
6031 // Affected by window padding and border size. Used by:
6032 // - Columns() for right-most edge
6033 // - TreeNode(), CollapsingHeader() for right-most edge
6034 // - BeginTabBar() for right-most edge
6035 const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
6036 const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
6037 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));
6038 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));
6039 window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
6040 window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
6041 window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
6042 window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
6043 window->ParentWorkRect = window->WorkRect;
6044
6045 // [LEGACY] Content Region
6046 // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
6047 // Used by:
6048 // - Mouse wheel scrolling + many other things
6049 window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
6050 window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height;
6051 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));
6052 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));
6053
6054 // Setup drawing context
6055 // (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.)
6056 window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
6057 window->DC.GroupOffset.x = 0.0f;
6058 window->DC.ColumnsOffset.x = 0.0f;
6059 window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y);
6060 window->DC.CursorPos = window->DC.CursorStartPos;
6061 window->DC.CursorPosPrevLine = window->DC.CursorPos;
6062 window->DC.CursorMaxPos = window->DC.CursorStartPos;
6063 window->DC.IdealMaxPos = window->DC.CursorStartPos;
6064 window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
6065 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
6066
6067 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
6068 window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
6069 window->DC.NavLayerActiveMaskNext = 0x00;
6070 window->DC.NavHideHighlightOneFrame = false;
6071 window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f);
6072
6073 window->DC.MenuBarAppending = false;
6074 window->DC.MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
6075 window->DC.TreeDepth = 0;
6076 window->DC.TreeJumpToParentOnPopMask = 0x00;
6077 window->DC.ChildWindows.resize(0);
6078 window->DC.StateStorage = &window->StateStorage;
6079 window->DC.CurrentColumns = NULL;
6080 window->DC.LayoutType = ImGuiLayoutType_Vertical;
6081 window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
6082 window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1;
6083
6084 window->DC.ItemWidth = window->ItemWidthDefault;
6085 window->DC.TextWrapPos = -1.0f; // disabled
6086 window->DC.ItemWidthStack.resize(0);
6087 window->DC.TextWrapPosStack.resize(0);
6088
6089 if (window->AutoFitFramesX > 0)
6090 window->AutoFitFramesX--;
6091 if (window->AutoFitFramesY > 0)
6092 window->AutoFitFramesY--;
6093
6094 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
6095 if (want_focus)
6096 {
6097 FocusWindow(window);
6098 NavInitWindow(window, false);
6099 }
6100
6101 // Title bar
6102 if (!(flags & ImGuiWindowFlags_NoTitleBar))
6103 RenderWindowTitleBarContents(window, title_bar_rect, name, p_open);
6104
6105 // Clear hit test shape every frame
6106 window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
6107
6108 // Pressing CTRL+C while holding on a window copy its content to the clipboard
6109 // 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.
6110 // Maybe we can support CTRL+C on every element?
6111 /*
6112 //if (g.NavWindow == window && g.ActiveId == 0)
6113 if (g.ActiveId == window->MoveId)
6114 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
6115 LogToClipboard();
6116 */
6117
6118 // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
6119 // This is useful to allow creating context menus on title bar only, etc.
6120 SetLastItemData(window, window->MoveId, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect);
6121
6122 #ifdef IMGUI_ENABLE_TEST_ENGINE
6123 if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
6124 IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId);
6125 #endif
6126 }
6127 else
6128 {
6129 // Append
6130 SetCurrentWindow(window);
6131 }
6132
6133 // Pull/inherit current state
6134 window->DC.ItemFlags = g.ItemFlagsStack.back(); // Inherit from shared stack
6135 window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : 0; // Inherit from parent only // -V595
6136
6137 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
6138
6139 // 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)
6140 window->WriteAccessed = false;
6141 window->BeginCount++;
6142 g.NextWindowData.ClearFlags();
6143
6144 // Update visibility
6145 if (first_begin_of_the_frame)
6146 {
6147 if (flags & ImGuiWindowFlags_ChildWindow)
6148 {
6149 // Child window can be out of sight and have "negative" clip windows.
6150 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
6151 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
6152 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) // FIXME: Doesn't make sense for ChildWindow??
6153 if (!g.LogEnabled)
6154 if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
6155 window->HiddenFramesCanSkipItems = 1;
6156
6157 // Hide along with parent or if parent is collapsed
6158 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
6159 window->HiddenFramesCanSkipItems = 1;
6160 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
6161 window->HiddenFramesCannotSkipItems = 1;
6162 }
6163
6164 // 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)
6165 if (style.Alpha <= 0.0f)
6166 window->HiddenFramesCanSkipItems = 1;
6167
6168 // Update the Hidden flag
6169 window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0) || (window->HiddenFramesForRenderOnly > 0);
6170
6171 // Update the SkipItems flag, used to early out of all items functions (no layout required)
6172 bool skip_items = false;
6173 if (window->Collapsed || !window->Active || window->Hidden)
6174 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
6175 skip_items = true;
6176 window->SkipItems = skip_items;
6177 }
6178
6179 return !window->SkipItems;
6180 }
6181
End()6182 void ImGui::End()
6183 {
6184 ImGuiContext& g = *GImGui;
6185 ImGuiWindow* window = g.CurrentWindow;
6186
6187 // Error checking: verify that user hasn't called End() too many times!
6188 if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
6189 {
6190 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
6191 return;
6192 }
6193 IM_ASSERT(g.CurrentWindowStack.Size > 0);
6194
6195 // Error checking: verify that user doesn't directly call End() on a child window.
6196 if (window->Flags & ImGuiWindowFlags_ChildWindow)
6197 IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
6198
6199 // Close anything that is open
6200 if (window->DC.CurrentColumns)
6201 EndColumns();
6202 PopClipRect(); // Inner window clip rectangle
6203
6204 // Stop logging
6205 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
6206 LogFinish();
6207
6208 // Pop from window stack
6209 g.CurrentWindowStack.pop_back();
6210 if (window->Flags & ImGuiWindowFlags_Popup)
6211 g.BeginPopupStack.pop_back();
6212 window->DC.StackSizesOnBegin.CompareWithCurrentState();
6213 SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
6214 }
6215
BringWindowToFocusFront(ImGuiWindow * window)6216 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
6217 {
6218 ImGuiContext& g = *GImGui;
6219 if (g.WindowsFocusOrder.back() == window)
6220 return;
6221 for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the top-most window
6222 if (g.WindowsFocusOrder[i] == window)
6223 {
6224 memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
6225 g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
6226 break;
6227 }
6228 }
6229
BringWindowToDisplayFront(ImGuiWindow * window)6230 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
6231 {
6232 ImGuiContext& g = *GImGui;
6233 ImGuiWindow* current_front_window = g.Windows.back();
6234 if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better)
6235 return;
6236 for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
6237 if (g.Windows[i] == window)
6238 {
6239 memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
6240 g.Windows[g.Windows.Size - 1] = window;
6241 break;
6242 }
6243 }
6244
BringWindowToDisplayBack(ImGuiWindow * window)6245 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
6246 {
6247 ImGuiContext& g = *GImGui;
6248 if (g.Windows[0] == window)
6249 return;
6250 for (int i = 0; i < g.Windows.Size; i++)
6251 if (g.Windows[i] == window)
6252 {
6253 memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
6254 g.Windows[0] = window;
6255 break;
6256 }
6257 }
6258
6259 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)6260 void ImGui::FocusWindow(ImGuiWindow* window)
6261 {
6262 ImGuiContext& g = *GImGui;
6263
6264 if (g.NavWindow != window)
6265 {
6266 g.NavWindow = window;
6267 if (window && g.NavDisableMouseHover)
6268 g.NavMousePosDirty = true;
6269 g.NavInitRequest = false;
6270 g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
6271 g.NavFocusScopeId = 0;
6272 g.NavIdIsAlive = false;
6273 g.NavLayer = ImGuiNavLayer_Main;
6274 //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
6275 }
6276
6277 // Close popups if any
6278 ClosePopupsOverWindow(window, false);
6279
6280 // Move the root window to the top of the pile
6281 IM_ASSERT(window == NULL || window->RootWindow != NULL);
6282 ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop
6283 ImGuiWindow* display_front_window = window ? window->RootWindow : NULL;
6284
6285 // Steal active widgets. Some of the cases it triggers includes:
6286 // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
6287 // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
6288 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
6289 if (!g.ActiveIdNoClearOnFocusLoss)
6290 ClearActiveID();
6291
6292 // Passing NULL allow to disable keyboard focus
6293 if (!window)
6294 return;
6295
6296 // Bring to front
6297 BringWindowToFocusFront(focus_front_window);
6298 if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
6299 BringWindowToDisplayFront(display_front_window);
6300 }
6301
FocusTopMostWindowUnderOne(ImGuiWindow * under_this_window,ImGuiWindow * ignore_window)6302 void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window)
6303 {
6304 ImGuiContext& g = *GImGui;
6305
6306 int start_idx = g.WindowsFocusOrder.Size - 1;
6307 if (under_this_window != NULL)
6308 {
6309 int under_this_window_idx = FindWindowFocusIndex(under_this_window);
6310 if (under_this_window_idx != -1)
6311 start_idx = under_this_window_idx - 1;
6312 }
6313 for (int i = start_idx; i >= 0; i--)
6314 {
6315 // 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.
6316 ImGuiWindow* window = g.WindowsFocusOrder[i];
6317 if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow))
6318 if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
6319 {
6320 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
6321 FocusWindow(focus_window);
6322 return;
6323 }
6324 }
6325 FocusWindow(NULL);
6326 }
6327
SetCurrentFont(ImFont * font)6328 void ImGui::SetCurrentFont(ImFont* font)
6329 {
6330 ImGuiContext& g = *GImGui;
6331 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
6332 IM_ASSERT(font->Scale > 0.0f);
6333 g.Font = font;
6334 g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
6335 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
6336
6337 ImFontAtlas* atlas = g.Font->ContainerAtlas;
6338 g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
6339 g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
6340 g.DrawListSharedData.Font = g.Font;
6341 g.DrawListSharedData.FontSize = g.FontSize;
6342 }
6343
PushFont(ImFont * font)6344 void ImGui::PushFont(ImFont* font)
6345 {
6346 ImGuiContext& g = *GImGui;
6347 if (!font)
6348 font = GetDefaultFont();
6349 SetCurrentFont(font);
6350 g.FontStack.push_back(font);
6351 g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
6352 }
6353
PopFont()6354 void ImGui::PopFont()
6355 {
6356 ImGuiContext& g = *GImGui;
6357 g.CurrentWindow->DrawList->PopTextureID();
6358 g.FontStack.pop_back();
6359 SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
6360 }
6361
PushItemFlag(ImGuiItemFlags option,bool enabled)6362 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
6363 {
6364 ImGuiContext& g = *GImGui;
6365 ImGuiWindow* window = g.CurrentWindow;
6366 ImGuiItemFlags item_flags = window->DC.ItemFlags;
6367 IM_ASSERT(item_flags == g.ItemFlagsStack.back());
6368 if (enabled)
6369 item_flags |= option;
6370 else
6371 item_flags &= ~option;
6372 window->DC.ItemFlags = item_flags;
6373 g.ItemFlagsStack.push_back(item_flags);
6374 }
6375
PopItemFlag()6376 void ImGui::PopItemFlag()
6377 {
6378 ImGuiContext& g = *GImGui;
6379 ImGuiWindow* window = g.CurrentWindow;
6380 IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack.
6381 g.ItemFlagsStack.pop_back();
6382 window->DC.ItemFlags = g.ItemFlagsStack.back();
6383 }
6384
6385 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)6386 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
6387 {
6388 PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
6389 }
6390
PopAllowKeyboardFocus()6391 void ImGui::PopAllowKeyboardFocus()
6392 {
6393 PopItemFlag();
6394 }
6395
PushButtonRepeat(bool repeat)6396 void ImGui::PushButtonRepeat(bool repeat)
6397 {
6398 PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
6399 }
6400
PopButtonRepeat()6401 void ImGui::PopButtonRepeat()
6402 {
6403 PopItemFlag();
6404 }
6405
PushTextWrapPos(float wrap_pos_x)6406 void ImGui::PushTextWrapPos(float wrap_pos_x)
6407 {
6408 ImGuiWindow* window = GetCurrentWindow();
6409 window->DC.TextWrapPos = wrap_pos_x;
6410 window->DC.TextWrapPosStack.push_back(wrap_pos_x);
6411 }
6412
PopTextWrapPos()6413 void ImGui::PopTextWrapPos()
6414 {
6415 ImGuiWindow* window = GetCurrentWindow();
6416 window->DC.TextWrapPosStack.pop_back();
6417 window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
6418 }
6419
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)6420 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
6421 {
6422 if (window->RootWindow == potential_parent)
6423 return true;
6424 while (window != NULL)
6425 {
6426 if (window == potential_parent)
6427 return true;
6428 window = window->ParentWindow;
6429 }
6430 return false;
6431 }
6432
IsWindowAbove(ImGuiWindow * potential_above,ImGuiWindow * potential_below)6433 bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)
6434 {
6435 ImGuiContext& g = *GImGui;
6436 for (int i = g.Windows.Size - 1; i >= 0; i--)
6437 {
6438 ImGuiWindow* candidate_window = g.Windows[i];
6439 if (candidate_window == potential_above)
6440 return true;
6441 if (candidate_window == potential_below)
6442 return false;
6443 }
6444 return false;
6445 }
6446
IsWindowHovered(ImGuiHoveredFlags flags)6447 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
6448 {
6449 IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
6450 ImGuiContext& g = *GImGui;
6451
6452 if (flags & ImGuiHoveredFlags_AnyWindow)
6453 {
6454 if (g.HoveredWindow == NULL)
6455 return false;
6456 }
6457 else
6458 {
6459 switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
6460 {
6461 case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
6462 if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
6463 return false;
6464 break;
6465 case ImGuiHoveredFlags_RootWindow:
6466 if (g.HoveredWindow != g.CurrentWindow->RootWindow)
6467 return false;
6468 break;
6469 case ImGuiHoveredFlags_ChildWindows:
6470 if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
6471 return false;
6472 break;
6473 default:
6474 if (g.HoveredWindow != g.CurrentWindow)
6475 return false;
6476 break;
6477 }
6478 }
6479
6480 if (!IsWindowContentHoverable(g.HoveredWindow, flags))
6481 return false;
6482 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
6483 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
6484 return false;
6485 return true;
6486 }
6487
IsWindowFocused(ImGuiFocusedFlags flags)6488 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
6489 {
6490 ImGuiContext& g = *GImGui;
6491
6492 if (flags & ImGuiFocusedFlags_AnyWindow)
6493 return g.NavWindow != NULL;
6494
6495 IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End()
6496 switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
6497 {
6498 case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
6499 return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
6500 case ImGuiFocusedFlags_RootWindow:
6501 return g.NavWindow == g.CurrentWindow->RootWindow;
6502 case ImGuiFocusedFlags_ChildWindows:
6503 return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
6504 default:
6505 return g.NavWindow == g.CurrentWindow;
6506 }
6507 }
6508
6509 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
6510 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
6511 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)6512 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
6513 {
6514 return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
6515 }
6516
GetWindowWidth()6517 float ImGui::GetWindowWidth()
6518 {
6519 ImGuiWindow* window = GImGui->CurrentWindow;
6520 return window->Size.x;
6521 }
6522
GetWindowHeight()6523 float ImGui::GetWindowHeight()
6524 {
6525 ImGuiWindow* window = GImGui->CurrentWindow;
6526 return window->Size.y;
6527 }
6528
GetWindowPos()6529 ImVec2 ImGui::GetWindowPos()
6530 {
6531 ImGuiContext& g = *GImGui;
6532 ImGuiWindow* window = g.CurrentWindow;
6533 return window->Pos;
6534 }
6535
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)6536 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
6537 {
6538 // Test condition (NB: bit 0 is always true) and clear flags for next time
6539 if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
6540 return;
6541
6542 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6543 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6544 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
6545
6546 // Set
6547 const ImVec2 old_pos = window->Pos;
6548 window->Pos = ImFloor(pos);
6549 ImVec2 offset = window->Pos - old_pos;
6550 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
6551 window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
6552 window->DC.CursorStartPos += offset;
6553 }
6554
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)6555 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
6556 {
6557 ImGuiWindow* window = GetCurrentWindowRead();
6558 SetWindowPos(window, pos, cond);
6559 }
6560
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)6561 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
6562 {
6563 if (ImGuiWindow* window = FindWindowByName(name))
6564 SetWindowPos(window, pos, cond);
6565 }
6566
GetWindowSize()6567 ImVec2 ImGui::GetWindowSize()
6568 {
6569 ImGuiWindow* window = GetCurrentWindowRead();
6570 return window->Size;
6571 }
6572
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)6573 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
6574 {
6575 // Test condition (NB: bit 0 is always true) and clear flags for next time
6576 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
6577 return;
6578
6579 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6580 window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6581
6582 // Set
6583 if (size.x > 0.0f)
6584 {
6585 window->AutoFitFramesX = 0;
6586 window->SizeFull.x = IM_FLOOR(size.x);
6587 }
6588 else
6589 {
6590 window->AutoFitFramesX = 2;
6591 window->AutoFitOnlyGrows = false;
6592 }
6593 if (size.y > 0.0f)
6594 {
6595 window->AutoFitFramesY = 0;
6596 window->SizeFull.y = IM_FLOOR(size.y);
6597 }
6598 else
6599 {
6600 window->AutoFitFramesY = 2;
6601 window->AutoFitOnlyGrows = false;
6602 }
6603 }
6604
SetWindowSize(const ImVec2 & size,ImGuiCond cond)6605 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
6606 {
6607 SetWindowSize(GImGui->CurrentWindow, size, cond);
6608 }
6609
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)6610 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
6611 {
6612 if (ImGuiWindow* window = FindWindowByName(name))
6613 SetWindowSize(window, size, cond);
6614 }
6615
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)6616 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
6617 {
6618 // Test condition (NB: bit 0 is always true) and clear flags for next time
6619 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
6620 return;
6621 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6622
6623 // Set
6624 window->Collapsed = collapsed;
6625 }
6626
SetWindowHitTestHole(ImGuiWindow * window,const ImVec2 & pos,const ImVec2 & size)6627 void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
6628 {
6629 IM_ASSERT(window->HitTestHoleSize.x == 0); // We don't support multiple holes/hit test filters
6630 window->HitTestHoleSize = ImVec2ih(size);
6631 window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
6632 }
6633
SetWindowCollapsed(bool collapsed,ImGuiCond cond)6634 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
6635 {
6636 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
6637 }
6638
IsWindowCollapsed()6639 bool ImGui::IsWindowCollapsed()
6640 {
6641 ImGuiWindow* window = GetCurrentWindowRead();
6642 return window->Collapsed;
6643 }
6644
IsWindowAppearing()6645 bool ImGui::IsWindowAppearing()
6646 {
6647 ImGuiWindow* window = GetCurrentWindowRead();
6648 return window->Appearing;
6649 }
6650
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)6651 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
6652 {
6653 if (ImGuiWindow* window = FindWindowByName(name))
6654 SetWindowCollapsed(window, collapsed, cond);
6655 }
6656
SetWindowFocus()6657 void ImGui::SetWindowFocus()
6658 {
6659 FocusWindow(GImGui->CurrentWindow);
6660 }
6661
SetWindowFocus(const char * name)6662 void ImGui::SetWindowFocus(const char* name)
6663 {
6664 if (name)
6665 {
6666 if (ImGuiWindow* window = FindWindowByName(name))
6667 FocusWindow(window);
6668 }
6669 else
6670 {
6671 FocusWindow(NULL);
6672 }
6673 }
6674
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)6675 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
6676 {
6677 ImGuiContext& g = *GImGui;
6678 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6679 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
6680 g.NextWindowData.PosVal = pos;
6681 g.NextWindowData.PosPivotVal = pivot;
6682 g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
6683 }
6684
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)6685 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
6686 {
6687 ImGuiContext& g = *GImGui;
6688 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6689 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
6690 g.NextWindowData.SizeVal = size;
6691 g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
6692 }
6693
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)6694 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
6695 {
6696 ImGuiContext& g = *GImGui;
6697 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
6698 g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
6699 g.NextWindowData.SizeCallback = custom_callback;
6700 g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
6701 }
6702
6703 // Content size = inner scrollable rectangle, padded with WindowPadding.
6704 // SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
SetNextWindowContentSize(const ImVec2 & size)6705 void ImGui::SetNextWindowContentSize(const ImVec2& size)
6706 {
6707 ImGuiContext& g = *GImGui;
6708 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
6709 g.NextWindowData.ContentSizeVal = ImFloor(size);
6710 }
6711
SetNextWindowScroll(const ImVec2 & scroll)6712 void ImGui::SetNextWindowScroll(const ImVec2& scroll)
6713 {
6714 ImGuiContext& g = *GImGui;
6715 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll;
6716 g.NextWindowData.ScrollVal = scroll;
6717 }
6718
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)6719 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
6720 {
6721 ImGuiContext& g = *GImGui;
6722 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6723 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
6724 g.NextWindowData.CollapsedVal = collapsed;
6725 g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
6726 }
6727
SetNextWindowFocus()6728 void ImGui::SetNextWindowFocus()
6729 {
6730 ImGuiContext& g = *GImGui;
6731 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
6732 }
6733
SetNextWindowBgAlpha(float alpha)6734 void ImGui::SetNextWindowBgAlpha(float alpha)
6735 {
6736 ImGuiContext& g = *GImGui;
6737 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
6738 g.NextWindowData.BgAlphaVal = alpha;
6739 }
6740
GetWindowDrawList()6741 ImDrawList* ImGui::GetWindowDrawList()
6742 {
6743 ImGuiWindow* window = GetCurrentWindow();
6744 return window->DrawList;
6745 }
6746
GetFont()6747 ImFont* ImGui::GetFont()
6748 {
6749 return GImGui->Font;
6750 }
6751
GetFontSize()6752 float ImGui::GetFontSize()
6753 {
6754 return GImGui->FontSize;
6755 }
6756
GetFontTexUvWhitePixel()6757 ImVec2 ImGui::GetFontTexUvWhitePixel()
6758 {
6759 return GImGui->DrawListSharedData.TexUvWhitePixel;
6760 }
6761
SetWindowFontScale(float scale)6762 void ImGui::SetWindowFontScale(float scale)
6763 {
6764 IM_ASSERT(scale > 0.0f);
6765 ImGuiContext& g = *GImGui;
6766 ImGuiWindow* window = GetCurrentWindow();
6767 window->FontWindowScale = scale;
6768 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
6769 }
6770
ActivateItem(ImGuiID id)6771 void ImGui::ActivateItem(ImGuiID id)
6772 {
6773 ImGuiContext& g = *GImGui;
6774 g.NavNextActivateId = id;
6775 }
6776
PushFocusScope(ImGuiID id)6777 void ImGui::PushFocusScope(ImGuiID id)
6778 {
6779 ImGuiContext& g = *GImGui;
6780 ImGuiWindow* window = g.CurrentWindow;
6781 g.FocusScopeStack.push_back(window->DC.NavFocusScopeIdCurrent);
6782 window->DC.NavFocusScopeIdCurrent = id;
6783 }
6784
PopFocusScope()6785 void ImGui::PopFocusScope()
6786 {
6787 ImGuiContext& g = *GImGui;
6788 ImGuiWindow* window = g.CurrentWindow;
6789 IM_ASSERT(g.FocusScopeStack.Size > 0); // Too many PopFocusScope() ?
6790 window->DC.NavFocusScopeIdCurrent = g.FocusScopeStack.back();
6791 g.FocusScopeStack.pop_back();
6792 }
6793
SetKeyboardFocusHere(int offset)6794 void ImGui::SetKeyboardFocusHere(int offset)
6795 {
6796 IM_ASSERT(offset >= -1); // -1 is allowed but not below
6797 ImGuiContext& g = *GImGui;
6798 ImGuiWindow* window = g.CurrentWindow;
6799 g.FocusRequestNextWindow = window;
6800 g.FocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset;
6801 g.FocusRequestNextCounterTabStop = INT_MAX;
6802 }
6803
SetItemDefaultFocus()6804 void ImGui::SetItemDefaultFocus()
6805 {
6806 ImGuiContext& g = *GImGui;
6807 ImGuiWindow* window = g.CurrentWindow;
6808 if (!window->Appearing)
6809 return;
6810 if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
6811 {
6812 g.NavInitRequest = false;
6813 g.NavInitResultId = g.NavWindow->DC.LastItemId;
6814 g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
6815 NavUpdateAnyRequestFlag();
6816 if (!IsItemVisible())
6817 SetScrollHereY();
6818 }
6819 }
6820
SetStateStorage(ImGuiStorage * tree)6821 void ImGui::SetStateStorage(ImGuiStorage* tree)
6822 {
6823 ImGuiWindow* window = GImGui->CurrentWindow;
6824 window->DC.StateStorage = tree ? tree : &window->StateStorage;
6825 }
6826
GetStateStorage()6827 ImGuiStorage* ImGui::GetStateStorage()
6828 {
6829 ImGuiWindow* window = GImGui->CurrentWindow;
6830 return window->DC.StateStorage;
6831 }
6832
PushID(const char * str_id)6833 void ImGui::PushID(const char* str_id)
6834 {
6835 ImGuiContext& g = *GImGui;
6836 ImGuiWindow* window = g.CurrentWindow;
6837 ImGuiID id = window->GetIDNoKeepAlive(str_id);
6838 window->IDStack.push_back(id);
6839 }
6840
PushID(const char * str_id_begin,const char * str_id_end)6841 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6842 {
6843 ImGuiContext& g = *GImGui;
6844 ImGuiWindow* window = g.CurrentWindow;
6845 ImGuiID id = window->GetIDNoKeepAlive(str_id_begin, str_id_end);
6846 window->IDStack.push_back(id);
6847 }
6848
PushID(const void * ptr_id)6849 void ImGui::PushID(const void* ptr_id)
6850 {
6851 ImGuiContext& g = *GImGui;
6852 ImGuiWindow* window = g.CurrentWindow;
6853 ImGuiID id = window->GetIDNoKeepAlive(ptr_id);
6854 window->IDStack.push_back(id);
6855 }
6856
PushID(int int_id)6857 void ImGui::PushID(int int_id)
6858 {
6859 ImGuiContext& g = *GImGui;
6860 ImGuiWindow* window = g.CurrentWindow;
6861 ImGuiID id = window->GetIDNoKeepAlive(int_id);
6862 window->IDStack.push_back(id);
6863 }
6864
6865 // Push a given id value ignoring the ID stack as a seed.
PushOverrideID(ImGuiID id)6866 void ImGui::PushOverrideID(ImGuiID id)
6867 {
6868 ImGuiContext& g = *GImGui;
6869 ImGuiWindow* window = g.CurrentWindow;
6870 window->IDStack.push_back(id);
6871 }
6872
6873 // Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call
6874 // (note that when using this pattern, TestEngine's "Stack Tool" will tend to not display the intermediate stack level.
6875 // for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more)
GetIDWithSeed(const char * str,const char * str_end,ImGuiID seed)6876 ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
6877 {
6878 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
6879 ImGui::KeepAliveID(id);
6880 #ifdef IMGUI_ENABLE_TEST_ENGINE
6881 ImGuiContext& g = *GImGui;
6882 IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
6883 #endif
6884 return id;
6885 }
6886
PopID()6887 void ImGui::PopID()
6888 {
6889 ImGuiWindow* window = GImGui->CurrentWindow;
6890 IM_ASSERT(window->IDStack.Size > 1); // Too many PopID(), or could be popping in a wrong/different window?
6891 window->IDStack.pop_back();
6892 }
6893
GetID(const char * str_id)6894 ImGuiID ImGui::GetID(const char* str_id)
6895 {
6896 ImGuiWindow* window = GImGui->CurrentWindow;
6897 return window->GetID(str_id);
6898 }
6899
GetID(const char * str_id_begin,const char * str_id_end)6900 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6901 {
6902 ImGuiWindow* window = GImGui->CurrentWindow;
6903 return window->GetID(str_id_begin, str_id_end);
6904 }
6905
GetID(const void * ptr_id)6906 ImGuiID ImGui::GetID(const void* ptr_id)
6907 {
6908 ImGuiWindow* window = GImGui->CurrentWindow;
6909 return window->GetID(ptr_id);
6910 }
6911
IsRectVisible(const ImVec2 & size)6912 bool ImGui::IsRectVisible(const ImVec2& size)
6913 {
6914 ImGuiWindow* window = GImGui->CurrentWindow;
6915 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
6916 }
6917
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)6918 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
6919 {
6920 ImGuiWindow* window = GImGui->CurrentWindow;
6921 return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
6922 }
6923
6924
6925 //-----------------------------------------------------------------------------
6926 // [SECTION] ERROR CHECKING
6927 //-----------------------------------------------------------------------------
6928
6929 // Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.
6930 // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
6931 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code
6932 // may see different structures than what imgui.cpp sees, which is problematic.
6933 // 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)6934 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)
6935 {
6936 bool error = false;
6937 if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
6938 if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
6939 if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
6940 if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
6941 if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
6942 if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
6943 if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
6944 return !error;
6945 }
6946
ErrorCheckNewFrameSanityChecks()6947 static void ImGui::ErrorCheckNewFrameSanityChecks()
6948 {
6949 ImGuiContext& g = *GImGui;
6950
6951 // Check user IM_ASSERT macro
6952 // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means you assert macro is incorrectly defined!
6953 // If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
6954 // This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
6955 // #define IM_ASSERT(EXPR) if (SomeCode(EXPR)) SomeMoreCode(); // Wrong!
6956 // #define IM_ASSERT(EXPR) do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0) // Correct!
6957 if (true) IM_ASSERT(1); else IM_ASSERT(0);
6958
6959 // Check user data
6960 // (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)
6961 IM_ASSERT(g.Initialized);
6962 IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!");
6963 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
6964 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!");
6965 IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?");
6966 IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?");
6967 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!");
6968 IM_ASSERT(g.Style.CircleSegmentMaxError > 0.0f && "Invalid style setting!");
6969 IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations
6970 IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
6971 IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
6972 for (int n = 0; n < ImGuiKey_COUNT; n++)
6973 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)");
6974
6975 // 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 added in 1.60 WIP)
6976 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
6977 IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
6978
6979 // Check: the io.ConfigWindowsResizeFromEdges option requires backend to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
6980 if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
6981 g.IO.ConfigWindowsResizeFromEdges = false;
6982 }
6983
ErrorCheckEndFrameSanityChecks()6984 static void ImGui::ErrorCheckEndFrameSanityChecks()
6985 {
6986 ImGuiContext& g = *GImGui;
6987
6988 // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
6989 // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
6990 // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
6991 // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
6992 // We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
6993 // while still correctly asserting on mid-frame key press events.
6994 const ImGuiKeyModFlags key_mod_flags = GetMergedKeyModFlags();
6995 IM_ASSERT((key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
6996 IM_UNUSED(key_mod_flags);
6997
6998 // Recover from errors
6999 //ErrorCheckEndFrameRecover();
7000
7001 // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
7002 // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
7003 if (g.CurrentWindowStack.Size != 1)
7004 {
7005 if (g.CurrentWindowStack.Size > 1)
7006 {
7007 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
7008 while (g.CurrentWindowStack.Size > 1)
7009 End();
7010 }
7011 else
7012 {
7013 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
7014 }
7015 }
7016
7017 IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!");
7018 }
7019
7020 // Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
7021 // Must be called during or before EndFrame().
7022 // This is generally flawed as we are not necessarily End/Popping things in the right order.
7023 // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
7024 // FIXME: Can't recover from interleaved BeginTabBar/Begin
ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback,void * user_data)7025 void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data)
7026 {
7027 // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
7028 ImGuiContext& g = *GImGui;
7029 while (g.CurrentWindowStack.Size > 0)
7030 {
7031 #ifdef IMGUI_HAS_TABLE
7032 while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow))
7033 {
7034 if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name);
7035 EndTable();
7036 }
7037 #endif
7038 ImGuiWindow* window = g.CurrentWindow;
7039 IM_ASSERT(window != NULL);
7040 while (g.CurrentTabBar != NULL) //-V1044
7041 {
7042 if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name);
7043 EndTabBar();
7044 }
7045 while (window->DC.TreeDepth > 0)
7046 {
7047 if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name);
7048 TreePop();
7049 }
7050 while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack)
7051 {
7052 if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name);
7053 EndGroup();
7054 }
7055 while (window->IDStack.Size > 1)
7056 {
7057 if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name);
7058 PopID();
7059 }
7060 while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack)
7061 {
7062 if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col));
7063 PopStyleColor();
7064 }
7065 while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack)
7066 {
7067 if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name);
7068 PopStyleVar();
7069 }
7070 while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack)
7071 {
7072 if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name);
7073 PopFocusScope();
7074 }
7075 if (g.CurrentWindowStack.Size == 1)
7076 {
7077 IM_ASSERT(g.CurrentWindow->IsFallbackWindow);
7078 break;
7079 }
7080 IM_ASSERT(window == g.CurrentWindow);
7081 if (window->Flags & ImGuiWindowFlags_ChildWindow)
7082 {
7083 if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'", window->Name);
7084 EndChild();
7085 }
7086 else
7087 {
7088 if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'", window->Name);
7089 End();
7090 }
7091 }
7092 }
7093
7094 // Save current stack sizes for later compare
SetToCurrentState()7095 void ImGuiStackSizes::SetToCurrentState()
7096 {
7097 ImGuiContext& g = *GImGui;
7098 ImGuiWindow* window = g.CurrentWindow;
7099 SizeOfIDStack = (short)window->IDStack.Size;
7100 SizeOfColorStack = (short)g.ColorStack.Size;
7101 SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
7102 SizeOfFontStack = (short)g.FontStack.Size;
7103 SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
7104 SizeOfGroupStack = (short)g.GroupStack.Size;
7105 SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
7106 }
7107
7108 // Compare to detect usage errors
CompareWithCurrentState()7109 void ImGuiStackSizes::CompareWithCurrentState()
7110 {
7111 ImGuiContext& g = *GImGui;
7112 ImGuiWindow* window = g.CurrentWindow;
7113 IM_UNUSED(window);
7114
7115 // Window stacks
7116 // NOT checking: DC.ItemWidth, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
7117 IM_ASSERT(SizeOfIDStack == window->IDStack.Size && "PushID/PopID or TreeNode/TreePop Mismatch!");
7118
7119 // Global stacks
7120 // 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.
7121 IM_ASSERT(SizeOfGroupStack == g.GroupStack.Size && "BeginGroup/EndGroup Mismatch!");
7122 IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!");
7123 IM_ASSERT(SizeOfColorStack >= g.ColorStack.Size && "PushStyleColor/PopStyleColor Mismatch!");
7124 IM_ASSERT(SizeOfStyleVarStack >= g.StyleVarStack.Size && "PushStyleVar/PopStyleVar Mismatch!");
7125 IM_ASSERT(SizeOfFontStack >= g.FontStack.Size && "PushFont/PopFont Mismatch!");
7126 IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size && "PushFocusScope/PopFocusScope Mismatch!");
7127 }
7128
7129
7130 //-----------------------------------------------------------------------------
7131 // [SECTION] LAYOUT
7132 //-----------------------------------------------------------------------------
7133 // - ItemSize()
7134 // - ItemAdd()
7135 // - SameLine()
7136 // - GetCursorScreenPos()
7137 // - SetCursorScreenPos()
7138 // - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
7139 // - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
7140 // - GetCursorStartPos()
7141 // - Indent()
7142 // - Unindent()
7143 // - SetNextItemWidth()
7144 // - PushItemWidth()
7145 // - PushMultiItemsWidths()
7146 // - PopItemWidth()
7147 // - CalcItemWidth()
7148 // - CalcItemSize()
7149 // - GetTextLineHeight()
7150 // - GetTextLineHeightWithSpacing()
7151 // - GetFrameHeight()
7152 // - GetFrameHeightWithSpacing()
7153 // - GetContentRegionMax()
7154 // - GetContentRegionMaxAbs() [Internal]
7155 // - GetContentRegionAvail(),
7156 // - GetWindowContentRegionMin(), GetWindowContentRegionMax()
7157 // - GetWindowContentRegionWidth()
7158 // - BeginGroup()
7159 // - EndGroup()
7160 // Also see in imgui_widgets: tab bars, columns.
7161 //-----------------------------------------------------------------------------
7162
7163 // Advance cursor given item size for layout.
7164 // Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
7165 // See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.
ItemSize(const ImVec2 & size,float text_baseline_y)7166 void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
7167 {
7168 ImGuiContext& g = *GImGui;
7169 ImGuiWindow* window = g.CurrentWindow;
7170 if (window->SkipItems)
7171 return;
7172
7173 // We increase the height in this function to accommodate for baseline offset.
7174 // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
7175 // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
7176 const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
7177 const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
7178
7179 // Always align ourselves on pixel boundaries
7180 //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]
7181 window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
7182 window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
7183 window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line
7184 window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y); // Next line
7185 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
7186 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
7187 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
7188
7189 window->DC.PrevLineSize.y = line_height;
7190 window->DC.CurrLineSize.y = 0.0f;
7191 window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
7192 window->DC.CurrLineTextBaseOffset = 0.0f;
7193
7194 // Horizontal layout mode
7195 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
7196 SameLine();
7197 }
7198
ItemSize(const ImRect & bb,float text_baseline_y)7199 void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
7200 {
7201 ItemSize(bb.GetSize(), text_baseline_y);
7202 }
7203
7204 // Declare item bounding box for clipping and interaction.
7205 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
7206 // declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
ItemAdd(const ImRect & bb,ImGuiID id,const ImRect * nav_bb_arg)7207 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
7208 {
7209 ImGuiContext& g = *GImGui;
7210 ImGuiWindow* window = g.CurrentWindow;
7211
7212 if (id != 0)
7213 {
7214 // Navigation processing runs prior to clipping early-out
7215 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
7216 // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
7217 // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
7218 // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
7219 // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
7220 // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
7221 // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
7222 // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
7223 window->DC.NavLayerActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
7224 if (g.NavId == id || g.NavAnyRequest)
7225 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
7226 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
7227 NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
7228
7229 // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd()
7230 #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
7231 if (id == g.DebugItemPickerBreakId)
7232 {
7233 IM_DEBUG_BREAK();
7234 g.DebugItemPickerBreakId = 0;
7235 }
7236 #endif
7237 }
7238
7239 // Equivalent to calling SetLastItemData()
7240 window->DC.LastItemId = id;
7241 window->DC.LastItemRect = bb;
7242 window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
7243 g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
7244
7245 #ifdef IMGUI_ENABLE_TEST_ENGINE
7246 if (id != 0)
7247 IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id);
7248 #endif
7249
7250 // Clipping test
7251 const bool is_clipped = IsClippedEx(bb, id, false);
7252 if (is_clipped)
7253 return false;
7254 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
7255
7256 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
7257 if (IsMouseHoveringRect(bb.Min, bb.Max))
7258 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
7259 return true;
7260 }
7261
7262 // Gets back to previous line and continue with horizontal layout
7263 // offset_from_start_x == 0 : follow right after previous item
7264 // offset_from_start_x != 0 : align to specified x position (relative to window/group left)
7265 // spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0
7266 // spacing_w >= 0 : enforce spacing amount
SameLine(float offset_from_start_x,float spacing_w)7267 void ImGui::SameLine(float offset_from_start_x, float spacing_w)
7268 {
7269 ImGuiWindow* window = GetCurrentWindow();
7270 if (window->SkipItems)
7271 return;
7272
7273 ImGuiContext& g = *GImGui;
7274 if (offset_from_start_x != 0.0f)
7275 {
7276 if (spacing_w < 0.0f) spacing_w = 0.0f;
7277 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
7278 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7279 }
7280 else
7281 {
7282 if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
7283 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
7284 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7285 }
7286 window->DC.CurrLineSize = window->DC.PrevLineSize;
7287 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
7288 }
7289
GetCursorScreenPos()7290 ImVec2 ImGui::GetCursorScreenPos()
7291 {
7292 ImGuiWindow* window = GetCurrentWindowRead();
7293 return window->DC.CursorPos;
7294 }
7295
SetCursorScreenPos(const ImVec2 & pos)7296 void ImGui::SetCursorScreenPos(const ImVec2& pos)
7297 {
7298 ImGuiWindow* window = GetCurrentWindow();
7299 window->DC.CursorPos = pos;
7300 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7301 }
7302
7303 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
7304 // 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()7305 ImVec2 ImGui::GetCursorPos()
7306 {
7307 ImGuiWindow* window = GetCurrentWindowRead();
7308 return window->DC.CursorPos - window->Pos + window->Scroll;
7309 }
7310
GetCursorPosX()7311 float ImGui::GetCursorPosX()
7312 {
7313 ImGuiWindow* window = GetCurrentWindowRead();
7314 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
7315 }
7316
GetCursorPosY()7317 float ImGui::GetCursorPosY()
7318 {
7319 ImGuiWindow* window = GetCurrentWindowRead();
7320 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
7321 }
7322
SetCursorPos(const ImVec2 & local_pos)7323 void ImGui::SetCursorPos(const ImVec2& local_pos)
7324 {
7325 ImGuiWindow* window = GetCurrentWindow();
7326 window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
7327 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7328 }
7329
SetCursorPosX(float x)7330 void ImGui::SetCursorPosX(float x)
7331 {
7332 ImGuiWindow* window = GetCurrentWindow();
7333 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
7334 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
7335 }
7336
SetCursorPosY(float y)7337 void ImGui::SetCursorPosY(float y)
7338 {
7339 ImGuiWindow* window = GetCurrentWindow();
7340 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
7341 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
7342 }
7343
GetCursorStartPos()7344 ImVec2 ImGui::GetCursorStartPos()
7345 {
7346 ImGuiWindow* window = GetCurrentWindowRead();
7347 return window->DC.CursorStartPos - window->Pos;
7348 }
7349
Indent(float indent_w)7350 void ImGui::Indent(float indent_w)
7351 {
7352 ImGuiContext& g = *GImGui;
7353 ImGuiWindow* window = GetCurrentWindow();
7354 window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7355 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7356 }
7357
Unindent(float indent_w)7358 void ImGui::Unindent(float indent_w)
7359 {
7360 ImGuiContext& g = *GImGui;
7361 ImGuiWindow* window = GetCurrentWindow();
7362 window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7363 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7364 }
7365
7366 // Affect large frame+labels widgets only.
SetNextItemWidth(float item_width)7367 void ImGui::SetNextItemWidth(float item_width)
7368 {
7369 ImGuiContext& g = *GImGui;
7370 g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
7371 g.NextItemData.Width = item_width;
7372 }
7373
PushItemWidth(float item_width)7374 void ImGui::PushItemWidth(float item_width)
7375 {
7376 ImGuiContext& g = *GImGui;
7377 ImGuiWindow* window = g.CurrentWindow;
7378 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
7379 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
7380 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
7381 }
7382
PushMultiItemsWidths(int components,float w_full)7383 void ImGui::PushMultiItemsWidths(int components, float w_full)
7384 {
7385 ImGuiContext& g = *GImGui;
7386 ImGuiWindow* window = g.CurrentWindow;
7387 const ImGuiStyle& style = g.Style;
7388 const float w_item_one = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));
7389 const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));
7390 window->DC.ItemWidthStack.push_back(w_item_last);
7391 for (int i = 0; i < components - 1; i++)
7392 window->DC.ItemWidthStack.push_back(w_item_one);
7393 window->DC.ItemWidth = window->DC.ItemWidthStack.back();
7394 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
7395 }
7396
PopItemWidth()7397 void ImGui::PopItemWidth()
7398 {
7399 ImGuiWindow* window = GetCurrentWindow();
7400 window->DC.ItemWidthStack.pop_back();
7401 window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
7402 }
7403
7404 // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
7405 // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
CalcItemWidth()7406 float ImGui::CalcItemWidth()
7407 {
7408 ImGuiContext& g = *GImGui;
7409 ImGuiWindow* window = g.CurrentWindow;
7410 float w;
7411 if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
7412 w = g.NextItemData.Width;
7413 else
7414 w = window->DC.ItemWidth;
7415 if (w < 0.0f)
7416 {
7417 float region_max_x = GetContentRegionMaxAbs().x;
7418 w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);
7419 }
7420 w = IM_FLOOR(w);
7421 return w;
7422 }
7423
7424 // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
7425 // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
7426 // Note that only CalcItemWidth() is publicly exposed.
7427 // 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)7428 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
7429 {
7430 ImGuiWindow* window = GImGui->CurrentWindow;
7431
7432 ImVec2 region_max;
7433 if (size.x < 0.0f || size.y < 0.0f)
7434 region_max = GetContentRegionMaxAbs();
7435
7436 if (size.x == 0.0f)
7437 size.x = default_w;
7438 else if (size.x < 0.0f)
7439 size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x);
7440
7441 if (size.y == 0.0f)
7442 size.y = default_h;
7443 else if (size.y < 0.0f)
7444 size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y);
7445
7446 return size;
7447 }
7448
GetTextLineHeight()7449 float ImGui::GetTextLineHeight()
7450 {
7451 ImGuiContext& g = *GImGui;
7452 return g.FontSize;
7453 }
7454
GetTextLineHeightWithSpacing()7455 float ImGui::GetTextLineHeightWithSpacing()
7456 {
7457 ImGuiContext& g = *GImGui;
7458 return g.FontSize + g.Style.ItemSpacing.y;
7459 }
7460
GetFrameHeight()7461 float ImGui::GetFrameHeight()
7462 {
7463 ImGuiContext& g = *GImGui;
7464 return g.FontSize + g.Style.FramePadding.y * 2.0f;
7465 }
7466
GetFrameHeightWithSpacing()7467 float ImGui::GetFrameHeightWithSpacing()
7468 {
7469 ImGuiContext& g = *GImGui;
7470 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
7471 }
7472
7473 // FIXME: All the Contents Region function are messy or misleading. WE WILL AIM TO OBSOLETE ALL OF THEM WITH A NEW "WORK RECT" API. Thanks for your patience!
7474
7475 // FIXME: This is in window space (not screen space!).
GetContentRegionMax()7476 ImVec2 ImGui::GetContentRegionMax()
7477 {
7478 ImGuiContext& g = *GImGui;
7479 ImGuiWindow* window = g.CurrentWindow;
7480 ImVec2 mx = window->ContentRegionRect.Max - window->Pos;
7481 if (window->DC.CurrentColumns || g.CurrentTable)
7482 mx.x = window->WorkRect.Max.x - window->Pos.x;
7483 return mx;
7484 }
7485
7486 // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.
GetContentRegionMaxAbs()7487 ImVec2 ImGui::GetContentRegionMaxAbs()
7488 {
7489 ImGuiContext& g = *GImGui;
7490 ImGuiWindow* window = g.CurrentWindow;
7491 ImVec2 mx = window->ContentRegionRect.Max;
7492 if (window->DC.CurrentColumns || g.CurrentTable)
7493 mx.x = window->WorkRect.Max.x;
7494 return mx;
7495 }
7496
GetContentRegionAvail()7497 ImVec2 ImGui::GetContentRegionAvail()
7498 {
7499 ImGuiWindow* window = GImGui->CurrentWindow;
7500 return GetContentRegionMaxAbs() - window->DC.CursorPos;
7501 }
7502
7503 // In window space (not screen space!)
GetWindowContentRegionMin()7504 ImVec2 ImGui::GetWindowContentRegionMin()
7505 {
7506 ImGuiWindow* window = GImGui->CurrentWindow;
7507 return window->ContentRegionRect.Min - window->Pos;
7508 }
7509
GetWindowContentRegionMax()7510 ImVec2 ImGui::GetWindowContentRegionMax()
7511 {
7512 ImGuiWindow* window = GImGui->CurrentWindow;
7513 return window->ContentRegionRect.Max - window->Pos;
7514 }
7515
GetWindowContentRegionWidth()7516 float ImGui::GetWindowContentRegionWidth()
7517 {
7518 ImGuiWindow* window = GImGui->CurrentWindow;
7519 return window->ContentRegionRect.GetWidth();
7520 }
7521
7522 // 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.)
7523 // Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
BeginGroup()7524 void ImGui::BeginGroup()
7525 {
7526 ImGuiContext& g = *GImGui;
7527 ImGuiWindow* window = g.CurrentWindow;
7528
7529 g.GroupStack.resize(g.GroupStack.Size + 1);
7530 ImGuiGroupData& group_data = g.GroupStack.back();
7531 group_data.WindowID = window->ID;
7532 group_data.BackupCursorPos = window->DC.CursorPos;
7533 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
7534 group_data.BackupIndent = window->DC.Indent;
7535 group_data.BackupGroupOffset = window->DC.GroupOffset;
7536 group_data.BackupCurrLineSize = window->DC.CurrLineSize;
7537 group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
7538 group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
7539 group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
7540 group_data.EmitItem = true;
7541
7542 window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
7543 window->DC.Indent = window->DC.GroupOffset;
7544 window->DC.CursorMaxPos = window->DC.CursorPos;
7545 window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
7546 if (g.LogEnabled)
7547 g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
7548 }
7549
EndGroup()7550 void ImGui::EndGroup()
7551 {
7552 ImGuiContext& g = *GImGui;
7553 ImGuiWindow* window = g.CurrentWindow;
7554 IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls
7555
7556 ImGuiGroupData& group_data = g.GroupStack.back();
7557 IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window?
7558
7559 ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));
7560
7561 window->DC.CursorPos = group_data.BackupCursorPos;
7562 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
7563 window->DC.Indent = group_data.BackupIndent;
7564 window->DC.GroupOffset = group_data.BackupGroupOffset;
7565 window->DC.CurrLineSize = group_data.BackupCurrLineSize;
7566 window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
7567 if (g.LogEnabled)
7568 g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
7569
7570 if (!group_data.EmitItem)
7571 {
7572 g.GroupStack.pop_back();
7573 return;
7574 }
7575
7576 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.
7577 ItemSize(group_bb.GetSize());
7578 ItemAdd(group_bb, 0);
7579
7580 // 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.
7581 // 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.
7582 // Also if you grep for LastItemId you'll notice it is only used in that context.
7583 // (The two tests not the same because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.)
7584 const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
7585 const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true);
7586 if (group_contains_curr_active_id)
7587 window->DC.LastItemId = g.ActiveId;
7588 else if (group_contains_prev_active_id)
7589 window->DC.LastItemId = g.ActiveIdPreviousFrame;
7590 window->DC.LastItemRect = group_bb;
7591
7592 // Forward Edited flag
7593 if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
7594 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
7595
7596 // Forward Deactivated flag
7597 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
7598 if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
7599 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated;
7600
7601 g.GroupStack.pop_back();
7602 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
7603 }
7604
7605
7606 //-----------------------------------------------------------------------------
7607 // [SECTION] SCROLLING
7608 //-----------------------------------------------------------------------------
7609
7610 // Helper to snap on edges when aiming at an item very close to the edge,
7611 // So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
7612 // When we refactor the scrolling API this may be configurable with a flag?
7613 // Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.
CalcScrollEdgeSnap(float target,float snap_min,float snap_max,float snap_threshold,float center_ratio)7614 static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
7615 {
7616 if (target <= snap_min + snap_threshold)
7617 return ImLerp(snap_min, target, center_ratio);
7618 if (target >= snap_max - snap_threshold)
7619 return ImLerp(target, snap_max, center_ratio);
7620 return target;
7621 }
7622
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window)7623 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
7624 {
7625 ImVec2 scroll = window->Scroll;
7626 if (window->ScrollTarget.x < FLT_MAX)
7627 {
7628 float center_x_ratio = window->ScrollTargetCenterRatio.x;
7629 float scroll_target_x = window->ScrollTarget.x;
7630 float snap_x_min = 0.0f;
7631 float snap_x_max = window->ScrollMax.x + window->Size.x;
7632 if (window->ScrollTargetEdgeSnapDist.x > 0.0f)
7633 scroll_target_x = CalcScrollEdgeSnap(scroll_target_x, snap_x_min, snap_x_max, window->ScrollTargetEdgeSnapDist.x, center_x_ratio);
7634 scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - window->ScrollbarSizes.x);
7635 }
7636 if (window->ScrollTarget.y < FLT_MAX)
7637 {
7638 float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
7639 float center_y_ratio = window->ScrollTargetCenterRatio.y;
7640 float scroll_target_y = window->ScrollTarget.y;
7641 float snap_y_min = 0.0f;
7642 float snap_y_max = window->ScrollMax.y + window->Size.y - decoration_up_height;
7643 if (window->ScrollTargetEdgeSnapDist.y > 0.0f)
7644 scroll_target_y = CalcScrollEdgeSnap(scroll_target_y, snap_y_min, snap_y_max, window->ScrollTargetEdgeSnapDist.y, center_y_ratio);
7645 scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height);
7646 }
7647 scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f));
7648 scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f));
7649 if (!window->Collapsed && !window->SkipItems)
7650 {
7651 scroll.x = ImMin(scroll.x, window->ScrollMax.x);
7652 scroll.y = ImMin(scroll.y, window->ScrollMax.y);
7653 }
7654 return scroll;
7655 }
7656
7657 // Scroll to keep newly navigated item fully into view
ScrollToBringRectIntoView(ImGuiWindow * window,const ImRect & item_rect)7658 ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect)
7659 {
7660 ImGuiContext& g = *GImGui;
7661 ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
7662 //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7663
7664 ImVec2 delta_scroll;
7665 if (!window_rect.Contains(item_rect))
7666 {
7667 if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7668 SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f);
7669 else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7670 SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f);
7671 if (item_rect.Min.y < window_rect.Min.y)
7672 SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f);
7673 else if (item_rect.Max.y >= window_rect.Max.y)
7674 SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
7675
7676 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
7677 delta_scroll = next_scroll - window->Scroll;
7678 }
7679
7680 // Also scroll parent window to keep us into view if necessary
7681 if (window->Flags & ImGuiWindowFlags_ChildWindow)
7682 delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll));
7683
7684 return delta_scroll;
7685 }
7686
GetScrollX()7687 float ImGui::GetScrollX()
7688 {
7689 ImGuiWindow* window = GImGui->CurrentWindow;
7690 return window->Scroll.x;
7691 }
7692
GetScrollY()7693 float ImGui::GetScrollY()
7694 {
7695 ImGuiWindow* window = GImGui->CurrentWindow;
7696 return window->Scroll.y;
7697 }
7698
GetScrollMaxX()7699 float ImGui::GetScrollMaxX()
7700 {
7701 ImGuiWindow* window = GImGui->CurrentWindow;
7702 return window->ScrollMax.x;
7703 }
7704
GetScrollMaxY()7705 float ImGui::GetScrollMaxY()
7706 {
7707 ImGuiWindow* window = GImGui->CurrentWindow;
7708 return window->ScrollMax.y;
7709 }
7710
SetScrollX(ImGuiWindow * window,float scroll_x)7711 void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)
7712 {
7713 window->ScrollTarget.x = scroll_x;
7714 window->ScrollTargetCenterRatio.x = 0.0f;
7715 window->ScrollTargetEdgeSnapDist.x = 0.0f;
7716 }
7717
SetScrollY(ImGuiWindow * window,float scroll_y)7718 void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)
7719 {
7720 window->ScrollTarget.y = scroll_y;
7721 window->ScrollTargetCenterRatio.y = 0.0f;
7722 window->ScrollTargetEdgeSnapDist.y = 0.0f;
7723 }
7724
SetScrollX(float scroll_x)7725 void ImGui::SetScrollX(float scroll_x)
7726 {
7727 ImGuiContext& g = *GImGui;
7728 SetScrollX(g.CurrentWindow, scroll_x);
7729 }
7730
SetScrollY(float scroll_y)7731 void ImGui::SetScrollY(float scroll_y)
7732 {
7733 ImGuiContext& g = *GImGui;
7734 SetScrollY(g.CurrentWindow, scroll_y);
7735 }
7736
7737 // Note that a local position will vary depending on initial scroll value,
7738 // This is a little bit confusing so bear with us:
7739 // - local_pos = (absolution_pos - window->Pos)
7740 // - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,
7741 // and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.
7742 // - They mostly exists because of legacy API.
7743 // Following the rules above, when trying to work with scrolling code, consider that:
7744 // - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!
7745 // - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense
7746 // We store a target position so centering and clamping can occur on the next frame when we are guaranteed to have a known window size
SetScrollFromPosX(ImGuiWindow * window,float local_x,float center_x_ratio)7747 void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
7748 {
7749 IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
7750 window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x); // Convert local position to scroll offset
7751 window->ScrollTargetCenterRatio.x = center_x_ratio;
7752 window->ScrollTargetEdgeSnapDist.x = 0.0f;
7753 }
7754
SetScrollFromPosY(ImGuiWindow * window,float local_y,float center_y_ratio)7755 void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
7756 {
7757 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
7758 local_y -= window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect
7759 window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); // Convert local position to scroll offset
7760 window->ScrollTargetCenterRatio.y = center_y_ratio;
7761 window->ScrollTargetEdgeSnapDist.y = 0.0f;
7762 }
7763
SetScrollFromPosX(float local_x,float center_x_ratio)7764 void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
7765 {
7766 ImGuiContext& g = *GImGui;
7767 SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
7768 }
7769
SetScrollFromPosY(float local_y,float center_y_ratio)7770 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
7771 {
7772 ImGuiContext& g = *GImGui;
7773 SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
7774 }
7775
7776 // 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)7777 void ImGui::SetScrollHereX(float center_x_ratio)
7778 {
7779 ImGuiContext& g = *GImGui;
7780 ImGuiWindow* window = g.CurrentWindow;
7781 float spacing_x = g.Style.ItemSpacing.x;
7782 float target_pos_x = ImLerp(window->DC.LastItemRect.Min.x - spacing_x, window->DC.LastItemRect.Max.x + spacing_x, center_x_ratio);
7783 SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos
7784
7785 // Tweak: snap on edges when aiming at an item very close to the edge
7786 window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x);
7787 }
7788
7789 // 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)7790 void ImGui::SetScrollHereY(float center_y_ratio)
7791 {
7792 ImGuiContext& g = *GImGui;
7793 ImGuiWindow* window = g.CurrentWindow;
7794 float spacing_y = g.Style.ItemSpacing.y;
7795 float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);
7796 SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos
7797
7798 // Tweak: snap on edges when aiming at an item very close to the edge
7799 window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y);
7800 }
7801
7802 //-----------------------------------------------------------------------------
7803 // [SECTION] TOOLTIPS
7804 //-----------------------------------------------------------------------------
7805
BeginTooltip()7806 void ImGui::BeginTooltip()
7807 {
7808 BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None);
7809 }
7810
BeginTooltipEx(ImGuiWindowFlags extra_flags,ImGuiTooltipFlags tooltip_flags)7811 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags)
7812 {
7813 ImGuiContext& g = *GImGui;
7814
7815 if (g.DragDropWithinSource || g.DragDropWithinTarget)
7816 {
7817 // 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)
7818 // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
7819 // 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.
7820 //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
7821 ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
7822 SetNextWindowPos(tooltip_pos);
7823 SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
7824 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
7825 tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip;
7826 }
7827
7828 char window_name[16];
7829 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
7830 if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip)
7831 if (ImGuiWindow* window = FindWindowByName(window_name))
7832 if (window->Active)
7833 {
7834 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
7835 window->Hidden = true;
7836 window->HiddenFramesCanSkipItems = 1; // FIXME: This may not be necessary?
7837 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
7838 }
7839 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize;
7840 Begin(window_name, NULL, flags | extra_flags);
7841 }
7842
EndTooltip()7843 void ImGui::EndTooltip()
7844 {
7845 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
7846 End();
7847 }
7848
SetTooltipV(const char * fmt,va_list args)7849 void ImGui::SetTooltipV(const char* fmt, va_list args)
7850 {
7851 BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip);
7852 TextV(fmt, args);
7853 EndTooltip();
7854 }
7855
SetTooltip(const char * fmt,...)7856 void ImGui::SetTooltip(const char* fmt, ...)
7857 {
7858 va_list args;
7859 va_start(args, fmt);
7860 SetTooltipV(fmt, args);
7861 va_end(args);
7862 }
7863
7864 //-----------------------------------------------------------------------------
7865 // [SECTION] POPUPS
7866 //-----------------------------------------------------------------------------
7867
7868 // Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
IsPopupOpen(ImGuiID id,ImGuiPopupFlags popup_flags)7869 bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
7870 {
7871 ImGuiContext& g = *GImGui;
7872 if (popup_flags & ImGuiPopupFlags_AnyPopupId)
7873 {
7874 // Return true if any popup is open at the current BeginPopup() level of the popup stack
7875 // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
7876 IM_ASSERT(id == 0);
7877 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
7878 return g.OpenPopupStack.Size > 0;
7879 else
7880 return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
7881 }
7882 else
7883 {
7884 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
7885 {
7886 // Return true if the popup is open anywhere in the popup stack
7887 for (int n = 0; n < g.OpenPopupStack.Size; n++)
7888 if (g.OpenPopupStack[n].PopupId == id)
7889 return true;
7890 return false;
7891 }
7892 else
7893 {
7894 // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
7895 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
7896 }
7897 }
7898 }
7899
IsPopupOpen(const char * str_id,ImGuiPopupFlags popup_flags)7900 bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
7901 {
7902 ImGuiContext& g = *GImGui;
7903 ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);
7904 if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
7905 IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
7906 return IsPopupOpen(id, popup_flags);
7907 }
7908
GetTopMostPopupModal()7909 ImGuiWindow* ImGui::GetTopMostPopupModal()
7910 {
7911 ImGuiContext& g = *GImGui;
7912 for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
7913 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
7914 if (popup->Flags & ImGuiWindowFlags_Modal)
7915 return popup;
7916 return NULL;
7917 }
7918
OpenPopup(const char * str_id,ImGuiPopupFlags popup_flags)7919 void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
7920 {
7921 ImGuiContext& g = *GImGui;
7922 OpenPopupEx(g.CurrentWindow->GetID(str_id), popup_flags);
7923 }
7924
7925 // Mark popup as open (toggle toward open state).
7926 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
7927 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
7928 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id,ImGuiPopupFlags popup_flags)7929 void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
7930 {
7931 ImGuiContext& g = *GImGui;
7932 ImGuiWindow* parent_window = g.CurrentWindow;
7933 const int current_stack_size = g.BeginPopupStack.Size;
7934
7935 if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
7936 if (IsPopupOpen(0u, ImGuiPopupFlags_AnyPopupId))
7937 return;
7938
7939 ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
7940 popup_ref.PopupId = id;
7941 popup_ref.Window = NULL;
7942 popup_ref.SourceWindow = g.NavWindow;
7943 popup_ref.OpenFrameCount = g.FrameCount;
7944 popup_ref.OpenParentId = parent_window->IDStack.back();
7945 popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
7946 popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
7947
7948 IMGUI_DEBUG_LOG_POPUP("OpenPopupEx(0x%08X)\n", id);
7949 if (g.OpenPopupStack.Size < current_stack_size + 1)
7950 {
7951 g.OpenPopupStack.push_back(popup_ref);
7952 }
7953 else
7954 {
7955 // 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
7956 // 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
7957 // 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.
7958 if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
7959 {
7960 g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
7961 }
7962 else
7963 {
7964 // Close child popups if any, then flag popup for open/reopen
7965 ClosePopupToLevel(current_stack_size, false);
7966 g.OpenPopupStack.push_back(popup_ref);
7967 }
7968
7969 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
7970 // This is equivalent to what ClosePopupToLevel() does.
7971 //if (g.OpenPopupStack[current_stack_size].PopupId == id)
7972 // FocusWindow(parent_window);
7973 }
7974 }
7975
7976 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
7977 // This function closes any popups that are over 'ref_window'.
ClosePopupsOverWindow(ImGuiWindow * ref_window,bool restore_focus_to_window_under_popup)7978 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
7979 {
7980 ImGuiContext& g = *GImGui;
7981 if (g.OpenPopupStack.Size == 0)
7982 return;
7983
7984 // Don't close our own child popup windows.
7985 int popup_count_to_keep = 0;
7986 if (ref_window)
7987 {
7988 // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
7989 for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
7990 {
7991 ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
7992 if (!popup.Window)
7993 continue;
7994 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
7995 if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
7996 continue;
7997
7998 // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
7999 // - With this stack of window, clicking/focusing Popup1 will close Popup2 and Popup3:
8000 // Window -> Popup1 -> Popup2 -> Popup3
8001 // - Each popups may contain child windows, which is why we compare ->RootWindow!
8002 // Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
8003 bool ref_window_is_descendent_of_popup = false;
8004 for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
8005 if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
8006 if (popup_window->RootWindow == ref_window->RootWindow)
8007 {
8008 ref_window_is_descendent_of_popup = true;
8009 break;
8010 }
8011 if (!ref_window_is_descendent_of_popup)
8012 break;
8013 }
8014 }
8015 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
8016 {
8017 IMGUI_DEBUG_LOG_POPUP("ClosePopupsOverWindow(\"%s\") -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
8018 ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
8019 }
8020 }
8021
ClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup)8022 void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
8023 {
8024 ImGuiContext& g = *GImGui;
8025 IMGUI_DEBUG_LOG_POPUP("ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup);
8026 IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
8027
8028 // Trim open popup stack
8029 ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
8030 ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
8031 g.OpenPopupStack.resize(remaining);
8032
8033 if (restore_focus_to_window_under_popup)
8034 {
8035 if (focus_window && !focus_window->WasActive && popup_window)
8036 {
8037 // Fallback
8038 FocusTopMostWindowUnderOne(popup_window, NULL);
8039 }
8040 else
8041 {
8042 if (g.NavLayer == ImGuiNavLayer_Main && focus_window)
8043 focus_window = NavRestoreLastChildNavWindow(focus_window);
8044 FocusWindow(focus_window);
8045 }
8046 }
8047 }
8048
8049 // Close the popup we have begin-ed into.
CloseCurrentPopup()8050 void ImGui::CloseCurrentPopup()
8051 {
8052 ImGuiContext& g = *GImGui;
8053 int popup_idx = g.BeginPopupStack.Size - 1;
8054 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
8055 return;
8056
8057 // Closing a menu closes its top-most parent popup (unless a modal)
8058 while (popup_idx > 0)
8059 {
8060 ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
8061 ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
8062 bool close_parent = false;
8063 if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
8064 if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal))
8065 close_parent = true;
8066 if (!close_parent)
8067 break;
8068 popup_idx--;
8069 }
8070 IMGUI_DEBUG_LOG_POPUP("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
8071 ClosePopupToLevel(popup_idx, true);
8072
8073 // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
8074 // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
8075 // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
8076 if (ImGuiWindow* window = g.NavWindow)
8077 window->DC.NavHideHighlightOneFrame = true;
8078 }
8079
8080 // Attention! BeginPopup() adds default flags which BeginPopupEx()!
BeginPopupEx(ImGuiID id,ImGuiWindowFlags flags)8081 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)
8082 {
8083 ImGuiContext& g = *GImGui;
8084 if (!IsPopupOpen(id, ImGuiPopupFlags_None))
8085 {
8086 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8087 return false;
8088 }
8089
8090 char name[20];
8091 if (flags & ImGuiWindowFlags_ChildMenu)
8092 ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
8093 else
8094 ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
8095
8096 flags |= ImGuiWindowFlags_Popup;
8097 bool is_open = Begin(name, NULL, flags);
8098 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
8099 EndPopup();
8100
8101 return is_open;
8102 }
8103
BeginPopup(const char * str_id,ImGuiWindowFlags flags)8104 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
8105 {
8106 ImGuiContext& g = *GImGui;
8107 if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
8108 {
8109 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8110 return false;
8111 }
8112 flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
8113 return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
8114 }
8115
8116 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
8117 // 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)8118 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
8119 {
8120 ImGuiContext& g = *GImGui;
8121 ImGuiWindow* window = g.CurrentWindow;
8122 const ImGuiID id = window->GetID(name);
8123 if (!IsPopupOpen(id, ImGuiPopupFlags_None))
8124 {
8125 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8126 return false;
8127 }
8128
8129 // Center modal windows by default for increased visibility
8130 // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
8131 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
8132 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
8133 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
8134
8135 flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse;
8136 const bool is_open = Begin(name, p_open, flags);
8137 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
8138 {
8139 EndPopup();
8140 if (is_open)
8141 ClosePopupToLevel(g.BeginPopupStack.Size, true);
8142 return false;
8143 }
8144 return is_open;
8145 }
8146
EndPopup()8147 void ImGui::EndPopup()
8148 {
8149 ImGuiContext& g = *GImGui;
8150 ImGuiWindow* window = g.CurrentWindow;
8151 IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
8152 IM_ASSERT(g.BeginPopupStack.Size > 0);
8153
8154 // Make all menus and popups wrap around for now, may need to expose that policy.
8155 if (g.NavWindow == window)
8156 NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
8157
8158 // Child-popups don't need to be laid out
8159 IM_ASSERT(g.WithinEndChild == false);
8160 if (window->Flags & ImGuiWindowFlags_ChildWindow)
8161 g.WithinEndChild = true;
8162 End();
8163 g.WithinEndChild = false;
8164 }
8165
8166 // Helper to open a popup if mouse button is released over the item
8167 // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()
OpenPopupOnItemClick(const char * str_id,ImGuiPopupFlags popup_flags)8168 void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)
8169 {
8170 ImGuiWindow* window = GImGui->CurrentWindow;
8171 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8172 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8173 {
8174 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!
8175 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
8176 OpenPopupEx(id, popup_flags);
8177 }
8178 }
8179
8180 // This is a helper to handle the simplest case of associating one named popup to one given widget.
8181 // - You can pass a NULL str_id to use the identifier of the last item.
8182 // - You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
8183 // - This is essentially the same as calling OpenPopupOnItemClick() + BeginPopup() but written to avoid
8184 // computing the ID twice because BeginPopupContextXXX functions may be called very frequently.
BeginPopupContextItem(const char * str_id,ImGuiPopupFlags popup_flags)8185 bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
8186 {
8187 ImGuiWindow* window = GImGui->CurrentWindow;
8188 if (window->SkipItems)
8189 return false;
8190 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!
8191 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
8192 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8193 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8194 OpenPopupEx(id, popup_flags);
8195 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8196 }
8197
BeginPopupContextWindow(const char * str_id,ImGuiPopupFlags popup_flags)8198 bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
8199 {
8200 ImGuiWindow* window = GImGui->CurrentWindow;
8201 if (!str_id)
8202 str_id = "window_context";
8203 ImGuiID id = window->GetID(str_id);
8204 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8205 if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8206 if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
8207 OpenPopupEx(id, popup_flags);
8208 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8209 }
8210
BeginPopupContextVoid(const char * str_id,ImGuiPopupFlags popup_flags)8211 bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
8212 {
8213 ImGuiWindow* window = GImGui->CurrentWindow;
8214 if (!str_id)
8215 str_id = "void_context";
8216 ImGuiID id = window->GetID(str_id);
8217 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8218 if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
8219 if (GetTopMostPopupModal() == NULL)
8220 OpenPopupEx(id, popup_flags);
8221 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8222 }
8223
8224 // 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.)
8225 // 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)8226 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
8227 {
8228 ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
8229 //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
8230 //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
8231
8232 // Combo Box policy (we want a connecting edge)
8233 if (policy == ImGuiPopupPositionPolicy_ComboBox)
8234 {
8235 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
8236 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
8237 {
8238 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
8239 if (n != -1 && dir == *last_dir) // Already tried this direction?
8240 continue;
8241 ImVec2 pos;
8242 if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
8243 if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
8244 if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
8245 if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
8246 if (!r_outer.Contains(ImRect(pos, pos + size)))
8247 continue;
8248 *last_dir = dir;
8249 return pos;
8250 }
8251 }
8252
8253 // Tooltip and Default popup policy
8254 // (Always first try the direction we used on the last frame, if any)
8255 if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default)
8256 {
8257 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
8258 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
8259 {
8260 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
8261 if (n != -1 && dir == *last_dir) // Already tried this direction?
8262 continue;
8263
8264 const 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);
8265 const 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);
8266
8267 // If there not enough room on one axis, there's no point in positioning on a side on this axis (e.g. when not enough width, use a top/bottom position to maximize available width)
8268 if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right))
8269 continue;
8270 if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down))
8271 continue;
8272
8273 ImVec2 pos;
8274 pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
8275 pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
8276
8277 // Clamp top-left corner of popup
8278 pos.x = ImMax(pos.x, r_outer.Min.x);
8279 pos.y = ImMax(pos.y, r_outer.Min.y);
8280
8281 *last_dir = dir;
8282 return pos;
8283 }
8284 }
8285
8286 // Fallback when not enough room:
8287 *last_dir = ImGuiDir_None;
8288
8289 // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
8290 if (policy == ImGuiPopupPositionPolicy_Tooltip)
8291 return ref_pos + ImVec2(2, 2);
8292
8293 // Otherwise try to keep within display
8294 ImVec2 pos = ref_pos;
8295 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
8296 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
8297 return pos;
8298 }
8299
GetWindowAllowedExtentRect(ImGuiWindow * window)8300 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window)
8301 {
8302 IM_UNUSED(window);
8303 ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
8304 ImRect r_screen = GetViewportRect();
8305 r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
8306 return r_screen;
8307 }
8308
FindBestWindowPosForPopup(ImGuiWindow * window)8309 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
8310 {
8311 ImGuiContext& g = *GImGui;
8312
8313 ImRect r_outer = GetWindowAllowedExtentRect(window);
8314 if (window->Flags & ImGuiWindowFlags_ChildMenu)
8315 {
8316 // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
8317 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
8318 IM_ASSERT(g.CurrentWindow == window);
8319 ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
8320 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).
8321 ImRect r_avoid;
8322 if (parent_window->DC.MenuBarAppending)
8323 r_avoid = ImRect(-FLT_MAX, parent_window->ClipRect.Min.y, FLT_MAX, parent_window->ClipRect.Max.y); // Avoid parent menu-bar. If we wanted multi-line menu-bar, we may instead want to have the calling window setup e.g. a NextWindowData.PosConstraintAvoidRect field
8324 else
8325 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);
8326 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
8327 }
8328 if (window->Flags & ImGuiWindowFlags_Popup)
8329 {
8330 ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
8331 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
8332 }
8333 if (window->Flags & ImGuiWindowFlags_Tooltip)
8334 {
8335 // Position tooltip (always follows mouse)
8336 float sc = g.Style.MouseCursorScale;
8337 ImVec2 ref_pos = NavCalcPreferredRefPos();
8338 ImRect r_avoid;
8339 if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
8340 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
8341 else
8342 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.
8343 return FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
8344 }
8345 IM_ASSERT(0);
8346 return window->Pos;
8347 }
8348
8349 //-----------------------------------------------------------------------------
8350 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
8351 //-----------------------------------------------------------------------------
8352
8353 // FIXME-NAV: The existence of SetNavID vs SetNavIDWithRectRel vs SetFocusID is incredibly messy and confusing,
8354 // and needs some explanation or serious refactoring.
SetNavID(ImGuiID id,int nav_layer,ImGuiID focus_scope_id)8355 void ImGui::SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id)
8356 {
8357 ImGuiContext& g = *GImGui;
8358 IM_ASSERT(g.NavWindow);
8359 IM_ASSERT(nav_layer == 0 || nav_layer == 1);
8360 g.NavId = id;
8361 g.NavFocusScopeId = focus_scope_id;
8362 g.NavWindow->NavLastIds[nav_layer] = id;
8363 }
8364
SetNavIDWithRectRel(ImGuiID id,int nav_layer,ImGuiID focus_scope_id,const ImRect & rect_rel)8365 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
8366 {
8367 ImGuiContext& g = *GImGui;
8368 SetNavID(id, nav_layer, focus_scope_id);
8369 g.NavWindow->NavRectRel[nav_layer] = rect_rel;
8370 g.NavMousePosDirty = true;
8371 g.NavDisableHighlight = false;
8372 g.NavDisableMouseHover = true;
8373 }
8374
SetFocusID(ImGuiID id,ImGuiWindow * window)8375 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
8376 {
8377 ImGuiContext& g = *GImGui;
8378 IM_ASSERT(id != 0);
8379
8380 // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and window->DC.NavFocusScopeIdCurrent are valid.
8381 // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
8382 const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
8383 if (g.NavWindow != window)
8384 g.NavInitRequest = false;
8385 g.NavWindow = window;
8386 g.NavId = id;
8387 g.NavLayer = nav_layer;
8388 g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
8389 window->NavLastIds[nav_layer] = id;
8390 if (window->DC.LastItemId == id)
8391 window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
8392
8393 if (g.ActiveIdSource == ImGuiInputSource_Nav)
8394 g.NavDisableMouseHover = true;
8395 else
8396 g.NavDisableHighlight = true;
8397 }
8398
ImGetDirQuadrantFromDelta(float dx,float dy)8399 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
8400 {
8401 if (ImFabs(dx) > ImFabs(dy))
8402 return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
8403 return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
8404 }
8405
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)8406 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
8407 {
8408 if (a1 < b0)
8409 return a1 - b0;
8410 if (b1 < a0)
8411 return a0 - b1;
8412 return 0.0f;
8413 }
8414
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)8415 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
8416 {
8417 if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
8418 {
8419 r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
8420 r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
8421 }
8422 else
8423 {
8424 r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
8425 r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
8426 }
8427 }
8428
8429 // Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)8430 static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
8431 {
8432 ImGuiContext& g = *GImGui;
8433 ImGuiWindow* window = g.CurrentWindow;
8434 if (g.NavLayer != window->DC.NavLayerCurrent)
8435 return false;
8436
8437 const ImRect& curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
8438 g.NavScoringCount++;
8439
8440 // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
8441 if (window->ParentWindow == g.NavWindow)
8442 {
8443 IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
8444 if (!window->ClipRect.Overlaps(cand))
8445 return false;
8446 cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
8447 }
8448
8449 // 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)
8450 // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
8451 NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
8452
8453 // Compute distance between boxes
8454 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
8455 float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
8456 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
8457 if (dby != 0.0f && dbx != 0.0f)
8458 dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
8459 float dist_box = ImFabs(dbx) + ImFabs(dby);
8460
8461 // 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)
8462 float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
8463 float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
8464 float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
8465
8466 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
8467 ImGuiDir quadrant;
8468 float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
8469 if (dbx != 0.0f || dby != 0.0f)
8470 {
8471 // For non-overlapping boxes, use distance between boxes
8472 dax = dbx;
8473 day = dby;
8474 dist_axial = dist_box;
8475 quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
8476 }
8477 else if (dcx != 0.0f || dcy != 0.0f)
8478 {
8479 // For overlapping boxes with different centers, use distance between centers
8480 dax = dcx;
8481 day = dcy;
8482 dist_axial = dist_center;
8483 quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
8484 }
8485 else
8486 {
8487 // 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)
8488 quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
8489 }
8490
8491 #if IMGUI_DEBUG_NAV_SCORING
8492 char buf[128];
8493 if (IsMouseHoveringRect(cand.Min, cand.Max))
8494 {
8495 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]);
8496 ImDrawList* draw_list = GetForegroundDrawList(window);
8497 draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
8498 draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
8499 draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150));
8500 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
8501 }
8502 else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
8503 {
8504 if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
8505 if (quadrant == g.NavMoveDir)
8506 {
8507 ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
8508 ImDrawList* draw_list = GetForegroundDrawList(window);
8509 draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
8510 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
8511 }
8512 }
8513 #endif
8514
8515 // Is it in the quadrant we're interesting in moving to?
8516 bool new_best = false;
8517 if (quadrant == g.NavMoveDir)
8518 {
8519 // Does it beat the current best candidate?
8520 if (dist_box < result->DistBox)
8521 {
8522 result->DistBox = dist_box;
8523 result->DistCenter = dist_center;
8524 return true;
8525 }
8526 if (dist_box == result->DistBox)
8527 {
8528 // Try using distance between center points to break ties
8529 if (dist_center < result->DistCenter)
8530 {
8531 result->DistCenter = dist_center;
8532 new_best = true;
8533 }
8534 else if (dist_center == result->DistCenter)
8535 {
8536 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
8537 // (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),
8538 // 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.
8539 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
8540 new_best = true;
8541 }
8542 }
8543 }
8544
8545 // 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
8546 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
8547 // 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.
8548 // 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.
8549 // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
8550 if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
8551 if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
8552 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))
8553 {
8554 result->DistAxial = dist_axial;
8555 new_best = true;
8556 }
8557
8558 return new_best;
8559 }
8560
NavApplyItemToResult(ImGuiNavMoveResult * result,ImGuiWindow * window,ImGuiID id,const ImRect & nav_bb_rel)8561 static void ImGui::NavApplyItemToResult(ImGuiNavMoveResult* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel)
8562 {
8563 result->Window = window;
8564 result->ID = id;
8565 result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
8566 result->RectRel = nav_bb_rel;
8567 }
8568
8569 // 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)8570 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
8571 {
8572 ImGuiContext& g = *GImGui;
8573 //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.
8574 // return;
8575
8576 const ImGuiItemFlags item_flags = window->DC.ItemFlags;
8577 const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
8578
8579 // Process Init Request
8580 if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
8581 {
8582 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
8583 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
8584 {
8585 g.NavInitResultId = id;
8586 g.NavInitResultRectRel = nav_bb_rel;
8587 }
8588 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
8589 {
8590 g.NavInitRequest = false; // Found a match, clear request
8591 NavUpdateAnyRequestFlag();
8592 }
8593 }
8594
8595 // Process Move Request (scoring for navigation)
8596 // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
8597 if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav)))
8598 {
8599 ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
8600 #if IMGUI_DEBUG_NAV_SCORING
8601 // [DEBUG] Score all items in NavWindow at all times
8602 if (!g.NavMoveRequest)
8603 g.NavMoveDir = g.NavMoveDirLast;
8604 bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
8605 #else
8606 bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
8607 #endif
8608 if (new_best)
8609 NavApplyItemToResult(result, window, id, nav_bb_rel);
8610
8611 // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
8612 const float VISIBLE_RATIO = 0.70f;
8613 if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
8614 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)
8615 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
8616 NavApplyItemToResult(&g.NavMoveResultLocalVisibleSet, window, id, nav_bb_rel);
8617 }
8618
8619 // Update window-relative bounding box of navigated item
8620 if (g.NavId == id)
8621 {
8622 g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
8623 g.NavLayer = window->DC.NavLayerCurrent;
8624 g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
8625 g.NavIdIsAlive = true;
8626 g.NavIdTabCounter = window->DC.FocusCounterTabStop;
8627 window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position)
8628 }
8629 }
8630
NavMoveRequestButNoResultYet()8631 bool ImGui::NavMoveRequestButNoResultYet()
8632 {
8633 ImGuiContext& g = *GImGui;
8634 return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
8635 }
8636
NavMoveRequestCancel()8637 void ImGui::NavMoveRequestCancel()
8638 {
8639 ImGuiContext& g = *GImGui;
8640 g.NavMoveRequest = false;
8641 NavUpdateAnyRequestFlag();
8642 }
8643
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)8644 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
8645 {
8646 ImGuiContext& g = *GImGui;
8647 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
8648 NavMoveRequestCancel();
8649 g.NavMoveDir = move_dir;
8650 g.NavMoveClipDir = clip_dir;
8651 g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
8652 g.NavMoveRequestFlags = move_flags;
8653 g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
8654 }
8655
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)8656 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
8657 {
8658 ImGuiContext& g = *GImGui;
8659
8660 // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
8661 // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
8662 g.NavWrapRequestWindow = window;
8663 g.NavWrapRequestFlags = move_flags;
8664 }
8665
8666 // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
8667 // This way we could find the last focused window among our children. It would be much less confusing this way?
NavSaveLastChildNavWindowIntoParent(ImGuiWindow * nav_window)8668 static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
8669 {
8670 ImGuiWindow* parent = nav_window;
8671 while (parent && (parent->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8672 parent = parent->ParentWindow;
8673 if (parent && parent != nav_window)
8674 parent->NavLastChildNavWindow = nav_window;
8675 }
8676
8677 // Restore the last focused child.
8678 // Call when we are expected to land on the Main Layer (0) after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)8679 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
8680 {
8681 if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)
8682 return window->NavLastChildNavWindow;
8683 return window;
8684 }
8685
NavRestoreLayer(ImGuiNavLayer layer)8686 static void NavRestoreLayer(ImGuiNavLayer layer)
8687 {
8688 ImGuiContext& g = *GImGui;
8689 g.NavLayer = layer;
8690 if (layer == 0)
8691 g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
8692 ImGuiWindow* window = g.NavWindow;
8693 if (layer == 0 && window->NavLastIds[0] != 0)
8694 ImGui::SetNavIDWithRectRel(window->NavLastIds[0], layer, 0, window->NavRectRel[0]);
8695 else
8696 ImGui::NavInitWindow(window, true);
8697 }
8698
NavUpdateAnyRequestFlag()8699 static inline void ImGui::NavUpdateAnyRequestFlag()
8700 {
8701 ImGuiContext& g = *GImGui;
8702 g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
8703 if (g.NavAnyRequest)
8704 IM_ASSERT(g.NavWindow != NULL);
8705 }
8706
8707 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)8708 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
8709 {
8710 ImGuiContext& g = *GImGui;
8711 IM_ASSERT(window == g.NavWindow);
8712 bool init_for_nav = false;
8713 if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
8714 if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
8715 init_for_nav = true;
8716 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
8717 if (init_for_nav)
8718 {
8719 SetNavID(0, g.NavLayer, 0);
8720 g.NavInitRequest = true;
8721 g.NavInitRequestFromMove = false;
8722 g.NavInitResultId = 0;
8723 g.NavInitResultRectRel = ImRect();
8724 NavUpdateAnyRequestFlag();
8725 }
8726 else
8727 {
8728 g.NavId = window->NavLastIds[0];
8729 g.NavFocusScopeId = 0;
8730 }
8731 }
8732
NavCalcPreferredRefPos()8733 static ImVec2 ImGui::NavCalcPreferredRefPos()
8734 {
8735 ImGuiContext& g = *GImGui;
8736 if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
8737 {
8738 // Mouse (we need a fallback in case the mouse becomes invalid after being used)
8739 if (IsMousePosValid(&g.IO.MousePos))
8740 return g.IO.MousePos;
8741 return g.LastValidMousePos;
8742 }
8743 else
8744 {
8745 // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
8746 const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
8747 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()));
8748 ImRect visible_rect = GetViewportRect();
8749 return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.
8750 }
8751 }
8752
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)8753 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
8754 {
8755 ImGuiContext& g = *GImGui;
8756 if (mode == ImGuiInputReadMode_Down)
8757 return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user)
8758
8759 const float t = g.IO.NavInputsDownDuration[n];
8760 if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input.
8761 return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
8762 if (t < 0.0f)
8763 return 0.0f;
8764 if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input.
8765 return (t == 0.0f) ? 1.0f : 0.0f;
8766 if (mode == ImGuiInputReadMode_Repeat)
8767 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f);
8768 if (mode == ImGuiInputReadMode_RepeatSlow)
8769 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f);
8770 if (mode == ImGuiInputReadMode_RepeatFast)
8771 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f);
8772 return 0.0f;
8773 }
8774
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)8775 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
8776 {
8777 ImVec2 delta(0.0f, 0.0f);
8778 if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
8779 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode));
8780 if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
8781 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode));
8782 if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
8783 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
8784 if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
8785 delta *= slow_factor;
8786 if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
8787 delta *= fast_factor;
8788 return delta;
8789 }
8790
NavUpdate()8791 static void ImGui::NavUpdate()
8792 {
8793 ImGuiContext& g = *GImGui;
8794 ImGuiIO& io = g.IO;
8795
8796 io.WantSetMousePos = false;
8797 g.NavWrapRequestWindow = NULL;
8798 g.NavWrapRequestFlags = ImGuiNavMoveFlags_None;
8799 #if 0
8800 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);
8801 #endif
8802
8803 // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard)
8804 // (do it before we map Keyboard input!)
8805 bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
8806 bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
8807 if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_NavGamepad)
8808 {
8809 if (io.NavInputs[ImGuiNavInput_Activate] > 0.0f || io.NavInputs[ImGuiNavInput_Input] > 0.0f || io.NavInputs[ImGuiNavInput_Cancel] > 0.0f || io.NavInputs[ImGuiNavInput_Menu] > 0.0f
8810 || io.NavInputs[ImGuiNavInput_DpadLeft] > 0.0f || io.NavInputs[ImGuiNavInput_DpadRight] > 0.0f || io.NavInputs[ImGuiNavInput_DpadUp] > 0.0f || io.NavInputs[ImGuiNavInput_DpadDown] > 0.0f)
8811 g.NavInputSource = ImGuiInputSource_NavGamepad;
8812 }
8813
8814 // Update Keyboard->Nav inputs mapping
8815 if (nav_keyboard_active)
8816 {
8817 #define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(io.KeyMap[_KEY])) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } } while (0)
8818 NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate );
8819 NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input );
8820 NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel );
8821 NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
8822 NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
8823 NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ );
8824 NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
8825 if (io.KeyCtrl)
8826 io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
8827 if (io.KeyShift)
8828 io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
8829 if (io.KeyAlt && !io.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu.
8830 io.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f;
8831 #undef NAV_MAP_KEY
8832 }
8833 memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration));
8834 for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++)
8835 io.NavInputsDownDuration[i] = (io.NavInputs[i] > 0.0f) ? (io.NavInputsDownDuration[i] < 0.0f ? 0.0f : io.NavInputsDownDuration[i] + io.DeltaTime) : -1.0f;
8836
8837 // Process navigation init request (select first/default focus)
8838 if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove))
8839 NavUpdateInitResult();
8840 g.NavInitRequest = false;
8841 g.NavInitRequestFromMove = false;
8842 g.NavInitResultId = 0;
8843 g.NavJustMovedToId = 0;
8844
8845 // Process navigation move request
8846 if (g.NavMoveRequest)
8847 NavUpdateMoveResult();
8848
8849 // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
8850 if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
8851 {
8852 IM_ASSERT(g.NavMoveRequest);
8853 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
8854 g.NavDisableHighlight = false;
8855 g.NavMoveRequestForward = ImGuiNavForward_None;
8856 }
8857
8858 // Apply application mouse position movement, after we had a chance to process move request result.
8859 if (g.NavMousePosDirty && g.NavIdIsAlive)
8860 {
8861 // Set mouse position given our knowledge of the navigated item position from last frame
8862 if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
8863 {
8864 if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
8865 {
8866 io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos();
8867 io.WantSetMousePos = true;
8868 }
8869 }
8870 g.NavMousePosDirty = false;
8871 }
8872 g.NavIdIsAlive = false;
8873 g.NavJustTabbedId = 0;
8874 IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
8875
8876 // 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
8877 if (g.NavWindow)
8878 NavSaveLastChildNavWindowIntoParent(g.NavWindow);
8879 if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
8880 g.NavWindow->NavLastChildNavWindow = NULL;
8881
8882 // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
8883 NavUpdateWindowing();
8884
8885 // Set output flags for user application
8886 io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
8887 io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
8888
8889 // Process NavCancel input (to close a popup, get back to parent, clear focus)
8890 if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
8891 {
8892 IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n");
8893 if (g.ActiveId != 0)
8894 {
8895 if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
8896 ClearActiveID();
8897 }
8898 else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
8899 {
8900 // Exit child window
8901 ImGuiWindow* child_window = g.NavWindow;
8902 ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
8903 IM_ASSERT(child_window->ChildId != 0);
8904 FocusWindow(parent_window);
8905 SetNavID(child_window->ChildId, 0, 0);
8906 // Reassigning with same value, we're being explicit here.
8907 g.NavIdIsAlive = false; // -V1048
8908 if (g.NavDisableMouseHover)
8909 g.NavMousePosDirty = true;
8910 }
8911 else if (g.OpenPopupStack.Size > 0)
8912 {
8913 // Close open popup/menu
8914 if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
8915 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
8916 }
8917 else if (g.NavLayer != ImGuiNavLayer_Main)
8918 {
8919 // Leave the "menu" layer
8920 NavRestoreLayer(ImGuiNavLayer_Main);
8921 }
8922 else
8923 {
8924 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
8925 if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
8926 g.NavWindow->NavLastIds[0] = 0;
8927 g.NavId = g.NavFocusScopeId = 0;
8928 }
8929 }
8930
8931 // Process manual activation request
8932 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
8933 if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8934 {
8935 bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
8936 bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
8937 if (g.ActiveId == 0 && activate_pressed)
8938 g.NavActivateId = g.NavId;
8939 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
8940 g.NavActivateDownId = g.NavId;
8941 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
8942 g.NavActivatePressedId = g.NavId;
8943 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
8944 g.NavInputId = g.NavId;
8945 }
8946 if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8947 g.NavDisableHighlight = true;
8948 if (g.NavActivateId != 0)
8949 IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
8950 g.NavMoveRequest = false;
8951
8952 // Process programmatic activation request
8953 if (g.NavNextActivateId != 0)
8954 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
8955 g.NavNextActivateId = 0;
8956
8957 // Initiate directional inputs request
8958 if (g.NavMoveRequestForward == ImGuiNavForward_None)
8959 {
8960 g.NavMoveDir = ImGuiDir_None;
8961 g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
8962 if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8963 {
8964 const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
8965 if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
8966 if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
8967 if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
8968 if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
8969 }
8970 g.NavMoveClipDir = g.NavMoveDir;
8971 }
8972 else
8973 {
8974 // 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)
8975 // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
8976 IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
8977 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
8978 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
8979 g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
8980 }
8981
8982 // Update PageUp/PageDown/Home/End scroll
8983 // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
8984 float nav_scoring_rect_offset_y = 0.0f;
8985 if (nav_keyboard_active)
8986 nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
8987
8988 // 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
8989 if (g.NavMoveDir != ImGuiDir_None)
8990 {
8991 g.NavMoveRequest = true;
8992 g.NavMoveRequestKeyMods = io.KeyMods;
8993 g.NavMoveDirLast = g.NavMoveDir;
8994 }
8995 if (g.NavMoveRequest && g.NavId == 0)
8996 {
8997 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
8998 g.NavInitRequest = g.NavInitRequestFromMove = true;
8999 // Reassigning with same value, we're being explicit here.
9000 g.NavInitResultId = 0; // -V1048
9001 g.NavDisableHighlight = false;
9002 }
9003 NavUpdateAnyRequestFlag();
9004
9005 // Scrolling
9006 if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
9007 {
9008 // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
9009 ImGuiWindow* window = g.NavWindow;
9010 const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
9011 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
9012 {
9013 if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
9014 SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
9015 if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
9016 SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
9017 }
9018
9019 // *Normal* Manual scroll with NavScrollXXX keys
9020 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
9021 ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f / 10.0f, 10.0f);
9022 if (scroll_dir.x != 0.0f && window->ScrollbarX)
9023 SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
9024 if (scroll_dir.y != 0.0f)
9025 SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
9026 }
9027
9028 // Reset search results
9029 g.NavMoveResultLocal.Clear();
9030 g.NavMoveResultLocalVisibleSet.Clear();
9031 g.NavMoveResultOther.Clear();
9032
9033 // When using gamepad, we project the reference nav bounding box into window visible area.
9034 // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative
9035 // (can't focus a visible object like we can with the mouse).
9036 if (g.NavMoveRequest && g.NavInputSource == ImGuiInputSource_NavGamepad && g.NavLayer == ImGuiNavLayer_Main)
9037 {
9038 ImGuiWindow* window = g.NavWindow;
9039 ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1));
9040 if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
9041 {
9042 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n");
9043 float pad = window->CalcFontSize() * 0.5f;
9044 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
9045 window->NavRectRel[g.NavLayer].ClipWithFull(window_rect_rel);
9046 g.NavId = g.NavFocusScopeId = 0;
9047 }
9048 }
9049
9050 // 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)
9051 ImRect nav_rect_rel = g.NavWindow ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
9052 g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
9053 g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y);
9054 g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x);
9055 g.NavScoringRect.Max.x = g.NavScoringRect.Min.x;
9056 IM_ASSERT(!g.NavScoringRect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
9057 //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
9058 g.NavScoringCount = 0;
9059 #if IMGUI_DEBUG_NAV_RECTS
9060 if (g.NavWindow)
9061 {
9062 ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow);
9063 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]
9064 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); }
9065 }
9066 #endif
9067 }
9068
NavUpdateInitResult()9069 static void ImGui::NavUpdateInitResult()
9070 {
9071 // 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)
9072 ImGuiContext& g = *GImGui;
9073 if (!g.NavWindow)
9074 return;
9075
9076 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
9077 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name);
9078 if (g.NavInitRequestFromMove)
9079 SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel);
9080 else
9081 SetNavID(g.NavInitResultId, g.NavLayer, 0);
9082 g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
9083 }
9084
9085 // Apply result from previous frame navigation directional move request
NavUpdateMoveResult()9086 static void ImGui::NavUpdateMoveResult()
9087 {
9088 ImGuiContext& g = *GImGui;
9089 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
9090 {
9091 // 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)
9092 if (g.NavId != 0)
9093 {
9094 g.NavDisableHighlight = false;
9095 g.NavDisableMouseHover = true;
9096 }
9097 return;
9098 }
9099
9100 // Select which result to use
9101 ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
9102
9103 // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
9104 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
9105 if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
9106 result = &g.NavMoveResultLocalVisibleSet;
9107
9108 // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
9109 if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
9110 if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
9111 result = &g.NavMoveResultOther;
9112 IM_ASSERT(g.NavWindow && result->Window);
9113
9114 // Scroll to keep newly navigated item fully into view.
9115 if (g.NavLayer == ImGuiNavLayer_Main)
9116 {
9117 ImVec2 delta_scroll;
9118 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge)
9119 {
9120 float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
9121 delta_scroll.y = result->Window->Scroll.y - scroll_target;
9122 SetScrollY(result->Window, scroll_target);
9123 }
9124 else
9125 {
9126 ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
9127 delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs);
9128 }
9129
9130 // Offset our result position so mouse position can be applied immediately after in NavUpdate()
9131 result->RectRel.TranslateX(-delta_scroll.x);
9132 result->RectRel.TranslateY(-delta_scroll.y);
9133 }
9134
9135 ClearActiveID();
9136 g.NavWindow = result->Window;
9137 if (g.NavId != result->ID)
9138 {
9139 // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
9140 g.NavJustMovedToId = result->ID;
9141 g.NavJustMovedToFocusScopeId = result->FocusScopeId;
9142 g.NavJustMovedToKeyMods = g.NavMoveRequestKeyMods;
9143 }
9144 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
9145 SetNavIDWithRectRel(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
9146 }
9147
9148 // Handle PageUp/PageDown/Home/End keys
NavUpdatePageUpPageDown()9149 static float ImGui::NavUpdatePageUpPageDown()
9150 {
9151 ImGuiContext& g = *GImGui;
9152 ImGuiIO& io = g.IO;
9153
9154 if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL)
9155 return 0.0f;
9156 if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main)
9157 return 0.0f;
9158
9159 ImGuiWindow* window = g.NavWindow;
9160 const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
9161 const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
9162 const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
9163 const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
9164 if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed
9165 {
9166 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
9167 {
9168 // Fallback manual-scroll when window has no navigable item
9169 if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
9170 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
9171 else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
9172 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
9173 else if (home_pressed)
9174 SetScrollY(window, 0.0f);
9175 else if (end_pressed)
9176 SetScrollY(window, window->ScrollMax.y);
9177 }
9178 else
9179 {
9180 ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
9181 const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
9182 float nav_scoring_rect_offset_y = 0.0f;
9183 if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
9184 {
9185 nav_scoring_rect_offset_y = -page_offset_y;
9186 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)
9187 g.NavMoveClipDir = ImGuiDir_Up;
9188 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
9189 }
9190 else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
9191 {
9192 nav_scoring_rect_offset_y = +page_offset_y;
9193 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)
9194 g.NavMoveClipDir = ImGuiDir_Down;
9195 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
9196 }
9197 else if (home_pressed)
9198 {
9199 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
9200 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
9201 // Preserve current horizontal position if we have any.
9202 nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
9203 if (nav_rect_rel.IsInverted())
9204 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
9205 g.NavMoveDir = ImGuiDir_Down;
9206 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
9207 }
9208 else if (end_pressed)
9209 {
9210 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
9211 if (nav_rect_rel.IsInverted())
9212 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
9213 g.NavMoveDir = ImGuiDir_Up;
9214 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
9215 }
9216 return nav_scoring_rect_offset_y;
9217 }
9218 }
9219 return 0.0f;
9220 }
9221
NavEndFrame()9222 static void ImGui::NavEndFrame()
9223 {
9224 ImGuiContext& g = *GImGui;
9225
9226 // Show CTRL+TAB list window
9227 if (g.NavWindowingTarget != NULL)
9228 NavUpdateWindowingOverlay();
9229
9230 // Perform wrap-around in menus
9231 ImGuiWindow* window = g.NavWrapRequestWindow;
9232 ImGuiNavMoveFlags move_flags = g.NavWrapRequestFlags;
9233 if (window != NULL && g.NavWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == ImGuiNavLayer_Main)
9234 {
9235 IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
9236 ImRect bb_rel = window->NavRectRel[0];
9237
9238 ImGuiDir clip_dir = g.NavMoveDir;
9239 if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
9240 {
9241 bb_rel.Min.x = bb_rel.Max.x =
9242 ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x;
9243 if (move_flags & ImGuiNavMoveFlags_WrapX)
9244 {
9245 bb_rel.TranslateY(-bb_rel.GetHeight());
9246 clip_dir = ImGuiDir_Up;
9247 }
9248 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9249 }
9250 if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
9251 {
9252 bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
9253 if (move_flags & ImGuiNavMoveFlags_WrapX)
9254 {
9255 bb_rel.TranslateY(+bb_rel.GetHeight());
9256 clip_dir = ImGuiDir_Down;
9257 }
9258 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9259 }
9260 if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
9261 {
9262 bb_rel.Min.y = bb_rel.Max.y =
9263 ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
9264 if (move_flags & ImGuiNavMoveFlags_WrapY)
9265 {
9266 bb_rel.TranslateX(-bb_rel.GetWidth());
9267 clip_dir = ImGuiDir_Left;
9268 }
9269 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9270 }
9271 if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
9272 {
9273 bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
9274 if (move_flags & ImGuiNavMoveFlags_WrapY)
9275 {
9276 bb_rel.TranslateX(+bb_rel.GetWidth());
9277 clip_dir = ImGuiDir_Right;
9278 }
9279 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9280 }
9281 }
9282 }
9283
FindWindowFocusIndex(ImGuiWindow * window)9284 static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
9285 {
9286 ImGuiContext& g = *GImGui;
9287 for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--)
9288 if (g.WindowsFocusOrder[i] == window)
9289 return i;
9290 return -1;
9291 }
9292
FindWindowNavFocusable(int i_start,int i_stop,int dir)9293 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
9294 {
9295 ImGuiContext& g = *GImGui;
9296 for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
9297 if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
9298 return g.WindowsFocusOrder[i];
9299 return NULL;
9300 }
9301
NavUpdateWindowingHighlightWindow(int focus_change_dir)9302 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
9303 {
9304 ImGuiContext& g = *GImGui;
9305 IM_ASSERT(g.NavWindowingTarget);
9306 if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
9307 return;
9308
9309 const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
9310 ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
9311 if (!window_target)
9312 window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
9313 if (window_target) // Don't reset windowing target if there's a single window in the list
9314 g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
9315 g.NavWindowingToggleLayer = false;
9316 }
9317
9318 // Windowing management mode
9319 // Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
9320 // Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
NavUpdateWindowing()9321 static void ImGui::NavUpdateWindowing()
9322 {
9323 ImGuiContext& g = *GImGui;
9324 ImGuiWindow* apply_focus_window = NULL;
9325 bool apply_toggle_layer = false;
9326
9327 ImGuiWindow* modal_window = GetTopMostPopupModal();
9328 bool allow_windowing = (modal_window == NULL);
9329 if (!allow_windowing)
9330 g.NavWindowingTarget = NULL;
9331
9332 // Fade out
9333 if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
9334 {
9335 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
9336 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
9337 g.NavWindowingTargetAnim = NULL;
9338 }
9339
9340 // Start CTRL-TAB or Square+L/R window selection
9341 bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
9342 bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
9343 if (start_windowing_with_gamepad || start_windowing_with_keyboard)
9344 if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
9345 {
9346 g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // FIXME-DOCK: Will need to use RootWindowDockStop
9347 g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
9348 g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
9349 g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
9350 }
9351
9352 // Gamepad update
9353 g.NavWindowingTimer += g.IO.DeltaTime;
9354 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
9355 {
9356 // 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
9357 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
9358
9359 // Select window to focus
9360 const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
9361 if (focus_change_dir != 0)
9362 {
9363 NavUpdateWindowingHighlightWindow(focus_change_dir);
9364 g.NavWindowingHighlightAlpha = 1.0f;
9365 }
9366
9367 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
9368 if (!IsNavInputDown(ImGuiNavInput_Menu))
9369 {
9370 g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
9371 if (g.NavWindowingToggleLayer && g.NavWindow)
9372 apply_toggle_layer = true;
9373 else if (!g.NavWindowingToggleLayer)
9374 apply_focus_window = g.NavWindowingTarget;
9375 g.NavWindowingTarget = NULL;
9376 }
9377 }
9378
9379 // Keyboard: Focus
9380 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
9381 {
9382 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
9383 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
9384 if (IsKeyPressedMap(ImGuiKey_Tab, true))
9385 NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
9386 if (!g.IO.KeyCtrl)
9387 apply_focus_window = g.NavWindowingTarget;
9388 }
9389
9390 // Keyboard: Press and Release ALT to toggle menu layer
9391 // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of backend clearing releases all keys on ALT-TAB
9392 if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed))
9393 g.NavWindowingToggleLayer = true;
9394 if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
9395 if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
9396 apply_toggle_layer = true;
9397
9398 // Move window
9399 if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
9400 {
9401 ImVec2 move_delta;
9402 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
9403 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
9404 if (g.NavInputSource == ImGuiInputSource_NavGamepad)
9405 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
9406 if (move_delta.x != 0.0f || move_delta.y != 0.0f)
9407 {
9408 const float NAV_MOVE_SPEED = 800.0f;
9409 const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well
9410 ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow;
9411 SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always);
9412 MarkIniSettingsDirty(moving_window);
9413 g.NavDisableMouseHover = true;
9414 }
9415 }
9416
9417 // Apply final focus
9418 if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
9419 {
9420 ClearActiveID();
9421 g.NavDisableHighlight = false;
9422 g.NavDisableMouseHover = true;
9423 apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
9424 ClosePopupsOverWindow(apply_focus_window, false);
9425 FocusWindow(apply_focus_window);
9426 if (apply_focus_window->NavLastIds[0] == 0)
9427 NavInitWindow(apply_focus_window, false);
9428
9429 // If the window only has a menu layer, select it directly
9430 if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu))
9431 g.NavLayer = ImGuiNavLayer_Menu;
9432 }
9433 if (apply_focus_window)
9434 g.NavWindowingTarget = NULL;
9435
9436 // Apply menu/layer toggle
9437 if (apply_toggle_layer && g.NavWindow)
9438 {
9439 // Move to parent menu if necessary
9440 ImGuiWindow* new_nav_window = g.NavWindow;
9441 while (new_nav_window->ParentWindow
9442 && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
9443 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
9444 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
9445 new_nav_window = new_nav_window->ParentWindow;
9446 if (new_nav_window != g.NavWindow)
9447 {
9448 ImGuiWindow* old_nav_window = g.NavWindow;
9449 FocusWindow(new_nav_window);
9450 new_nav_window->NavLastChildNavWindow = old_nav_window;
9451 }
9452 g.NavDisableHighlight = false;
9453 g.NavDisableMouseHover = true;
9454
9455 // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID.
9456 const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
9457 NavRestoreLayer(new_nav_layer);
9458 }
9459 }
9460
9461 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)9462 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
9463 {
9464 if (window->Flags & ImGuiWindowFlags_Popup)
9465 return "(Popup)";
9466 if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
9467 return "(Main menu bar)";
9468 return "(Untitled)";
9469 }
9470
9471 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingOverlay()9472 void ImGui::NavUpdateWindowingOverlay()
9473 {
9474 ImGuiContext& g = *GImGui;
9475 IM_ASSERT(g.NavWindowingTarget != NULL);
9476
9477 if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
9478 return;
9479
9480 if (g.NavWindowingListWindow == NULL)
9481 g.NavWindowingListWindow = FindWindowByName("###NavWindowingList");
9482 SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
9483 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
9484 PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
9485 Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
9486 for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
9487 {
9488 ImGuiWindow* window = g.WindowsFocusOrder[n];
9489 if (!IsWindowNavFocusable(window))
9490 continue;
9491 const char* label = window->Name;
9492 if (label == FindRenderedTextEnd(label))
9493 label = GetFallbackWindowNameForWindowingList(window);
9494 Selectable(label, g.NavWindowingTarget == window);
9495 }
9496 End();
9497 PopStyleVar();
9498 }
9499
9500
9501 //-----------------------------------------------------------------------------
9502 // [SECTION] DRAG AND DROP
9503 //-----------------------------------------------------------------------------
9504
ClearDragDrop()9505 void ImGui::ClearDragDrop()
9506 {
9507 ImGuiContext& g = *GImGui;
9508 g.DragDropActive = false;
9509 g.DragDropPayload.Clear();
9510 g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
9511 g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
9512 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
9513 g.DragDropAcceptFrameCount = -1;
9514
9515 g.DragDropPayloadBufHeap.clear();
9516 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
9517 }
9518
9519 // Call when current ID is active.
9520 // 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)9521 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
9522 {
9523 ImGuiContext& g = *GImGui;
9524 ImGuiWindow* window = g.CurrentWindow;
9525
9526 bool source_drag_active = false;
9527 ImGuiID source_id = 0;
9528 ImGuiID source_parent_id = 0;
9529 ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
9530 if (!(flags & ImGuiDragDropFlags_SourceExtern))
9531 {
9532 source_id = window->DC.LastItemId;
9533 if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
9534 return false;
9535 if (g.IO.MouseDown[mouse_button] == false)
9536 return false;
9537
9538 if (source_id == 0)
9539 {
9540 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
9541 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
9542 if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
9543 {
9544 IM_ASSERT(0);
9545 return false;
9546 }
9547
9548 // Early out
9549 if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
9550 return false;
9551
9552 // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
9553 // We build a throwaway ID based on current ID stack + relative AABB of items in window.
9554 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
9555 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
9556 source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
9557 bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id);
9558 if (is_hovered && g.IO.MouseClicked[mouse_button])
9559 {
9560 SetActiveID(source_id, window);
9561 FocusWindow(window);
9562 }
9563 if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
9564 g.ActiveIdAllowOverlap = is_hovered;
9565 }
9566 else
9567 {
9568 g.ActiveIdAllowOverlap = false;
9569 }
9570 if (g.ActiveId != source_id)
9571 return false;
9572 source_parent_id = window->IDStack.back();
9573 source_drag_active = IsMouseDragging(mouse_button);
9574
9575 // Disable navigation and key inputs while dragging
9576 g.ActiveIdUsingNavDirMask = ~(ImU32)0;
9577 g.ActiveIdUsingNavInputMask = ~(ImU32)0;
9578 g.ActiveIdUsingKeyInputMask = ~(ImU64)0;
9579 }
9580 else
9581 {
9582 window = NULL;
9583 source_id = ImHashStr("#SourceExtern");
9584 source_drag_active = true;
9585 }
9586
9587 if (source_drag_active)
9588 {
9589 if (!g.DragDropActive)
9590 {
9591 IM_ASSERT(source_id != 0);
9592 ClearDragDrop();
9593 ImGuiPayload& payload = g.DragDropPayload;
9594 payload.SourceId = source_id;
9595 payload.SourceParentId = source_parent_id;
9596 g.DragDropActive = true;
9597 g.DragDropSourceFlags = flags;
9598 g.DragDropMouseButton = mouse_button;
9599 if (payload.SourceId == g.ActiveId)
9600 g.ActiveIdNoClearOnFocusLoss = true;
9601 }
9602 g.DragDropSourceFrameCount = g.FrameCount;
9603 g.DragDropWithinSource = true;
9604
9605 if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
9606 {
9607 // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
9608 // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
9609 BeginTooltip();
9610 if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
9611 {
9612 ImGuiWindow* tooltip_window = g.CurrentWindow;
9613 tooltip_window->SkipItems = true;
9614 tooltip_window->HiddenFramesCanSkipItems = 1;
9615 }
9616 }
9617
9618 if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
9619 window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
9620
9621 return true;
9622 }
9623 return false;
9624 }
9625
EndDragDropSource()9626 void ImGui::EndDragDropSource()
9627 {
9628 ImGuiContext& g = *GImGui;
9629 IM_ASSERT(g.DragDropActive);
9630 IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?");
9631
9632 if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
9633 EndTooltip();
9634
9635 // Discard the drag if have not called SetDragDropPayload()
9636 if (g.DragDropPayload.DataFrameCount == -1)
9637 ClearDragDrop();
9638 g.DragDropWithinSource = false;
9639 }
9640
9641 // 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)9642 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
9643 {
9644 ImGuiContext& g = *GImGui;
9645 ImGuiPayload& payload = g.DragDropPayload;
9646 if (cond == 0)
9647 cond = ImGuiCond_Always;
9648
9649 IM_ASSERT(type != NULL);
9650 IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
9651 IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
9652 IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
9653 IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
9654
9655 if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
9656 {
9657 // Copy payload
9658 ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
9659 g.DragDropPayloadBufHeap.resize(0);
9660 if (data_size > sizeof(g.DragDropPayloadBufLocal))
9661 {
9662 // Store in heap
9663 g.DragDropPayloadBufHeap.resize((int)data_size);
9664 payload.Data = g.DragDropPayloadBufHeap.Data;
9665 memcpy(payload.Data, data, data_size);
9666 }
9667 else if (data_size > 0)
9668 {
9669 // Store locally
9670 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
9671 payload.Data = g.DragDropPayloadBufLocal;
9672 memcpy(payload.Data, data, data_size);
9673 }
9674 else
9675 {
9676 payload.Data = NULL;
9677 }
9678 payload.DataSize = (int)data_size;
9679 }
9680 payload.DataFrameCount = g.FrameCount;
9681
9682 return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
9683 }
9684
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)9685 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
9686 {
9687 ImGuiContext& g = *GImGui;
9688 if (!g.DragDropActive)
9689 return false;
9690
9691 ImGuiWindow* window = g.CurrentWindow;
9692 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
9693 if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
9694 return false;
9695 IM_ASSERT(id != 0);
9696 if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
9697 return false;
9698 if (window->SkipItems)
9699 return false;
9700
9701 IM_ASSERT(g.DragDropWithinTarget == false);
9702 g.DragDropTargetRect = bb;
9703 g.DragDropTargetId = id;
9704 g.DragDropWithinTarget = true;
9705 return true;
9706 }
9707
9708 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
9709 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
9710 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
9711 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()9712 bool ImGui::BeginDragDropTarget()
9713 {
9714 ImGuiContext& g = *GImGui;
9715 if (!g.DragDropActive)
9716 return false;
9717
9718 ImGuiWindow* window = g.CurrentWindow;
9719 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
9720 return false;
9721 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
9722 if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
9723 return false;
9724
9725 const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
9726 ImGuiID id = window->DC.LastItemId;
9727 if (id == 0)
9728 id = window->GetIDFromRectangle(display_rect);
9729 if (g.DragDropPayload.SourceId == id)
9730 return false;
9731
9732 IM_ASSERT(g.DragDropWithinTarget == false);
9733 g.DragDropTargetRect = display_rect;
9734 g.DragDropTargetId = id;
9735 g.DragDropWithinTarget = true;
9736 return true;
9737 }
9738
IsDragDropPayloadBeingAccepted()9739 bool ImGui::IsDragDropPayloadBeingAccepted()
9740 {
9741 ImGuiContext& g = *GImGui;
9742 return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
9743 }
9744
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)9745 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
9746 {
9747 ImGuiContext& g = *GImGui;
9748 ImGuiWindow* window = g.CurrentWindow;
9749 ImGuiPayload& payload = g.DragDropPayload;
9750 IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
9751 IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
9752 if (type != NULL && !payload.IsDataType(type))
9753 return NULL;
9754
9755 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
9756 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
9757 const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
9758 ImRect r = g.DragDropTargetRect;
9759 float r_surface = r.GetWidth() * r.GetHeight();
9760 if (r_surface <= g.DragDropAcceptIdCurrRectSurface)
9761 {
9762 g.DragDropAcceptFlags = flags;
9763 g.DragDropAcceptIdCurr = g.DragDropTargetId;
9764 g.DragDropAcceptIdCurrRectSurface = r_surface;
9765 }
9766
9767 // Render default drop visuals
9768 payload.Preview = was_accepted_previously;
9769 flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
9770 if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
9771 {
9772 // FIXME-DRAG: Settle on a proper default visuals for drop target.
9773 r.Expand(3.5f);
9774 bool push_clip_rect = !window->ClipRect.Contains(r);
9775 if (push_clip_rect) window->DrawList->PushClipRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1));
9776 window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
9777 if (push_clip_rect) window->DrawList->PopClipRect();
9778 }
9779
9780 g.DragDropAcceptFrameCount = g.FrameCount;
9781 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()
9782 if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
9783 return NULL;
9784
9785 return &payload;
9786 }
9787
GetDragDropPayload()9788 const ImGuiPayload* ImGui::GetDragDropPayload()
9789 {
9790 ImGuiContext& g = *GImGui;
9791 return g.DragDropActive ? &g.DragDropPayload : NULL;
9792 }
9793
9794 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()9795 void ImGui::EndDragDropTarget()
9796 {
9797 ImGuiContext& g = *GImGui;
9798 IM_ASSERT(g.DragDropActive);
9799 IM_ASSERT(g.DragDropWithinTarget);
9800 g.DragDropWithinTarget = false;
9801 }
9802
9803 //-----------------------------------------------------------------------------
9804 // [SECTION] LOGGING/CAPTURING
9805 //-----------------------------------------------------------------------------
9806 // All text output from the interface can be captured into tty/file/clipboard.
9807 // By default, tree nodes are automatically opened during logging.
9808 //-----------------------------------------------------------------------------
9809
9810 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)9811 void ImGui::LogText(const char* fmt, ...)
9812 {
9813 ImGuiContext& g = *GImGui;
9814 if (!g.LogEnabled)
9815 return;
9816
9817 va_list args;
9818 va_start(args, fmt);
9819 if (g.LogFile)
9820 {
9821 g.LogBuffer.Buf.resize(0);
9822 g.LogBuffer.appendfv(fmt, args);
9823 ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
9824 }
9825 else
9826 {
9827 g.LogBuffer.appendfv(fmt, args);
9828 }
9829 va_end(args);
9830 }
9831
9832 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
9833 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)9834 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
9835 {
9836 ImGuiContext& g = *GImGui;
9837 ImGuiWindow* window = g.CurrentWindow;
9838
9839 if (!text_end)
9840 text_end = FindRenderedTextEnd(text, text_end);
9841
9842 const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1);
9843 if (ref_pos)
9844 g.LogLinePosY = ref_pos->y;
9845 if (log_new_line)
9846 g.LogLineFirstItem = true;
9847
9848 const char* text_remaining = text;
9849 if (g.LogDepthRef > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth
9850 g.LogDepthRef = window->DC.TreeDepth;
9851 const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
9852 for (;;)
9853 {
9854 // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
9855 // We don't add a trailing \n to allow a subsequent item on the same line to be captured.
9856 const char* line_start = text_remaining;
9857 const char* line_end = ImStreolRange(line_start, text_end);
9858 const bool is_first_line = (line_start == text);
9859 const bool is_last_line = (line_end == text_end);
9860 if (!is_last_line || (line_start != line_end))
9861 {
9862 const int char_count = (int)(line_end - line_start);
9863 if (log_new_line || !is_first_line)
9864 LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start);
9865 else if (g.LogLineFirstItem)
9866 LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start);
9867 else
9868 LogText(" %.*s", char_count, line_start);
9869 g.LogLineFirstItem = false;
9870 }
9871 else if (log_new_line)
9872 {
9873 // An empty "" string at a different Y position should output a carriage return.
9874 LogText(IM_NEWLINE);
9875 break;
9876 }
9877
9878 if (is_last_line)
9879 break;
9880 text_remaining = line_end + 1;
9881 }
9882 }
9883
9884 // Start logging/capturing text output
LogBegin(ImGuiLogType type,int auto_open_depth)9885 void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
9886 {
9887 ImGuiContext& g = *GImGui;
9888 ImGuiWindow* window = g.CurrentWindow;
9889 IM_ASSERT(g.LogEnabled == false);
9890 IM_ASSERT(g.LogFile == NULL);
9891 IM_ASSERT(g.LogBuffer.empty());
9892 g.LogEnabled = true;
9893 g.LogType = type;
9894 g.LogDepthRef = window->DC.TreeDepth;
9895 g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
9896 g.LogLinePosY = FLT_MAX;
9897 g.LogLineFirstItem = true;
9898 }
9899
LogToTTY(int auto_open_depth)9900 void ImGui::LogToTTY(int auto_open_depth)
9901 {
9902 ImGuiContext& g = *GImGui;
9903 if (g.LogEnabled)
9904 return;
9905 IM_UNUSED(auto_open_depth);
9906 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9907 LogBegin(ImGuiLogType_TTY, auto_open_depth);
9908 g.LogFile = stdout;
9909 #endif
9910 }
9911
9912 // Start logging/capturing text output to given file
LogToFile(int auto_open_depth,const char * filename)9913 void ImGui::LogToFile(int auto_open_depth, const char* filename)
9914 {
9915 ImGuiContext& g = *GImGui;
9916 if (g.LogEnabled)
9917 return;
9918
9919 // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
9920 // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
9921 // By opening the file in binary mode "ab" we have consistent output everywhere.
9922 if (!filename)
9923 filename = g.IO.LogFilename;
9924 if (!filename || !filename[0])
9925 return;
9926 ImFileHandle f = ImFileOpen(filename, "ab");
9927 if (!f)
9928 {
9929 IM_ASSERT(0);
9930 return;
9931 }
9932
9933 LogBegin(ImGuiLogType_File, auto_open_depth);
9934 g.LogFile = f;
9935 }
9936
9937 // Start logging/capturing text output to clipboard
LogToClipboard(int auto_open_depth)9938 void ImGui::LogToClipboard(int auto_open_depth)
9939 {
9940 ImGuiContext& g = *GImGui;
9941 if (g.LogEnabled)
9942 return;
9943 LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
9944 }
9945
LogToBuffer(int auto_open_depth)9946 void ImGui::LogToBuffer(int auto_open_depth)
9947 {
9948 ImGuiContext& g = *GImGui;
9949 if (g.LogEnabled)
9950 return;
9951 LogBegin(ImGuiLogType_Buffer, auto_open_depth);
9952 }
9953
LogFinish()9954 void ImGui::LogFinish()
9955 {
9956 ImGuiContext& g = *GImGui;
9957 if (!g.LogEnabled)
9958 return;
9959
9960 LogText(IM_NEWLINE);
9961 switch (g.LogType)
9962 {
9963 case ImGuiLogType_TTY:
9964 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9965 fflush(g.LogFile);
9966 #endif
9967 break;
9968 case ImGuiLogType_File:
9969 ImFileClose(g.LogFile);
9970 break;
9971 case ImGuiLogType_Buffer:
9972 break;
9973 case ImGuiLogType_Clipboard:
9974 if (!g.LogBuffer.empty())
9975 SetClipboardText(g.LogBuffer.begin());
9976 break;
9977 case ImGuiLogType_None:
9978 IM_ASSERT(0);
9979 break;
9980 }
9981
9982 g.LogEnabled = false;
9983 g.LogType = ImGuiLogType_None;
9984 g.LogFile = NULL;
9985 g.LogBuffer.clear();
9986 }
9987
9988 // Helper to display logging buttons
9989 // FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
LogButtons()9990 void ImGui::LogButtons()
9991 {
9992 ImGuiContext& g = *GImGui;
9993
9994 PushID("LogButtons");
9995 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9996 const bool log_to_tty = Button("Log To TTY"); SameLine();
9997 #else
9998 const bool log_to_tty = false;
9999 #endif
10000 const bool log_to_file = Button("Log To File"); SameLine();
10001 const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
10002 PushAllowKeyboardFocus(false);
10003 SetNextItemWidth(80.0f);
10004 SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
10005 PopAllowKeyboardFocus();
10006 PopID();
10007
10008 // Start logging at the end of the function so that the buttons don't appear in the log
10009 if (log_to_tty)
10010 LogToTTY();
10011 if (log_to_file)
10012 LogToFile();
10013 if (log_to_clipboard)
10014 LogToClipboard();
10015 }
10016
10017
10018 //-----------------------------------------------------------------------------
10019 // [SECTION] SETTINGS
10020 //-----------------------------------------------------------------------------
10021 // - UpdateSettings() [Internal]
10022 // - MarkIniSettingsDirty() [Internal]
10023 // - CreateNewWindowSettings() [Internal]
10024 // - FindWindowSettings() [Internal]
10025 // - FindOrCreateWindowSettings() [Internal]
10026 // - FindSettingsHandler() [Internal]
10027 // - ClearIniSettings() [Internal]
10028 // - LoadIniSettingsFromDisk()
10029 // - LoadIniSettingsFromMemory()
10030 // - SaveIniSettingsToDisk()
10031 // - SaveIniSettingsToMemory()
10032 // - WindowSettingsHandler_***() [Internal]
10033 //-----------------------------------------------------------------------------
10034
10035 // Called by NewFrame()
UpdateSettings()10036 void ImGui::UpdateSettings()
10037 {
10038 // Load settings on first frame (if not explicitly loaded manually before)
10039 ImGuiContext& g = *GImGui;
10040 if (!g.SettingsLoaded)
10041 {
10042 IM_ASSERT(g.SettingsWindows.empty());
10043 if (g.IO.IniFilename)
10044 LoadIniSettingsFromDisk(g.IO.IniFilename);
10045 g.SettingsLoaded = true;
10046 }
10047
10048 // Save settings (with a delay after the last modification, so we don't spam disk too much)
10049 if (g.SettingsDirtyTimer > 0.0f)
10050 {
10051 g.SettingsDirtyTimer -= g.IO.DeltaTime;
10052 if (g.SettingsDirtyTimer <= 0.0f)
10053 {
10054 if (g.IO.IniFilename != NULL)
10055 SaveIniSettingsToDisk(g.IO.IniFilename);
10056 else
10057 g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
10058 g.SettingsDirtyTimer = 0.0f;
10059 }
10060 }
10061 }
10062
MarkIniSettingsDirty()10063 void ImGui::MarkIniSettingsDirty()
10064 {
10065 ImGuiContext& g = *GImGui;
10066 if (g.SettingsDirtyTimer <= 0.0f)
10067 g.SettingsDirtyTimer = g.IO.IniSavingRate;
10068 }
10069
MarkIniSettingsDirty(ImGuiWindow * window)10070 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
10071 {
10072 ImGuiContext& g = *GImGui;
10073 if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
10074 if (g.SettingsDirtyTimer <= 0.0f)
10075 g.SettingsDirtyTimer = g.IO.IniSavingRate;
10076 }
10077
CreateNewWindowSettings(const char * name)10078 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
10079 {
10080 ImGuiContext& g = *GImGui;
10081
10082 #if !IMGUI_DEBUG_INI_SETTINGS
10083 // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
10084 // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier.
10085 if (const char* p = strstr(name, "###"))
10086 name = p;
10087 #endif
10088 const size_t name_len = strlen(name);
10089
10090 // Allocate chunk
10091 const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
10092 ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
10093 IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
10094 settings->ID = ImHashStr(name, name_len);
10095 memcpy(settings->GetName(), name, name_len + 1); // Store with zero terminator
10096
10097 return settings;
10098 }
10099
FindWindowSettings(ImGuiID id)10100 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
10101 {
10102 ImGuiContext& g = *GImGui;
10103 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10104 if (settings->ID == id)
10105 return settings;
10106 return NULL;
10107 }
10108
FindOrCreateWindowSettings(const char * name)10109 ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
10110 {
10111 if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name)))
10112 return settings;
10113 return CreateNewWindowSettings(name);
10114 }
10115
FindSettingsHandler(const char * type_name)10116 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
10117 {
10118 ImGuiContext& g = *GImGui;
10119 const ImGuiID type_hash = ImHashStr(type_name);
10120 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10121 if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
10122 return &g.SettingsHandlers[handler_n];
10123 return NULL;
10124 }
10125
ClearIniSettings()10126 void ImGui::ClearIniSettings()
10127 {
10128 ImGuiContext& g = *GImGui;
10129 g.SettingsIniData.clear();
10130 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10131 if (g.SettingsHandlers[handler_n].ClearAllFn)
10132 g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]);
10133 }
10134
LoadIniSettingsFromDisk(const char * ini_filename)10135 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
10136 {
10137 size_t file_data_size = 0;
10138 char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
10139 if (!file_data)
10140 return;
10141 LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
10142 IM_FREE(file_data);
10143 }
10144
10145 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)10146 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
10147 {
10148 ImGuiContext& g = *GImGui;
10149 IM_ASSERT(g.Initialized);
10150 //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()");
10151 //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
10152
10153 // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
10154 // 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..
10155 if (ini_size == 0)
10156 ini_size = strlen(ini_data);
10157 g.SettingsIniData.Buf.resize((int)ini_size + 1);
10158 char* const buf = g.SettingsIniData.Buf.Data;
10159 char* const buf_end = buf + ini_size;
10160 memcpy(buf, ini_data, ini_size);
10161 buf_end[0] = 0;
10162
10163 // Call pre-read handlers
10164 // Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
10165 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10166 if (g.SettingsHandlers[handler_n].ReadInitFn)
10167 g.SettingsHandlers[handler_n].ReadInitFn(&g, &g.SettingsHandlers[handler_n]);
10168
10169 void* entry_data = NULL;
10170 ImGuiSettingsHandler* entry_handler = NULL;
10171
10172 char* line_end = NULL;
10173 for (char* line = buf; line < buf_end; line = line_end + 1)
10174 {
10175 // Skip new lines markers, then find end of the line
10176 while (*line == '\n' || *line == '\r')
10177 line++;
10178 line_end = line;
10179 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
10180 line_end++;
10181 line_end[0] = 0;
10182 if (line[0] == ';')
10183 continue;
10184 if (line[0] == '[' && line_end > line && line_end[-1] == ']')
10185 {
10186 // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
10187 line_end[-1] = 0;
10188 const char* name_end = line_end - 1;
10189 const char* type_start = line + 1;
10190 char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
10191 const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
10192 if (!type_end || !name_start)
10193 continue;
10194 *type_end = 0; // Overwrite first ']'
10195 name_start++; // Skip second '['
10196 entry_handler = FindSettingsHandler(type_start);
10197 entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
10198 }
10199 else if (entry_handler != NULL && entry_data != NULL)
10200 {
10201 // Let type handler parse the line
10202 entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
10203 }
10204 }
10205 g.SettingsLoaded = true;
10206
10207 // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
10208 memcpy(buf, ini_data, ini_size);
10209
10210 // Call post-read handlers
10211 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10212 if (g.SettingsHandlers[handler_n].ApplyAllFn)
10213 g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]);
10214 }
10215
SaveIniSettingsToDisk(const char * ini_filename)10216 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
10217 {
10218 ImGuiContext& g = *GImGui;
10219 g.SettingsDirtyTimer = 0.0f;
10220 if (!ini_filename)
10221 return;
10222
10223 size_t ini_data_size = 0;
10224 const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
10225 ImFileHandle f = ImFileOpen(ini_filename, "wt");
10226 if (!f)
10227 return;
10228 ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
10229 ImFileClose(f);
10230 }
10231
10232 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)10233 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
10234 {
10235 ImGuiContext& g = *GImGui;
10236 g.SettingsDirtyTimer = 0.0f;
10237 g.SettingsIniData.Buf.resize(0);
10238 g.SettingsIniData.Buf.push_back(0);
10239 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10240 {
10241 ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
10242 handler->WriteAllFn(&g, handler, &g.SettingsIniData);
10243 }
10244 if (out_size)
10245 *out_size = (size_t)g.SettingsIniData.size();
10246 return g.SettingsIniData.c_str();
10247 }
10248
WindowSettingsHandler_ClearAll(ImGuiContext * ctx,ImGuiSettingsHandler *)10249 static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
10250 {
10251 ImGuiContext& g = *ctx;
10252 for (int i = 0; i != g.Windows.Size; i++)
10253 g.Windows[i]->SettingsOffset = -1;
10254 g.SettingsWindows.clear();
10255 }
10256
WindowSettingsHandler_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)10257 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
10258 {
10259 ImGuiWindowSettings* settings = ImGui::FindOrCreateWindowSettings(name);
10260 ImGuiID id = settings->ID;
10261 *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry
10262 settings->ID = id;
10263 settings->WantApply = true;
10264 return (void*)settings;
10265 }
10266
WindowSettingsHandler_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)10267 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
10268 {
10269 ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
10270 int x, y;
10271 int i;
10272 if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); }
10273 else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); }
10274 else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); }
10275 }
10276
10277 // Apply to existing windows (if any)
WindowSettingsHandler_ApplyAll(ImGuiContext * ctx,ImGuiSettingsHandler *)10278 static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
10279 {
10280 ImGuiContext& g = *ctx;
10281 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10282 if (settings->WantApply)
10283 {
10284 if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))
10285 ApplyWindowSettings(window, settings);
10286 settings->WantApply = false;
10287 }
10288 }
10289
WindowSettingsHandler_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)10290 static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
10291 {
10292 // Gather data from windows that were active during this session
10293 // (if a window wasn't opened in this session we preserve its settings)
10294 ImGuiContext& g = *ctx;
10295 for (int i = 0; i != g.Windows.Size; i++)
10296 {
10297 ImGuiWindow* window = g.Windows[i];
10298 if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
10299 continue;
10300
10301 ImGuiWindowSettings* settings = (window->SettingsOffset != -1) ? g.SettingsWindows.ptr_from_offset(window->SettingsOffset) : ImGui::FindWindowSettings(window->ID);
10302 if (!settings)
10303 {
10304 settings = ImGui::CreateNewWindowSettings(window->Name);
10305 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
10306 }
10307 IM_ASSERT(settings->ID == window->ID);
10308 settings->Pos = ImVec2ih((short)window->Pos.x, (short)window->Pos.y);
10309 settings->Size = ImVec2ih((short)window->SizeFull.x, (short)window->SizeFull.y);
10310 settings->Collapsed = window->Collapsed;
10311 }
10312
10313 // Write to text buffer
10314 buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
10315 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10316 {
10317 const char* settings_name = settings->GetName();
10318 buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
10319 buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
10320 buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
10321 buf->appendf("Collapsed=%d\n", settings->Collapsed);
10322 buf->append("\n");
10323 }
10324 }
10325
10326
10327 //-----------------------------------------------------------------------------
10328 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
10329 //-----------------------------------------------------------------------------
10330
10331 // (this section is filled in the 'docking' branch)
10332
10333
10334 //-----------------------------------------------------------------------------
10335 // [SECTION] DOCKING
10336 //-----------------------------------------------------------------------------
10337
10338 // (this section is filled in the 'docking' branch)
10339
10340
10341 //-----------------------------------------------------------------------------
10342 // [SECTION] PLATFORM DEPENDENT HELPERS
10343 //-----------------------------------------------------------------------------
10344
10345 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
10346
10347 #ifdef _MSC_VER
10348 #pragma comment(lib, "user32")
10349 #pragma comment(lib, "kernel32")
10350 #endif
10351
10352 // Win32 clipboard implementation
10353 // We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()
GetClipboardTextFn_DefaultImpl(void *)10354 static const char* GetClipboardTextFn_DefaultImpl(void*)
10355 {
10356 ImGuiContext& g = *GImGui;
10357 g.ClipboardHandlerData.clear();
10358 if (!::OpenClipboard(NULL))
10359 return NULL;
10360 HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
10361 if (wbuf_handle == NULL)
10362 {
10363 ::CloseClipboard();
10364 return NULL;
10365 }
10366 if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
10367 {
10368 int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
10369 g.ClipboardHandlerData.resize(buf_len);
10370 ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
10371 }
10372 ::GlobalUnlock(wbuf_handle);
10373 ::CloseClipboard();
10374 return g.ClipboardHandlerData.Data;
10375 }
10376
SetClipboardTextFn_DefaultImpl(void *,const char * text)10377 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10378 {
10379 if (!::OpenClipboard(NULL))
10380 return;
10381 const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
10382 HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
10383 if (wbuf_handle == NULL)
10384 {
10385 ::CloseClipboard();
10386 return;
10387 }
10388 WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
10389 ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
10390 ::GlobalUnlock(wbuf_handle);
10391 ::EmptyClipboard();
10392 if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
10393 ::GlobalFree(wbuf_handle);
10394 ::CloseClipboard();
10395 }
10396
10397 #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
10398
10399 #include <Carbon/Carbon.h> // Use old API to avoid need for separate .mm file
10400 static PasteboardRef main_clipboard = 0;
10401
10402 // OSX clipboard implementation
10403 // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
SetClipboardTextFn_DefaultImpl(void *,const char * text)10404 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10405 {
10406 if (!main_clipboard)
10407 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
10408 PasteboardClear(main_clipboard);
10409 CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
10410 if (cf_data)
10411 {
10412 PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
10413 CFRelease(cf_data);
10414 }
10415 }
10416
GetClipboardTextFn_DefaultImpl(void *)10417 static const char* GetClipboardTextFn_DefaultImpl(void*)
10418 {
10419 if (!main_clipboard)
10420 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
10421 PasteboardSynchronize(main_clipboard);
10422
10423 ItemCount item_count = 0;
10424 PasteboardGetItemCount(main_clipboard, &item_count);
10425 for (ItemCount i = 0; i < item_count; i++)
10426 {
10427 PasteboardItemID item_id = 0;
10428 PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
10429 CFArrayRef flavor_type_array = 0;
10430 PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
10431 for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
10432 {
10433 CFDataRef cf_data;
10434 if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
10435 {
10436 ImGuiContext& g = *GImGui;
10437 g.ClipboardHandlerData.clear();
10438 int length = (int)CFDataGetLength(cf_data);
10439 g.ClipboardHandlerData.resize(length + 1);
10440 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);
10441 g.ClipboardHandlerData[length] = 0;
10442 CFRelease(cf_data);
10443 return g.ClipboardHandlerData.Data;
10444 }
10445 }
10446 }
10447 return NULL;
10448 }
10449
10450 #else
10451
10452 // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
GetClipboardTextFn_DefaultImpl(void *)10453 static const char* GetClipboardTextFn_DefaultImpl(void*)
10454 {
10455 ImGuiContext& g = *GImGui;
10456 return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();
10457 }
10458
SetClipboardTextFn_DefaultImpl(void *,const char * text)10459 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10460 {
10461 ImGuiContext& g = *GImGui;
10462 g.ClipboardHandlerData.clear();
10463 const char* text_end = text + strlen(text);
10464 g.ClipboardHandlerData.resize((int)(text_end - text) + 1);
10465 memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));
10466 g.ClipboardHandlerData[(int)(text_end - text)] = 0;
10467 }
10468
10469 #endif
10470
10471 // Win32 API IME support (for Asian languages, etc.)
10472 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
10473
10474 #include <imm.h>
10475 #ifdef _MSC_VER
10476 #pragma comment(lib, "imm32")
10477 #endif
10478
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)10479 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
10480 {
10481 // Notify OS Input Method Editor of text input position
10482 ImGuiIO& io = ImGui::GetIO();
10483 if (HWND hwnd = (HWND)io.ImeWindowHandle)
10484 if (HIMC himc = ::ImmGetContext(hwnd))
10485 {
10486 COMPOSITIONFORM cf;
10487 cf.ptCurrentPos.x = x;
10488 cf.ptCurrentPos.y = y;
10489 cf.dwStyle = CFS_FORCE_POSITION;
10490 ::ImmSetCompositionWindow(himc, &cf);
10491 ::ImmReleaseContext(hwnd, himc);
10492 }
10493 }
10494
10495 #else
10496
ImeSetInputScreenPosFn_DefaultImpl(int,int)10497 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
10498
10499 #endif
10500
10501 //-----------------------------------------------------------------------------
10502 // [SECTION] METRICS/DEBUGGER WINDOW
10503 //-----------------------------------------------------------------------------
10504 // - MetricsHelpMarker() [Internal]
10505 // - ShowMetricsWindow()
10506 // - DebugNodeColumns() [Internal]
10507 // - DebugNodeDrawList() [Internal]
10508 // - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]
10509 // - DebugNodeStorage() [Internal]
10510 // - DebugNodeTabBar() [Internal]
10511 // - DebugNodeWindow() [Internal]
10512 // - DebugNodeWindowSettings() [Internal]
10513 // - DebugNodeWindowsList() [Internal]
10514 //-----------------------------------------------------------------------------
10515
10516 #ifndef IMGUI_DISABLE_METRICS_WINDOW
10517
10518 // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
MetricsHelpMarker(const char * desc)10519 static void MetricsHelpMarker(const char* desc)
10520 {
10521 ImGui::TextDisabled("(?)");
10522 if (ImGui::IsItemHovered())
10523 {
10524 ImGui::BeginTooltip();
10525 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
10526 ImGui::TextUnformatted(desc);
10527 ImGui::PopTextWrapPos();
10528 ImGui::EndTooltip();
10529 }
10530 }
10531
ShowMetricsWindow(bool * p_open)10532 void ImGui::ShowMetricsWindow(bool* p_open)
10533 {
10534 if (!Begin("Dear ImGui Metrics/Debugger", p_open))
10535 {
10536 End();
10537 return;
10538 }
10539
10540 ImGuiContext& g = *GImGui;
10541 ImGuiIO& io = g.IO;
10542 ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
10543
10544 // Basic info
10545 Text("Dear ImGui %s", ImGui::GetVersion());
10546 Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
10547 Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
10548 Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
10549 Text("%d active allocations", io.MetricsActiveAllocations);
10550 //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; }
10551
10552 Separator();
10553
10554 // Debugging enums
10555 enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
10556 const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" };
10557 enum { TRT_OuterRect, TRT_InnerRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsWorkRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentFrozen, TRT_ColumnsContentUnfrozen, TRT_Count }; // Tables Rect Type
10558 const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" };
10559 if (cfg->ShowWindowsRectsType < 0)
10560 cfg->ShowWindowsRectsType = WRT_WorkRect;
10561 if (cfg->ShowTablesRectsType < 0)
10562 cfg->ShowTablesRectsType = TRT_WorkRect;
10563
10564 struct Funcs
10565 {
10566 static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n)
10567 {
10568 if (rect_type == TRT_OuterRect) { return table->OuterRect; }
10569 else if (rect_type == TRT_InnerRect) { return table->InnerRect; }
10570 else if (rect_type == TRT_WorkRect) { return table->WorkRect; }
10571 else if (rect_type == TRT_HostClipRect) { return table->HostClipRect; }
10572 else if (rect_type == TRT_InnerClipRect) { return table->InnerClipRect; }
10573 else if (rect_type == TRT_BackgroundClipRect) { return table->BgClipRect; }
10574 else if (rect_type == TRT_ColumnsRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table->LastOuterHeight); }
10575 else if (rect_type == TRT_ColumnsWorkRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->WorkRect.Min.y, c->WorkMaxX, table->WorkRect.Max.y); }
10576 else if (rect_type == TRT_ColumnsClipRect) { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }
10577 else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table->LastFirstRowHeight); } // Note: y1/y2 not always accurate
10578 else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table->LastFirstRowHeight); }
10579 else if (rect_type == TRT_ColumnsContentFrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table->LastFirstRowHeight); }
10580 else if (rect_type == TRT_ColumnsContentUnfrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table->LastFirstRowHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); }
10581 IM_ASSERT(0);
10582 return ImRect();
10583 }
10584
10585 static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
10586 {
10587 if (rect_type == WRT_OuterRect) { return window->Rect(); }
10588 else if (rect_type == WRT_OuterRectClipped) { return window->OuterRectClipped; }
10589 else if (rect_type == WRT_InnerRect) { return window->InnerRect; }
10590 else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; }
10591 else if (rect_type == WRT_WorkRect) { return window->WorkRect; }
10592 else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
10593 else if (rect_type == WRT_ContentIdeal) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }
10594 else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; }
10595 IM_ASSERT(0);
10596 return ImRect();
10597 }
10598 };
10599
10600 // Tools
10601 if (TreeNode("Tools"))
10602 {
10603 // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
10604 if (Button("Item Picker.."))
10605 DebugStartItemPicker();
10606 SameLine();
10607 MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.");
10608
10609 Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder);
10610 Checkbox("Show windows rectangles", &cfg->ShowWindowsRects);
10611 SameLine();
10612 SetNextItemWidth(GetFontSize() * 12);
10613 cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count);
10614 if (cfg->ShowWindowsRects && g.NavWindow != NULL)
10615 {
10616 BulletText("'%s':", g.NavWindow->Name);
10617 Indent();
10618 for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
10619 {
10620 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
10621 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]);
10622 }
10623 Unindent();
10624 }
10625 Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
10626 Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
10627
10628 Checkbox("Show tables rectangles", &cfg->ShowTablesRects);
10629 SameLine();
10630 SetNextItemWidth(GetFontSize() * 12);
10631 cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count);
10632 if (cfg->ShowTablesRects && g.NavWindow != NULL)
10633 {
10634 for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++)
10635 {
10636 ImGuiTable* table = g.Tables.GetByIndex(table_n);
10637 if (table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow))
10638 continue;
10639
10640 BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name);
10641 if (IsItemHovered())
10642 GetForegroundDrawList()->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, ~0, 2.0f);
10643 Indent();
10644 char buf[128];
10645 for (int rect_n = 0; rect_n < TRT_Count; rect_n++)
10646 {
10647 if (rect_n >= TRT_ColumnsRect)
10648 {
10649 if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect)
10650 continue;
10651 for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
10652 {
10653 ImRect r = Funcs::GetTableRect(table, rect_n, column_n);
10654 ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]);
10655 Selectable(buf);
10656 if (IsItemHovered())
10657 GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, ~0, 2.0f);
10658 }
10659 }
10660 else
10661 {
10662 ImRect r = Funcs::GetTableRect(table, rect_n, -1);
10663 ImFormatString(buf, IM_ARRAYSIZE(buf), "(%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(), trt_rects_names[rect_n]);
10664 Selectable(buf);
10665 if (IsItemHovered())
10666 GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, ~0, 2.0f);
10667 }
10668 }
10669 Unindent();
10670 }
10671 }
10672
10673 TreePop();
10674 }
10675
10676 // Contents
10677 DebugNodeWindowsList(&g.Windows, "Windows");
10678 //DebugNodeWindowList(&g.WindowsFocusOrder, "WindowsFocusOrder");
10679 if (TreeNode("DrawLists", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
10680 {
10681 for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
10682 DebugNodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
10683 TreePop();
10684 }
10685
10686 // Details for Popups
10687 if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
10688 {
10689 for (int i = 0; i < g.OpenPopupStack.Size; i++)
10690 {
10691 ImGuiWindow* window = g.OpenPopupStack[i].Window;
10692 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" : "");
10693 }
10694 TreePop();
10695 }
10696
10697 // Details for TabBars
10698 if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize()))
10699 {
10700 for (int n = 0; n < g.TabBars.GetSize(); n++)
10701 DebugNodeTabBar(g.TabBars.GetByIndex(n), "TabBar");
10702 TreePop();
10703 }
10704
10705 // Details for Tables
10706 #ifdef IMGUI_HAS_TABLE
10707 if (TreeNode("Tables", "Tables (%d)", g.Tables.GetSize()))
10708 {
10709 for (int n = 0; n < g.Tables.GetSize(); n++)
10710 DebugNodeTable(g.Tables.GetByIndex(n));
10711 TreePop();
10712 }
10713 #endif // #ifdef IMGUI_HAS_TABLE
10714
10715 // Details for Docking
10716 #ifdef IMGUI_HAS_DOCK
10717 if (TreeNode("Docking"))
10718 {
10719 TreePop();
10720 }
10721 #endif // #ifdef IMGUI_HAS_DOCK
10722
10723 // Settings
10724 if (TreeNode("Settings"))
10725 {
10726 if (SmallButton("Clear"))
10727 ClearIniSettings();
10728 SameLine();
10729 if (SmallButton("Save to memory"))
10730 SaveIniSettingsToMemory();
10731 SameLine();
10732 if (SmallButton("Save to disk"))
10733 SaveIniSettingsToDisk(g.IO.IniFilename);
10734 SameLine();
10735 if (g.IO.IniFilename)
10736 Text("\"%s\"", g.IO.IniFilename);
10737 else
10738 TextUnformatted("<NULL>");
10739 Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
10740 if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
10741 {
10742 for (int n = 0; n < g.SettingsHandlers.Size; n++)
10743 BulletText("%s", g.SettingsHandlers[n].TypeName);
10744 TreePop();
10745 }
10746 if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
10747 {
10748 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10749 DebugNodeWindowSettings(settings);
10750 TreePop();
10751 }
10752
10753 #ifdef IMGUI_HAS_TABLE
10754 if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
10755 {
10756 for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
10757 DebugNodeTableSettings(settings);
10758 TreePop();
10759 }
10760 #endif // #ifdef IMGUI_HAS_TABLE
10761
10762 #ifdef IMGUI_HAS_DOCK
10763 #endif // #ifdef IMGUI_HAS_DOCK
10764
10765 if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
10766 {
10767 InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);
10768 TreePop();
10769 }
10770 TreePop();
10771 }
10772
10773 // Misc Details
10774 if (TreeNode("Internal state"))
10775 {
10776 const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
10777
10778 Text("WINDOWING");
10779 Indent();
10780 Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
10781 Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
10782 Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL");
10783 Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
10784 Unindent();
10785
10786 Text("ITEMS");
10787 Indent();
10788 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]);
10789 Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
10790 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
10791 Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
10792 Unindent();
10793
10794 Text("NAV,FOCUS");
10795 Indent();
10796 Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
10797 Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
10798 Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
10799 Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
10800 Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
10801 Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
10802 Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
10803 Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
10804 Unindent();
10805
10806 TreePop();
10807 }
10808
10809 // Overlay: Display windows Rectangles and Begin Order
10810 if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder)
10811 {
10812 for (int n = 0; n < g.Windows.Size; n++)
10813 {
10814 ImGuiWindow* window = g.Windows[n];
10815 if (!window->WasActive)
10816 continue;
10817 ImDrawList* draw_list = GetForegroundDrawList(window);
10818 if (cfg->ShowWindowsRects)
10819 {
10820 ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType);
10821 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
10822 }
10823 if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow))
10824 {
10825 char buf[32];
10826 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
10827 float font_size = GetFontSize();
10828 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
10829 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
10830 }
10831 }
10832 }
10833
10834 #ifdef IMGUI_HAS_TABLE
10835 // Overlay: Display Tables Rectangles
10836 if (cfg->ShowTablesRects)
10837 {
10838 for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++)
10839 {
10840 ImGuiTable* table = g.Tables.GetByIndex(table_n);
10841 if (table->LastFrameActive < g.FrameCount - 1)
10842 continue;
10843 ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow);
10844 if (cfg->ShowTablesRectsType >= TRT_ColumnsRect)
10845 {
10846 for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
10847 {
10848 ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n);
10849 ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255);
10850 float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f;
10851 draw_list->AddRect(r.Min, r.Max, col, 0.0f, ~0, thickness);
10852 }
10853 }
10854 else
10855 {
10856 ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1);
10857 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
10858 }
10859 }
10860 }
10861 #endif // #ifdef IMGUI_HAS_TABLE
10862
10863 #ifdef IMGUI_HAS_DOCK
10864 // Overlay: Display Docking info
10865 if (show_docking_nodes && g.IO.KeyCtrl)
10866 {
10867 }
10868 #endif // #ifdef IMGUI_HAS_DOCK
10869
10870 End();
10871 }
10872
10873 // [DEBUG] Display contents of Columns
DebugNodeColumns(ImGuiOldColumns * columns)10874 void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
10875 {
10876 if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
10877 return;
10878 BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
10879 for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
10880 BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm));
10881 TreePop();
10882 }
10883
10884 // [DEBUG] Display contents of ImDrawList
DebugNodeDrawList(ImGuiWindow * window,const ImDrawList * draw_list,const char * label)10885 void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label)
10886 {
10887 ImGuiContext& g = *GImGui;
10888 ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
10889 int cmd_count = draw_list->CmdBuffer.Size;
10890 if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL)
10891 cmd_count--;
10892 bool node_open = 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, cmd_count);
10893 if (draw_list == GetWindowDrawList())
10894 {
10895 SameLine();
10896 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)
10897 if (node_open)
10898 TreePop();
10899 return;
10900 }
10901
10902 ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
10903 if (window && IsItemHovered())
10904 fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
10905 if (!node_open)
10906 return;
10907
10908 if (window && !window->WasActive)
10909 TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
10910
10911 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++)
10912 {
10913 if (pcmd->UserCallback)
10914 {
10915 BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
10916 continue;
10917 }
10918
10919 char buf[300];
10920 ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
10921 pcmd->ElemCount / 3, (void*)(intptr_t)pcmd->TextureId,
10922 pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
10923 bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
10924 if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)
10925 DebugNodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);
10926 if (!pcmd_node_open)
10927 continue;
10928
10929 // Calculate approximate coverage area (touched pixel count)
10930 // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
10931 const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
10932 const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset;
10933 float total_area = 0.0f;
10934 for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; )
10935 {
10936 ImVec2 triangle[3];
10937 for (int n = 0; n < 3; n++, idx_n++)
10938 triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos;
10939 total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
10940 }
10941
10942 // Display vertex information summary. Hover to get all triangles drawn in wire-frame
10943 ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
10944 Selectable(buf);
10945 if (IsItemHovered() && fg_draw_list)
10946 DebugNodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, true, false);
10947
10948 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
10949 ImGuiListClipper clipper;
10950 clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
10951 while (clipper.Step())
10952 for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
10953 {
10954 char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf);
10955 ImVec2 triangle[3];
10956 for (int n = 0; n < 3; n++, idx_i++)
10957 {
10958 const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
10959 triangle[n] = v.pos;
10960 buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
10961 (n == 0) ? "Vert:" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
10962 }
10963
10964 Selectable(buf, false);
10965 if (fg_draw_list && IsItemHovered())
10966 {
10967 ImDrawListFlags backup_flags = fg_draw_list->Flags;
10968 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
10969 fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f);
10970 fg_draw_list->Flags = backup_flags;
10971 }
10972 }
10973 TreePop();
10974 }
10975 TreePop();
10976 }
10977
10978 // [DEBUG] Display mesh/aabb of a ImDrawCmd
DebugNodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow * window,const ImDrawList * draw_list,const ImDrawCmd * draw_cmd,bool show_mesh,bool show_aabb)10979 void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow* window, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)
10980 {
10981 IM_ASSERT(show_mesh || show_aabb);
10982 ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
10983 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
10984 ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;
10985
10986 // Draw wire-frame version of all triangles
10987 ImRect clip_rect = draw_cmd->ClipRect;
10988 ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
10989 ImDrawListFlags backup_flags = fg_draw_list->Flags;
10990 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
10991 for (unsigned int idx_n = draw_cmd->IdxOffset; idx_n < draw_cmd->IdxOffset + draw_cmd->ElemCount; )
10992 {
10993 ImVec2 triangle[3];
10994 for (int n = 0; n < 3; n++, idx_n++)
10995 vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));
10996 if (show_mesh)
10997 fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f); // In yellow: mesh triangles
10998 }
10999 // Draw bounding boxes
11000 if (show_aabb)
11001 {
11002 fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU
11003 fg_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
11004 }
11005 fg_draw_list->Flags = backup_flags;
11006 }
11007
11008 // [DEBUG] Display contents of ImGuiStorage
DebugNodeStorage(ImGuiStorage * storage,const char * label)11009 void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)
11010 {
11011 if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
11012 return;
11013 for (int n = 0; n < storage->Data.Size; n++)
11014 {
11015 const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n];
11016 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.
11017 }
11018 TreePop();
11019 }
11020
11021 // [DEBUG] Display contents of ImGuiTabBar
DebugNodeTabBar(ImGuiTabBar * tab_bar,const char * label)11022 void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
11023 {
11024 // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
11025 char buf[256];
11026 char* p = buf;
11027 const char* buf_end = buf + IM_ARRAYSIZE(buf);
11028 const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2);
11029 p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*");
11030 IM_UNUSED(p);
11031 if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
11032 bool open = TreeNode(tab_bar, "%s", buf);
11033 if (!is_active) { PopStyleColor(); }
11034 if (is_active && IsItemHovered())
11035 {
11036 ImDrawList* draw_list = GetForegroundDrawList();
11037 draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));
11038 draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
11039 draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
11040 }
11041 if (open)
11042 {
11043 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
11044 {
11045 const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
11046 PushID(tab);
11047 if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2);
11048 if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine();
11049 Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f",
11050 tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "", tab->Offset, tab->Width, tab->ContentWidth);
11051 PopID();
11052 }
11053 TreePop();
11054 }
11055 }
11056
DebugNodeWindow(ImGuiWindow * window,const char * label)11057 void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
11058 {
11059 if (window == NULL)
11060 {
11061 BulletText("%s: NULL", label);
11062 return;
11063 }
11064
11065 ImGuiContext& g = *GImGui;
11066 const bool is_active = window->WasActive;
11067 ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
11068 if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
11069 const bool open = TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*");
11070 if (!is_active) { PopStyleColor(); }
11071 if (IsItemHovered() && is_active)
11072 GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
11073 if (!open)
11074 return;
11075
11076 if (window->MemoryCompacted)
11077 TextDisabled("Note: some memory buffers have been compacted/freed.");
11078
11079 ImGuiWindowFlags flags = window->Flags;
11080 DebugNodeDrawList(window, window->DrawList, "DrawList");
11081 BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y);
11082 BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
11083 (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
11084 (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
11085 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
11086 BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : "");
11087 BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
11088 BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
11089 BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
11090 BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
11091 if (!window->NavRectRel[0].IsInverted())
11092 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);
11093 else
11094 BulletText("NavRectRel[0]: <None>");
11095 if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); }
11096 if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
11097 if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); }
11098 if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
11099 {
11100 for (int n = 0; n < window->ColumnsStorage.Size; n++)
11101 DebugNodeColumns(&window->ColumnsStorage[n]);
11102 TreePop();
11103 }
11104 DebugNodeStorage(&window->StateStorage, "Storage");
11105 TreePop();
11106 }
11107
DebugNodeWindowSettings(ImGuiWindowSettings * settings)11108 void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)
11109 {
11110 Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
11111 settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
11112 }
11113
11114
DebugNodeWindowsList(ImVector<ImGuiWindow * > * windows,const char * label)11115 void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label)
11116 {
11117 if (!TreeNode(label, "%s (%d)", label, windows->Size))
11118 return;
11119 Text("(In front-to-back order:)");
11120 for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back
11121 {
11122 PushID((*windows)[i]);
11123 DebugNodeWindow((*windows)[i], "Window");
11124 PopID();
11125 }
11126 TreePop();
11127 }
11128
11129 #else
11130
ShowMetricsWindow(bool *)11131 void ImGui::ShowMetricsWindow(bool*) {}
DebugNodeColumns(ImGuiOldColumns *)11132 void ImGui::DebugNodeColumns(ImGuiOldColumns*) {}
DebugNodeDrawList(ImGuiWindow *,const ImDrawList *,const char *)11133 void ImGui::DebugNodeDrawList(ImGuiWindow*, const ImDrawList*, const char*) {}
DebugNodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow *,const ImDrawList *,const ImDrawCmd *,bool,bool)11134 void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
DebugNodeStorage(ImGuiStorage *,const char *)11135 void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}
DebugNodeTabBar(ImGuiTabBar *,const char *)11136 void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {}
DebugNodeWindow(ImGuiWindow *,const char *)11137 void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {}
DebugNodeWindowSettings(ImGuiWindowSettings *)11138 void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}
DebugNodeWindowsList(ImVector<ImGuiWindow * > *,const char *)11139 void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}
11140
11141 #endif
11142
11143 //-----------------------------------------------------------------------------
11144
11145 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
11146 // 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.
11147 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
11148 #include "imgui_user.inl"
11149 #endif
11150
11151 //-----------------------------------------------------------------------------
11152
11153 #endif // #ifndef IMGUI_DISABLE
11154