1// dear imgui, v1.91.6
2// (main code and documentation)
3
4// Help:
5// - See links below.
6// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
7// - Read top of imgui.cpp for more details, links and comments.
8
9// Resources:
10// - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md)
11// - Homepage ................... https://github.com/ocornut/imgui
12// - Releases & changelog ....... https://github.com/ocornut/imgui/releases
13// - Gallery .................... https://github.com/ocornut/imgui/issues?q=label%3Agallery (please post your screenshots/video there!)
14// - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there)
15// - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code)
16// - Third-party Extensions https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more)
17// - Bindings/Backends https://github.com/ocornut/imgui/wiki/Bindings (language bindings, backends for various tech/engines)
18// - Glossary https://github.com/ocornut/imgui/wiki/Glossary
19// - Debug Tools https://github.com/ocornut/imgui/wiki/Debug-Tools
20// - Software using Dear ImGui https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui
21// - Issues & support ........... https://github.com/ocornut/imgui/issues
22// - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps)
23
24// For first-time users having issues compiling/linking/running/loading fonts:
25// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
26// Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there.
27
28// Copyright (c) 2014-2024 Omar Cornut
29// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
30// See LICENSE.txt for copyright and licensing details (standard MIT License).
31// This library is free but needs your support to sustain development and maintenance.
32// Businesses: you can support continued development via B2B invoiced technical support, maintenance and sponsoring contracts.
33// PLEASE reach out at omar AT dearimgui DOT com. See https://github.com/ocornut/imgui/wiki/Funding
34// Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.
35
36// It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
37// Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
38// modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
39// come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
40// to a better solution or official support for them.
41
42/*
43
44Index of this file:
45
46DOCUMENTATION
47
48- MISSION STATEMENT
49- CONTROLS GUIDE
50- PROGRAMMER GUIDE
51 - READ FIRST
52 - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
53 - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
54 - HOW A SIMPLE APPLICATION MAY LOOK LIKE
55 - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
56- API BREAKING CHANGES (read me when you update!)
57- FREQUENTLY ASKED QUESTIONS (FAQ)
58 - Read all answers online: https://www.dearimgui.com/faq, or in docs/FAQ.md (with a Markdown viewer)
59
60CODE
61(search for "[SECTION]" in the code to find them)
62
63// [SECTION] INCLUDES
64// [SECTION] FORWARD DECLARATIONS
65// [SECTION] CONTEXT AND MEMORY ALLOCATORS
66// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO)
67// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
68// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
69// [SECTION] MISC HELPERS/UTILITIES (File functions)
70// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
71// [SECTION] MISC HELPERS/UTILITIES (Color functions)
72// [SECTION] ImGuiStorage
73// [SECTION] ImGuiTextFilter
74// [SECTION] ImGuiTextBuffer, ImGuiTextIndex
75// [SECTION] ImGuiListClipper
76// [SECTION] STYLING
77// [SECTION] RENDER HELPERS
78// [SECTION] INITIALIZATION, SHUTDOWN
79// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
80// [SECTION] ID STACK
81// [SECTION] INPUTS
82// [SECTION] ERROR CHECKING, STATE RECOVERY
83// [SECTION] ITEM SUBMISSION
84// [SECTION] LAYOUT
85// [SECTION] SCROLLING
86// [SECTION] TOOLTIPS
87// [SECTION] POPUPS
88// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
89// [SECTION] DRAG AND DROP
90// [SECTION] LOGGING/CAPTURING
91// [SECTION] SETTINGS
92// [SECTION] LOCALIZATION
93// [SECTION] VIEWPORTS, PLATFORM WINDOWS
94// [SECTION] DOCKING
95// [SECTION] PLATFORM DEPENDENT HELPERS
96// [SECTION] METRICS/DEBUGGER WINDOW
97// [SECTION] DEBUG LOG WINDOW
98// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)
99
100*/
101
102//-----------------------------------------------------------------------------
103// DOCUMENTATION
104//-----------------------------------------------------------------------------
105
106/*
107
108 MISSION STATEMENT
109 =================
110
111 - Easy to use to create code-driven and data-driven tools.
112 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
113 - Easy to hack and improve.
114 - Minimize setup and maintenance.
115 - Minimize state storage on user side.
116 - Minimize state synchronization.
117 - Portable, minimize dependencies, run on target (consoles, phones, etc.).
118 - Efficient runtime and memory consumption.
119
120 Designed primarily for developers and content-creators, not the typical end-user!
121 Some of the current weaknesses (which we aim to address in the future) includes:
122
123 - Doesn't look fancy.
124 - Limited layout features, intricate layouts are typically crafted in code.
125
126
127 CONTROLS GUIDE
128 ==============
129
130 - MOUSE CONTROLS
131 - Mouse wheel: Scroll vertically.
132 - SHIFT+Mouse wheel: Scroll horizontally.
133 - Click [X]: Close a window, available when 'bool* p_open' is passed to ImGui::Begin().
134 - Click ^, Double-Click title: Collapse window.
135 - Drag on corner/border: Resize window (double-click to auto fit window to its contents).
136 - Drag on any empty space: Move window (unless io.ConfigWindowsMoveFromTitleBarOnly = true).
137 - Left-click outside popup: Close popup stack (right-click over underlying popup: Partially close popup stack).
138
139 - TEXT EDITOR
140 - Hold SHIFT or Drag Mouse: Select text.
141 - CTRL+Left/Right: Word jump.
142 - CTRL+Shift+Left/Right: Select words.
143 - CTRL+A or Double-Click: Select All.
144 - CTRL+X, CTRL+C, CTRL+V: Use OS clipboard.
145 - CTRL+Z, CTRL+Y: Undo, Redo.
146 - ESCAPE: Revert text to its original value.
147 - On OSX, controls are automatically adjusted to match standard OSX text editing 2ts and behaviors.
148
149 - KEYBOARD CONTROLS
150 - Basic:
151 - Tab, SHIFT+Tab Cycle through text editable fields.
152 - CTRL+Tab, CTRL+Shift+Tab Cycle through windows.
153 - CTRL+Click Input text into a Slider or Drag widget.
154 - Extended features with `io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard`:
155 - Tab, SHIFT+Tab: Cycle through every items.
156 - Arrow keys Move through items using directional navigation. Tweak value.
157 - Arrow keys + Alt, Shift Tweak slower, tweak faster (when using arrow keys).
158 - Enter Activate item (prefer text input when possible).
159 - Space Activate item (prefer tweaking with arrows when possible).
160 - Escape Deactivate item, leave child window, close popup.
161 - Page Up, Page Down Previous page, next page.
162 - Home, End Scroll to top, scroll to bottom.
163 - Alt Toggle between scrolling layer and menu layer.
164 - CTRL+Tab then Ctrl+Arrows Move window. Hold SHIFT to resize instead of moving.
165 - Output when ImGuiConfigFlags_NavEnableKeyboard set,
166 - io.WantCaptureKeyboard flag is set when keyboard is claimed.
167 - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
168 - io.NavVisible: true when the navigation cursor is visible (usually goes to back false when mouse is used).
169
170 - GAMEPAD CONTROLS
171 - Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
172 - Particularly useful to use Dear ImGui on a console system (e.g. PlayStation, Switch, Xbox) without a mouse!
173 - Download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets
174 - Backend support: backend needs to:
175 - Set 'io.BackendFlags |= ImGuiBackendFlags_HasGamepad' + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys.
176 - For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly.
177 Backend 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.).
178 - If you need to share inputs between your game and the Dear ImGui interface, the easiest approach is to go all-or-nothing,
179 with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
180
181 - REMOTE INPUTS SHARING & MOUSE EMULATION
182 - PS4/PS5 users: Consider emulating a mouse cursor with DualShock touch pad or a spare analog stick as a mouse-emulation fallback.
183 - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + run examples/libs/synergy/uSynergy.c (on your console/tablet/phone app)
184 in order to share your PC mouse/keyboard.
185 - See https://github.com/ocornut/imgui/wiki/Useful-Extensions#remoting for other remoting solutions.
186 - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the io.ConfigNavMoveSetMousePos flag.
187 Enabling io.ConfigNavMoveSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs Dear ImGui to move your mouse cursor along with navigation movements.
188 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.
189 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.
190 (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, Dear ImGui will misbehave as it will see your mouse moving back & forth!)
191 (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
192 to set a boolean to ignore your other external mouse positions until the external source is moved again.)
193
194
195 PROGRAMMER GUIDE
196 ================
197
198 READ FIRST
199 ----------
200 - Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki)
201 - Your code creates the UI every frame of your application loop, if your code doesn't run the UI is gone!
202 The UI can be highly dynamic, there are no construction or destruction steps, less superfluous
203 data retention on your side, less state duplication, less state synchronization, fewer bugs.
204 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
205 Or browse https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html for interactive web version.
206 - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
207 - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
208 You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki.
209 - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
210 For every application frame, your UI code will be called only once. This is in contrast to e.g. Unity's implementation of an IMGUI,
211 where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
212 - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
213 - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
214 If you get an assert, read the messages and comments around the assert.
215 - This codebase aims to be highly optimized:
216 - A typical idle frame should never call malloc/free.
217 - We rely on a maximum of constant-time or O(N) algorithms. Limiting searches/scans as much as possible.
218 - We put particular energy in making sure performances are decent with typical "Debug" build settings as well.
219 Which mean we tend to avoid over-relying on "zero-cost abstraction" as they aren't zero-cost at all.
220 - This codebase aims to be both highly opinionated and highly flexible:
221 - This code works because of the things it choose to solve or not solve.
222 - C++: this is a pragmatic C-ish codebase: we don't use fancy C++ features, we don't include C++ headers,
223 and ImGui:: is a namespace. We rarely use member functions (and when we did, I am mostly regretting it now).
224 This is to increase compatibility, increase maintainability and facilitate use from other languages.
225 - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
226 See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
227 We can can optionally export math operators for ImVec2/ImVec4 using IMGUI_DEFINE_MATH_OPERATORS, which we use internally.
228 - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction
229 (so don't use ImVector in your code or at our own risk!).
230 - Building: We don't use nor mandate a build system for the main library.
231 This is in an effort to ensure that it works in the real world aka with any esoteric build setup.
232 This is also because providing a build system for the main library would be of little-value.
233 The build problems are almost never coming from the main library but from specific backends.
234
235
236 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
237 ----------------------------------------------
238 - Update submodule or copy/overwrite every file.
239 - About imconfig.h:
240 - You may modify your copy of imconfig.h, in this case don't overwrite it.
241 - or you may locally branch to modify imconfig.h and merge/rebase latest.
242 - or you may '#define IMGUI_USER_CONFIG "my_config_file.h"' globally from your build system to
243 specify a custom path for your imconfig.h file and instead not have to modify the default one.
244
245 - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h)
246 - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master".
247 - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file.
248 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
249 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
250 from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
251 likely be a comment about it. Please report any issue to the GitHub page!
252 - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file.
253 - Try to keep your copy of Dear ImGui reasonably up to date!
254
255
256 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
257 ---------------------------------------------------------------
258 - See https://github.com/ocornut/imgui/wiki/Getting-Started.
259 - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
260 - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder.
261 - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system.
262 It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL).
263 - 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.
264 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
265 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
266 Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
267 phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui::Render().
268 - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code.
269 - 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.
270
271
272 HOW A SIMPLE APPLICATION MAY LOOK LIKE
273 --------------------------------------
274 EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder).
275 The sub-folders in examples/ contain examples applications following this structure.
276
277 // Application init: create a dear imgui context, setup some options, load fonts
278 ImGui::CreateContext();
279 ImGuiIO& io = ImGui::GetIO();
280 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
281 // TODO: Fill optional fields of the io structure later.
282 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
283
284 // Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
285 ImGui_ImplWin32_Init(hwnd);
286 ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
287
288 // Application main loop
289 while (true)
290 {
291 // Feed inputs to dear imgui, start new frame
292 ImGui_ImplDX11_NewFrame();
293 ImGui_ImplWin32_NewFrame();
294 ImGui::NewFrame();
295
296 // Any application code here
297 ImGui::Text("Hello, world!");
298
299 // Render dear imgui into screen
300 ImGui::Render();
301 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
302 g_pSwapChain->Present(1, 0);
303 }
304
305 // Shutdown
306 ImGui_ImplDX11_Shutdown();
307 ImGui_ImplWin32_Shutdown();
308 ImGui::DestroyContext();
309
310 EXHIBIT 2: IMPLEMENTING CUSTOM BACKEND / CUSTOM ENGINE
311
312 // Application init: create a dear imgui context, setup some options, load fonts
313 ImGui::CreateContext();
314 ImGuiIO& io = ImGui::GetIO();
315 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
316 // TODO: Fill optional fields of the io structure later.
317 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
318
319 // Build and load the texture atlas into a texture
320 // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
321 int width, height;
322 unsigned char* pixels = nullptr;
323 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
324
325 // At this point you've got the texture data and you need to upload that to your graphic system:
326 // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
327 // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
328 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
329 io.Fonts->SetTexID((void*)texture);
330
331 // Application main loop
332 while (true)
333 {
334 // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
335 // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends)
336 io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds)
337 io.DisplaySize.x = 1920.0f; // set the current display width
338 io.DisplaySize.y = 1280.0f; // set the current display height here
339 io.AddMousePosEvent(mouse_x, mouse_y); // update mouse position
340 io.AddMouseButtonEvent(0, mouse_b[0]); // update mouse button states
341 io.AddMouseButtonEvent(1, mouse_b[1]); // update mouse button states
342
343 // Call NewFrame(), after this point you can use ImGui::* functions anytime
344 // (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere)
345 ImGui::NewFrame();
346
347 // Most of your application code here
348 ImGui::Text("Hello, world!");
349 MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
350 MyGameRender(); // may use any Dear ImGui functions as well!
351
352 // Render dear imgui, swap buffers
353 // (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)
354 ImGui::EndFrame();
355 ImGui::Render();
356 ImDrawData* draw_data = ImGui::GetDrawData();
357 MyImGuiRenderFunction(draw_data);
358 SwapBuffers();
359 }
360
361 // Shutdown
362 ImGui::DestroyContext();
363
364 To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application,
365 you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
366 Please read the FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" about this.
367
368
369 HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
370 ---------------------------------------------
371 The backends in impl_impl_XXX.cpp files contain many working implementations of a rendering function.
372
373 void MyImGuiRenderFunction(ImDrawData* draw_data)
374 {
375 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
376 // TODO: Setup texture sampling state: sample with bilinear filtering (NOT point/nearest filtering). Use 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines;' to allow point/nearest filtering.
377 // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
378 // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
379 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
380 ImVec2 clip_off = draw_data->DisplayPos;
381 for (int n = 0; n < draw_data->CmdListsCount; n++)
382 {
383 const ImDrawList* cmd_list = draw_data->CmdLists[n];
384 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by Dear ImGui
385 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by Dear ImGui
386 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
387 {
388 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
389 if (pcmd->UserCallback)
390 {
391 pcmd->UserCallback(cmd_list, pcmd);
392 }
393 else
394 {
395 // Project scissor/clipping rectangles into framebuffer space
396 ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
397 ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
398 if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
399 continue;
400
401 // We are using scissoring to clip some objects. All low-level graphics API should support it.
402 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
403 // (some elements visible outside their bounds) but you can fix that once everything else works!
404 // - Clipping coordinates are provided in imgui coordinates space:
405 // - For a given viewport, draw_data->DisplayPos == viewport->Pos and draw_data->DisplaySize == viewport->Size
406 // - In a single viewport application, draw_data->DisplayPos == (0,0) and draw_data->DisplaySize == io.DisplaySize, but always use GetMainViewport()->Pos/Size instead of hardcoding those values.
407 // - In the interest of supporting multi-viewport applications (see 'docking' branch on github),
408 // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
409 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
410 MyEngineSetScissor(clip_min.x, clip_min.y, clip_max.x, clip_max.y);
411
412 // The texture for the draw call is specified by pcmd->GetTexID().
413 // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
414 MyEngineBindTexture((MyTexture*)pcmd->GetTexID());
415
416 // Render 'pcmd->ElemCount/3' indexed triangles.
417 // 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.
418 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer + pcmd->IdxOffset, vtx_buffer, pcmd->VtxOffset);
419 }
420 }
421 }
422 }
423
424
425 API BREAKING CHANGES
426 ====================
427
428 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
429 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.
430 When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
431 You can read releases logs https://github.com/ocornut/imgui/releases for more details.
432
433(Docking/Viewport Branch)
434 - 2024/XX/XX (1.XXXX) - when multi-viewports are enabled, all positions will be in your natural OS coordinates space. It means that:
435 - reference to hard-coded positions such as in SetNextWindowPos(ImVec2(0,0)) are probably not what you want anymore.
436 you may use GetMainViewport()->Pos to offset hard-coded positions, e.g. SetNextWindowPos(GetMainViewport()->Pos)
437 - likewise io.MousePos and GetMousePos() will use OS coordinates.
438 If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos.
439
440 - 2024/11/27 (1.91.6) - changed CRC32 table from CRC32-adler to CRC32c polynomial in order to be compatible with the result of SSE 4.2 instructions.
441 As a result, old .ini data may be partially lost (docking and tables information particularly).
442 Because some users have crafted and storing .ini data as a way to workaround limitations of the docking API, we are providing a '#define IMGUI_USE_LEGACY_CRC32_ADLER' compile-time option to keep using old CRC32 tables if you cannot afford invalidating old .ini data.
443 - 2024/11/06 (1.91.5) - commented/obsoleted out pre-1.87 IO system (equivalent to using IMGUI_DISABLE_OBSOLETE_KEYIO or IMGUI_DISABLE_OBSOLETE_FUNCTIONS before)
444 - io.KeyMap[] and io.KeysDown[] are removed (obsoleted February 2022).
445 - io.NavInputs[] and ImGuiNavInput are removed (obsoleted July 2022).
446 - pre-1.87 backends are not supported:
447 - backends need to call io.AddKeyEvent(), io.AddMouseEvent() instead of writing to io.KeysDown[], io.MouseDown[] fields.
448 - backends need to call io.AddKeyAnalogEvent() for gamepad values instead of writing to io.NavInputs[] fields.
449 - for more reference:
450 - read 1.87 and 1.88 part of this section or read Changelog for 1.87 and 1.88.
451 - read https://github.com/ocornut/imgui/issues/4921
452 - if you have trouble updating a very old codebase using legacy backend-specific key codes: consider updating to 1.91.4 first, then #define IMGUI_DISABLE_OBSOLETE_KEYIO, then update to latest.
453 - obsoleted ImGuiKey_COUNT (it is unusually error-prone/misleading since valid keys don't start at 0). probably use ImGuiKey_NamedKey_BEGIN/ImGuiKey_NamedKey_END?
454 - fonts: removed const qualifiers from most font functions in prevision for upcoming font improvements.
455 - 2024/10/18 (1.91.4) - renamed ImGuiCol_NavHighlight to ImGuiCol_NavCursor (for consistency with newly exposed and reworked features). Kept inline redirection enum (will obsolete).
456 - 2024/10/14 (1.91.4) - moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool.
457 moved ImGuiConfigFlags_NavNoCaptureKeyboard to standalone io.ConfigNavCaptureKeyboard bool (note the inverted value!).
458 kept legacy names (will obsolete) + code that copies settings once the first time. Dynamically changing the old value won't work. Switch to using the new value!
459 - 2024/10/10 (1.91.4) - the typedef for ImTextureID now defaults to ImU64 instead of void*. (#1641)
460 this removes the requirement to redefine it for backends which are e.g. storing descriptor sets or other 64-bits structures when building on 32-bits archs. It therefore simplify various building scripts/helpers.
461 you may have compile-time issues if you were casting to 'void*' instead of 'ImTextureID' when passing your types to functions taking ImTextureID values, e.g. ImGui::Image().
462 in doubt it is almost always better to do an intermediate intptr_t cast, since it allows casting any pointer/integer type without warning:
463 - May warn: ImGui::Image((void*)MyTextureData, ...);
464 - May warn: ImGui::Image((void*)(intptr_t)MyTextureData, ...);
465 - Won't warn: ImGui::Image((ImTextureID)(intptr_t)MyTextureData), ...);
466 - note that you can always define ImTextureID to be your own high-level structures (with dedicated constructors) if you like.
467 - 2024/10/03 (1.91.3) - drags: treat v_min==v_max as a valid clamping range when != 0.0f. Zero is a still special value due to legacy reasons, unless using ImGuiSliderFlags_ClampZeroRange. (#7968, #3361, #76)
468 - drags: extended behavior of ImGuiSliderFlags_AlwaysClamp to include _ClampZeroRange. It considers v_min==v_max==0.0f as a valid clamping range (aka edits not allowed).
469 although unlikely, it you wish to only clamp on text input but want v_min==v_max==0.0f to mean unclamped drags, you can use _ClampOnInput instead of _AlwaysClamp. (#7968, #3361, #76)
470 - 2024/09/10 (1.91.2) - internals: using multiple overlayed ButtonBehavior() with same ID will now have io.ConfigDebugHighlightIdConflicts=true feature emit a warning. (#8030)
471 it was one of the rare case where using same ID is legal. workarounds: (1) use single ButtonBehavior() call with multiple _MouseButton flags, or (2) surround the calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()
472 - 2024/08/23 (1.91.1) - renamed ImGuiChildFlags_Border to ImGuiChildFlags_Borders for consistency. kept inline redirection flag.
473 - 2024/08/22 (1.91.1) - moved some functions from ImGuiIO to ImGuiPlatformIO structure:
474 - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn + changed 'void* user_data' to 'ImGuiContext* ctx'. Pull your user data from platform_io.ClipboardUserData.
475 - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn + same as above line.
476 - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn (#7660)
477 - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn
478 - io.PlatformLocaleDecimalPoint -> platform_io.Platform_LocaleDecimalPoint (#7389, #6719, #2278)
479 - access those via GetPlatformIO() instead of GetIO().
480 some were introduced very recently and often automatically setup by core library and backends, so for those we are exceptionally not maintaining a legacy redirection symbol.
481 - commented the old ImageButton() signature obsoleted in 1.89 (~August 2022). As a reminder:
482 - old ImageButton() before 1.89 used ImTextureId as item id (created issue with e.g. multiple buttons in same scope, transient texture id values, opaque computation of ID)
483 - new ImageButton() since 1.89 requires an explicit 'const char* str_id'
484 - old ImageButton() before 1.89 had frame_padding' override argument.
485 - new ImageButton() since 1.89 always use style.FramePadding, which you can freely override with PushStyleVar()/PopStyleVar().
486 - 2024/07/25 (1.91.0) - obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). (see #7838 on GitHub for more info)
487 you should never need those functions. you can do everything with GetCursorScreenPos() and GetContentRegionAvail() in a more simple way.
488 - instead of: GetWindowContentRegionMax().x - GetCursorPos().x
489 - you can use: GetContentRegionAvail().x
490 - instead of: GetWindowContentRegionMax().x + GetWindowPos().x
491 - you can use: GetCursorScreenPos().x + GetContentRegionAvail().x // when called from left edge of window
492 - instead of: GetContentRegionMax()
493 - you can use: GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos() // right edge in local coordinates
494 - instead of: GetWindowContentRegionMax().x - GetWindowContentRegionMin().x
495 - you can use: GetContentRegionAvail() // when called from left edge of window
496 - 2024/07/15 (1.91.0) - renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups. (#1379, #1468, #2200, #4936, #5216, #7302, #7573)
497 (internals: also renamed ImGuiItemFlags_SelectableDontClosePopup into ImGuiItemFlags_AutoClosePopups with inverted behaviors)
498 - 2024/07/15 (1.91.0) - obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag(ImGuiItemFlags_ButtonRepeat, ...)/PopItemFlag().
499 - 2024/07/02 (1.91.0) - commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456)
500 - commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456)
501 - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc.
502 - 2024/07/02 (1.91.0) - IO, IME: renamed platform IME hook and added explicit context for consistency and future-proofness.
503 - old: io.SetPlatformImeDataFn(ImGuiViewport* viewport, ImGuiPlatformImeData* data);
504 - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);
505 - 2024/06/21 (1.90.9) - BeginChild: added ImGuiChildFlags_NavFlattened as a replacement for the window flag ImGuiWindowFlags_NavFlattened: the feature only ever made sense for BeginChild() anyhow.
506 - old: BeginChild("Name", size, 0, ImGuiWindowFlags_NavFlattened);
507 - new: BeginChild("Name", size, ImGuiChildFlags_NavFlattened, 0);
508 - 2024/06/21 (1.90.9) - io: ClearInputKeys() (first exposed in 1.89.8) doesn't clear mouse data, newly added ClearInputMouse() does.
509 - 2024/06/20 (1.90.9) - renamed ImGuiDragDropFlags_SourceAutoExpirePayload to ImGuiDragDropFlags_PayloadAutoExpire.
510 - 2024/06/18 (1.90.9) - style: renamed ImGuiCol_TabActive -> ImGuiCol_TabSelected, ImGuiCol_TabUnfocused -> ImGuiCol_TabDimmed, ImGuiCol_TabUnfocusedActive -> ImGuiCol_TabDimmedSelected.
511 - 2024/06/10 (1.90.9) - removed old nested structure: renaming ImGuiStorage::ImGuiStoragePair type to ImGuiStoragePair (simpler for many languages).
512 - 2024/06/06 (1.90.8) - reordered ImGuiInputTextFlags values. This should not be breaking unless you are using generated headers that have values not matching the main library.
513 - 2024/06/06 (1.90.8) - removed 'ImGuiButtonFlags_MouseButtonDefault_ = ImGuiButtonFlags_MouseButtonLeft', was mostly unused and misleading.
514 - 2024/05/27 (1.90.7) - commented out obsolete symbols marked obsolete in 1.88 (May 2022):
515 - old: CaptureKeyboardFromApp(bool)
516 - new: SetNextFrameWantCaptureKeyboard(bool)
517 - old: CaptureMouseFromApp(bool)
518 - new: SetNextFrameWantCaptureMouse(bool)
519 - 2024/05/22 (1.90.7) - inputs (internals): renamed ImGuiKeyOwner_None to ImGuiKeyOwner_NoOwner, to make use more explicit and reduce confusion with the default it is a non-zero value and cannot be the default value (never made public, but disclosing as I expect a few users caught on owner-aware inputs).
520 - inputs (internals): renamed ImGuiInputFlags_RouteGlobalLow -> ImGuiInputFlags_RouteGlobal, ImGuiInputFlags_RouteGlobal -> ImGuiInputFlags_RouteGlobalOverFocused, ImGuiInputFlags_RouteGlobalHigh -> ImGuiInputFlags_RouteGlobalHighest.
521 - inputs (internals): Shortcut(), SetShortcutRouting(): swapped last two parameters order in function signatures:
522 - old: Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0);
523 - new: Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0, ImGuiID owner_id = 0);
524 - inputs (internals): owner-aware versions of IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(): swapped last two parameters order in function signatures.
525 - old: IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
526 - new: IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0);
527 - old: IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0);
528 - new: IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0);
529 for various reasons those changes makes sense. They are being made because making some of those API public.
530 only past users of imgui_internal.h with the extra parameters will be affected. Added asserts for valid flags in various functions to detect _some_ misuses, BUT NOT ALL.
531 - 2024/05/21 (1.90.7) - docking: changed signature of DockSpaceOverViewport() to add explicit dockspace id if desired. pass 0 to use old behavior. (#7611)
532 - old: DockSpaceOverViewport(const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, ...);
533 - new: DockSpaceOverViewport(ImGuiID dockspace_id = 0, const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, ...);
534 - 2024/05/16 (1.90.7) - inputs: on macOS X, Cmd and Ctrl keys are now automatically swapped by io.AddKeyEvent() as this naturally align with how macOS X uses those keys.
535 - it shouldn't really affect you unless you had custom shortcut swapping in place for macOS X apps.
536 - removed ImGuiMod_Shortcut which was previously dynamically remapping to Ctrl or Cmd/Super. It is now unnecessary to specific cross-platform idiomatic shortcuts. (#2343, #4084, #5923, #456)
537 - 2024/05/14 (1.90.7) - backends: SDL_Renderer2 and SDL_Renderer3 backend now take a SDL_Renderer* in their RenderDrawData() functions.
538 - 2024/04/18 (1.90.6) - TreeNode: Fixed a layout inconsistency when using an empty/hidden label followed by a SameLine() call. (#7505, #282)
539 - old: TreeNode("##Hidden"); SameLine(); Text("Hello"); // <-- This was actually incorrect! BUT appeared to look ok with the default style where ItemSpacing.x == FramePadding.x * 2 (it didn't look aligned otherwise).
540 - new: TreeNode("##Hidden"); SameLine(0, 0); Text("Hello"); // <-- This is correct for all styles values.
541 with the fix, IF you were successfully using TreeNode("")+SameLine(); you will now have extra spacing between your TreeNode and the following item.
542 You'll need to change the SameLine() call to SameLine(0,0) to remove this extraneous spacing. This seemed like the more sensible fix that's not making things less consistent.
543 (Note: when using this idiom you are likely to also use ImGuiTreeNodeFlags_SpanAvailWidth).
544 - 2024/03/18 (1.90.5) - merged the radius_x/radius_y parameters in ImDrawList::AddEllipse(), AddEllipseFilled() and PathEllipticalArcTo() into a single ImVec2 parameter. Exceptionally, because those functions were added in 1.90, we are not adding inline redirection functions. The transition is easy and should affect few users. (#2743, #7417)
545 - 2024/03/08 (1.90.5) - inputs: more formally obsoleted GetKeyIndex() when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is set. It has been unnecessary and a no-op since 1.87 (it returns the same value as passed when used with a 1.87+ backend using io.AddKeyEvent() function). (#4921)
546 - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX)
547 - 2024/01/15 (1.90.2) - commented out obsolete ImGuiIO::ImeWindowHandle marked obsolete in 1.87, favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'.
548 - 2023/12/19 (1.90.1) - commented out obsolete ImGuiKey_KeyPadEnter redirection to ImGuiKey_KeypadEnter.
549 - 2023/11/06 (1.90.1) - removed CalcListClipping() marked obsolete in 1.86. Prefer using ImGuiListClipper which can return non-contiguous ranges.
550 - 2023/11/05 (1.90.1) - imgui_freetype: commented out ImGuiFreeType::BuildFontAtlas() obsoleted in 1.81. prefer using #define IMGUI_ENABLE_FREETYPE or see commented code for manual calls.
551 - 2023/11/05 (1.90.1) - internals,columns: commented out legacy ImGuiColumnsFlags_XXX symbols redirecting to ImGuiOldColumnsFlags_XXX, obsoleted from imgui_internal.h in 1.80.
552 - 2023/11/09 (1.90.0) - removed IM_OFFSETOF() macro in favor of using offsetof() available in C++11. Kept redirection define (will obsolete).
553 - 2023/11/07 (1.90.0) - removed BeginChildFrame()/EndChildFrame() in favor of using BeginChild() with the ImGuiChildFlags_FrameStyle flag. kept inline redirection function (will obsolete).
554 those functions were merely PushStyle/PopStyle helpers, the removal isn't so much motivated by needing to add the feature in BeginChild(), but by the necessity to avoid BeginChildFrame() signature mismatching BeginChild() signature and features.
555 - 2023/11/02 (1.90.0) - BeginChild: upgraded 'bool border = true' parameter to 'ImGuiChildFlags flags' type, added ImGuiChildFlags_Border equivalent. As with our prior "bool-to-flags" API updates, the ImGuiChildFlags_Border value is guaranteed to be == true forever to ensure a smoother transition, meaning all existing calls will still work.
556 - old: BeginChild("Name", size, true)
557 - new: BeginChild("Name", size, ImGuiChildFlags_Border)
558 - old: BeginChild("Name", size, false)
559 - new: BeginChild("Name", size) or BeginChild("Name", 0) or BeginChild("Name", size, ImGuiChildFlags_None)
560 **AMEND FROM THE FUTURE: from 1.91.1, 'ImGuiChildFlags_Border' is called 'ImGuiChildFlags_Borders'**
561 - 2023/11/02 (1.90.0) - BeginChild: added child-flag ImGuiChildFlags_AlwaysUseWindowPadding as a replacement for the window-flag ImGuiWindowFlags_AlwaysUseWindowPadding: the feature only ever made sense for BeginChild() anyhow.
562 - old: BeginChild("Name", size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding);
563 - new: BeginChild("Name", size, ImGuiChildFlags_AlwaysUseWindowPadding, 0);
564 - 2023/09/27 (1.90.0) - io: removed io.MetricsActiveAllocations introduced in 1.63. Same as 'g.DebugMemAllocCount - g.DebugMemFreeCount' (still displayed in Metrics, unlikely to be accessed by end-user).
565 - 2023/09/26 (1.90.0) - debug tools: Renamed ShowStackToolWindow() ("Stack Tool") to ShowIDStackToolWindow() ("ID Stack Tool"), as earlier name was misleading. Kept inline redirection function. (#4631)
566 - 2023/09/15 (1.90.0) - ListBox, Combo: changed signature of "name getter" callback in old one-liner ListBox()/Combo() apis. kept inline redirection function (will obsolete).
567 - old: bool Combo(const char* label, int* current_item, bool (*getter)(void* user_data, int idx, const char** out_text), ...)
568 - new: bool Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
569 - old: bool ListBox(const char* label, int* current_item, bool (*getting)(void* user_data, int idx, const char** out_text), ...);
570 - new: bool ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
571 - 2023/09/08 (1.90.0) - commented out obsolete redirecting functions:
572 - GetWindowContentRegionWidth() -> use GetWindowContentRegionMax().x - GetWindowContentRegionMin().x. Consider that generally 'GetContentRegionAvail().x' is more useful.
573 - ImDrawCornerFlags_XXX -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details + grep commented names in sources.
574 - commented out runtime support for hardcoded ~0 or 0x01..0x0F rounding flags values for AddRect()/AddRectFilled()/PathRect()/AddImageRounded() -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details
575 - 2023/08/25 (1.89.9) - clipper: Renamed IncludeRangeByIndices() (also called ForceDisplayRangeByIndices() before 1.89.6) to IncludeItemsByIndex(). Kept inline redirection function. Sorry!
576 - 2023/07/12 (1.89.8) - ImDrawData: CmdLists now owned, changed from ImDrawList** to ImVector<ImDrawList*>. Majority of users shouldn't be affected, but you cannot compare to NULL nor reassign manually anymore. Instead use AddDrawList(). (#6406, #4879, #1878)
577 - 2023/06/28 (1.89.7) - overlapping items: obsoleted 'SetItemAllowOverlap()' (called after item) in favor of calling 'SetNextItemAllowOverlap()' (called before item). 'SetItemAllowOverlap()' didn't and couldn't work reliably since 1.89 (2022-11-15).
578 - 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete).
579 - 2023/06/28 (1.89.7) - overlapping items: IsItemHovered() now by default return false when querying an item using AllowOverlap mode which is being overlapped. Use ImGuiHoveredFlags_AllowWhenOverlappedByItem to revert to old behavior.
580 - 2023/06/28 (1.89.7) - overlapping items: Selectable and TreeNode don't allow overlap when active so overlapping widgets won't appear as hovered. While this fixes a common small visual issue, it also means that calling IsItemHovered() after a non-reactive elements - e.g. Text() - overlapping an active one may fail if you don't use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem). (#6610)
581 - 2023/06/20 (1.89.7) - moved io.HoverDelayShort/io.HoverDelayNormal to style.HoverDelayShort/style.HoverDelayNormal. As the fields were added in 1.89 and expected to be left unchanged by most users, or only tweaked once during app initialization, we are exceptionally accepting the breakage.
582 - 2023/05/30 (1.89.6) - backends: renamed "imgui_impl_sdlrenderer.cpp" to "imgui_impl_sdlrenderer2.cpp" and "imgui_impl_sdlrenderer.h" to "imgui_impl_sdlrenderer2.h". This is in prevision for the future release of SDL3.
583 - 2023/05/22 (1.89.6) - listbox: commented out obsolete/redirecting functions that were marked obsolete more than two years ago:
584 - ListBoxHeader() -> use BeginListBox() (note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for reference)
585 - ListBoxFooter() -> use EndListBox()
586 - 2023/05/15 (1.89.6) - clipper: commented out obsolete redirection constructor 'ImGuiListClipper(int items_count, float items_height = -1.0f)' that was marked obsolete in 1.79. Use default constructor + clipper.Begin().
587 - 2023/05/15 (1.89.6) - clipper: renamed ImGuiListClipper::ForceDisplayRangeByIndices() to ImGuiListClipper::IncludeRangeByIndices().
588 - 2023/03/14 (1.89.4) - commented out redirecting enums/functions names that were marked obsolete two years ago:
589 - ImGuiSliderFlags_ClampOnInput -> use ImGuiSliderFlags_AlwaysClamp
590 - ImGuiInputTextFlags_AlwaysInsertMode -> use ImGuiInputTextFlags_AlwaysOverwrite
591 - ImDrawList::AddBezierCurve() -> use ImDrawList::AddBezierCubic()
592 - ImDrawList::PathBezierCurveTo() -> use ImDrawList::PathBezierCubicCurveTo()
593 - 2023/03/09 (1.89.4) - renamed PushAllowKeyboardFocus()/PopAllowKeyboardFocus() to PushTabStop()/PopTabStop(). Kept inline redirection functions (will obsolete).
594 - 2023/03/09 (1.89.4) - tooltips: Added 'bool' return value to BeginTooltip() for API consistency. Please only submit contents and call EndTooltip() if BeginTooltip() returns true. In reality the function will _currently_ always return true, but further changes down the line may change this, best to clarify API sooner.
595 - 2023/02/15 (1.89.4) - moved the optional "courtesy maths operators" implementation from imgui_internal.h in imgui.h.
596 Even though we encourage using your own maths types and operators by setting up IM_VEC2_CLASS_EXTRA,
597 it has been frequently requested by people to use our own. We had an opt-in define which was
598 previously fulfilled in imgui_internal.h. It is now fulfilled in imgui.h. (#6164)
599 - OK: #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui.h" / #include "imgui_internal.h"
600 - Error: #include "imgui.h" / #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui_internal.h"
601 - 2023/02/07 (1.89.3) - backends: renamed "imgui_impl_sdl.cpp" to "imgui_impl_sdl2.cpp" and "imgui_impl_sdl.h" to "imgui_impl_sdl2.h". (#6146) This is in prevision for the future release of SDL3.
602 - 2022/10/26 (1.89) - commented out redirecting OpenPopupContextItem() which was briefly the name of OpenPopupOnItemClick() from 1.77 to 1.79.
603 - 2022/10/12 (1.89) - removed runtime patching of invalid "%f"/"%0.f" format strings for DragInt()/SliderInt(). This was obsoleted in 1.61 (May 2018). See 1.61 changelog for details.
604 - 2022/09/26 (1.89) - renamed and merged keyboard modifiers key enums and flags into a same set. Kept inline redirection enums (will obsolete).
605 - ImGuiKey_ModCtrl and ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl
606 - ImGuiKey_ModShift and ImGuiModFlags_Shift -> ImGuiMod_Shift
607 - ImGuiKey_ModAlt and ImGuiModFlags_Alt -> ImGuiMod_Alt
608 - ImGuiKey_ModSuper and ImGuiModFlags_Super -> ImGuiMod_Super
609 the ImGuiKey_ModXXX were introduced in 1.87 and mostly used by backends.
610 the ImGuiModFlags_XXX have been exposed in imgui.h but not really used by any public api only by third-party extensions.
611 exceptionally commenting out the older ImGuiKeyModFlags_XXX names ahead of obsolescence schedule to reduce confusion and because they were not meant to be used anyway.
612 - 2022/09/20 (1.89) - ImGuiKey is now a typed enum, allowing ImGuiKey_XXX symbols to be named in debuggers.
613 this will require uses of legacy backend-dependent indices to be casted, e.g.
614 - with imgui_impl_glfw: IsKeyPressed(GLFW_KEY_A) -> IsKeyPressed((ImGuiKey)GLFW_KEY_A);
615 - with imgui_impl_win32: IsKeyPressed('A') -> IsKeyPressed((ImGuiKey)'A')
616 - etc. However if you are upgrading code you might well use the better, backend-agnostic IsKeyPressed(ImGuiKey_A) now!
617 - 2022/09/12 (1.89) - removed the bizarre legacy default argument for 'TreePush(const void* ptr = NULL)', always pass a pointer value explicitly. NULL/nullptr is ok but require cast, e.g. TreePush((void*)nullptr);
618 - 2022/09/05 (1.89) - commented out redirecting functions/enums names that were marked obsolete in 1.77 and 1.78 (June 2020):
619 - DragScalar(), DragScalarN(), DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(): For old signatures ending with (..., const char* format, float power = 1.0f) -> use (..., format ImGuiSliderFlags_Logarithmic) if power != 1.0f.
620 - SliderScalar(), SliderScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(): For old signatures ending with (..., const char* format, float power = 1.0f) -> use (..., format ImGuiSliderFlags_Logarithmic) if power != 1.0f.
621 - BeginPopupContextWindow(const char*, ImGuiMouseButton, bool) -> use BeginPopupContextWindow(const char*, ImGuiPopupFlags)
622 - 2022/09/02 (1.89) - obsoleted using SetCursorPos()/SetCursorScreenPos() to extend parent window/cell boundaries.
623 this relates to when moving the cursor position beyond current boundaries WITHOUT submitting an item.
624 - previously this would make the window content size ~200x200:
625 Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();
626 - instead, please submit an item:
627 Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End();
628 - alternative:
629 Begin(...) + Dummy(ImVec2(200,200)) + End();
630 - content size is now only extended when submitting an item!
631 - with '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will now be detected and assert.
632 - without '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will silently be fixed until we obsolete it.
633 - 2022/08/03 (1.89) - changed signature of ImageButton() function. Kept redirection function (will obsolete).
634 - added 'const char* str_id' parameter + removed 'int frame_padding = -1' parameter.
635 - old signature: bool ImageButton(ImTextureID tex_id, ImVec2 size, ImVec2 uv0 = ImVec2(0,0), ImVec2 uv1 = ImVec2(1,1), int frame_padding = -1, ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1));
636 - used the ImTextureID value to create an ID. This was inconsistent with other functions, led to ID conflicts, and caused problems with engines using transient ImTextureID values.
637 - had a FramePadding override which was inconsistent with other functions and made the already-long signature even longer.
638 - new signature: bool ImageButton(const char* str_id, ImTextureID tex_id, ImVec2 size, ImVec2 uv0 = ImVec2(0,0), ImVec2 uv1 = ImVec2(1,1), ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1));
639 - requires an explicit identifier. You may still use e.g. PushID() calls and then pass an empty identifier.
640 - always uses style.FramePadding for padding, to be consistent with other buttons. You may use PushStyleVar() to alter this.
641 - 2022/07/08 (1.89) - inputs: removed io.NavInputs[] and ImGuiNavInput enum (following 1.87 changes).
642 - Official backends from 1.87+ -> no issue.
643 - Official backends from 1.60 to 1.86 -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need updating!
644 - Custom backends not writing to io.NavInputs[] -> no issue.
645 - Custom backends writing to io.NavInputs[] -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need fixing!
646 - TL;DR: Backends should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values instead of filling io.NavInput[].
647 - 2022/06/15 (1.88) - renamed IMGUI_DISABLE_METRICS_WINDOW to IMGUI_DISABLE_DEBUG_TOOLS for correctness. kept support for old define (will obsolete).
648 - 2022/05/03 (1.88) - backends: osx: removed ImGui_ImplOSX_HandleEvent() from backend API in favor of backend automatically handling event capture. All ImGui_ImplOSX_HandleEvent() calls should be removed as they are now unnecessary.
649 - 2022/04/05 (1.88) - inputs: renamed ImGuiKeyModFlags to ImGuiModFlags. Kept inline redirection enums (will obsolete). This was never used in public API functions but technically present in imgui.h and ImGuiIO.
650 - 2022/01/20 (1.87) - inputs: reworded gamepad IO.
651 - Backend writing to io.NavInputs[] -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values.
652 - 2022/01/19 (1.87) - sliders, drags: removed support for legacy arithmetic operators (+,+-,*,/) when inputing text. This doesn't break any api/code but a feature that used to be accessible by end-users (which seemingly no one used).
653 - 2022/01/17 (1.87) - inputs: reworked mouse IO.
654 - Backend writing to io.MousePos -> backend should call io.AddMousePosEvent()
655 - Backend writing to io.MouseDown[] -> backend should call io.AddMouseButtonEvent()
656 - Backend writing to io.MouseWheel -> backend should call io.AddMouseWheelEvent()
657 - Backend writing to io.MouseHoveredViewport -> backend should call io.AddMouseViewportEvent() [Docking branch w/ multi-viewports only]
658 note: for all calls to IO new functions, the Dear ImGui context should be bound/current.
659 read https://github.com/ocornut/imgui/issues/4921 for details.
660 - 2022/01/10 (1.87) - inputs: reworked keyboard IO. Removed io.KeyMap[], io.KeysDown[] in favor of calling io.AddKeyEvent(). Removed GetKeyIndex(), now unnecessary. All IsKeyXXX() functions now take ImGuiKey values. All features are still functional until IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Read Changelog and Release Notes for details.
661 - IsKeyPressed(MY_NATIVE_KEY_XXX) -> use IsKeyPressed(ImGuiKey_XXX)
662 - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX)
663 - Backend writing to io.KeyMap[],io.KeysDown[] -> backend should call io.AddKeyEvent() (+ call io.SetKeyEventNativeData() if you want legacy user code to stil function with legacy key codes).
664 - Backend writing to io.KeyCtrl, io.KeyShift.. -> backend should call io.AddKeyEvent() with ImGuiMod_XXX values. *IF YOU PULLED CODE BETWEEN 2021/01/10 and 2021/01/27: We used to have a io.AddKeyModsEvent() function which was now replaced by io.AddKeyEvent() with ImGuiMod_XXX values.*
665 - one case won't work with backward compatibility: if your custom backend used ImGuiKey as mock native indices (e.g. "io.KeyMap[ImGuiKey_A] = ImGuiKey_A") because those values are now larger than the legacy KeyDown[] array. Will assert.
666 - inputs: added ImGuiKey_ModCtrl/ImGuiKey_ModShift/ImGuiKey_ModAlt/ImGuiKey_ModSuper values to submit keyboard modifiers using io.AddKeyEvent(), instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper.
667 - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum.
668 - 2022/01/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'.
669 - 2022/01/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019)
670 - ImGui::SetNextTreeNodeOpen() -> use ImGui::SetNextItemOpen()
671 - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x
672 - ImGui::TreeAdvanceToLabelPos() -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing());
673 - ImFontAtlas::CustomRect -> use ImFontAtlasCustomRect
674 - ImGuiColorEditFlags_RGB/HSV/HEX -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex
675 - 2021/12/20 (1.86) - backends: removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example. Find last supported version at https://github.com/ocornut/imgui/wiki/Bindings
676 - 2021/11/04 (1.86) - removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function.
677 - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful.
678 - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019):
679 - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList()
680 - ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder
681 - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID().
682 - if you are using official backends from the source tree: you have nothing to do.
683 - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID().
684 - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags.
685 - ImDrawCornerFlags_TopLeft -> use ImDrawFlags_RoundCornersTopLeft
686 - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight
687 - ImDrawCornerFlags_None -> use ImDrawFlags_RoundCornersNone etc.
688 flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API.
689 breaking: the default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners":
690 - rounding == 0.0f + flags == 0 --> meant no rounding --> unchanged (common use)
691 - rounding > 0.0f + flags != 0 --> meant rounding --> unchanged (common use)
692 - rounding == 0.0f + flags != 0 --> meant no rounding --> unchanged (unlikely use)
693 - rounding > 0.0f + flags == 0 --> meant no rounding --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f.
694 this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok.
695 the old ImDrawCornersFlags used awkward default values of ~0 or 0xF (4 lower bits set) to signify "round all corners" and we sometimes encouraged using them as shortcuts.
696 legacy path still support use of hard coded ~0 or any value from 0x1 or 0xF. They will behave the same with legacy paths enabled (will assert otherwise).
697 - 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018):
698 - ImGui::SetScrollHere() -> use ImGui::SetScrollHereY()
699 - 2021/03/11 (1.82) - clarified that ImDrawList::PathArcTo(), ImDrawList::PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would generally lead to counter-clockwise paths and have an effect on anti-aliasing.
700 - 2021/03/10 (1.82) - upgraded ImDrawList::AddPolyline() and PathStroke() "bool closed" parameter to "ImDrawFlags flags". The matching ImDrawFlags_Closed value is guaranteed to always stay == 1 in the future.
701 - 2021/02/22 (1.82) - (*undone in 1.84*) win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'.
702 - 2021/02/17 (1.82) - renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as the meaning of the value changed.
703 - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete).
704 - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete).
705 - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete).
706 - 2021/01/26 (1.81) - removed ImGuiFreeType::BuildFontAtlas(). Kept inline redirection function. Prefer using '#define IMGUI_ENABLE_FREETYPE', but there's a runtime selection path available too. The shared extra flags parameters (very rarely used) are now stored in ImFontAtlas::FontBuilderFlags.
707 - renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags.
708 - renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API.
709 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018):
710 - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit().
711 - ImGuiCol_ModalWindowDarkening -> use ImGuiCol_ModalWindowDimBg
712 - ImGuiInputTextCallback -> use ImGuiTextEditCallback
713 - ImGuiInputTextCallbackData -> use ImGuiTextEditCallbackData
714 - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete).
715 - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added!
716 - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API.
717 - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures
718 - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/.
719 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018):
720 - io.RenderDrawListsFn pointer -> use ImGui::GetDrawData() value and call the render function of your backend
721 - ImGui::IsAnyWindowFocused() -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)
722 - ImGui::IsAnyWindowHovered() -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
723 - ImGuiStyleVar_Count_ -> use ImGuiStyleVar_COUNT
724 - ImGuiMouseCursor_Count_ -> use ImGuiMouseCursor_COUNT
725 - removed redirecting functions names that were marked obsolete in 1.61 (May 2018):
726 - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision.
727 - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter.
728 - 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).
729 - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently).
730 - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton.
731 - 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.
732 - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on an 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.
733 - 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!
734 - 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().
735 replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
736 worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
737 - if you omitted the 'power' parameter (likely!), you are not affected.
738 - 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.
739 - 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.
740 see https://github.com/ocornut/imgui/issues/3361 for all details.
741 kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version was removed directly as they were most unlikely ever used.
742 for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.
743 - 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.
744 - 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.
745 - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79]
746 - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
747 - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
748 - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
749 - 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.
750 - 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.
751 - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
752 - ShowTestWindow() -> use ShowDemoWindow()
753 - IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
754 - IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
755 - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
756 - GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing()
757 - ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg
758 - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding
759 - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
760 - IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS
761 - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was vaguely documented and rarely if ever used). Instead, we added an explicit PrimUnreserve() API.
762 - 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).
763 - 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.
764 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
765 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
766 - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
767 - Begin() [old 5 args version] -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
768 - IsRootWindowOrAnyChildHovered() -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
769 - AlignFirstTextHeightToWidgets() -> use AlignTextToFramePadding()
770 - SetNextWindowPosCenter() -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
771 - ImFont::Glyph -> use ImFontGlyph
772 - 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.
773 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.
774 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).
775 If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
776 - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
777 - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
778 - 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.
779 - 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
780 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.
781 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.
782 Please reach out if you are affected.
783 - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
784 - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
785 - 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.
786 - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
787 - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
788 - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
789 - 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 arbitrarily small value!
790 - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
791 - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
792 - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
793 - 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.
794 - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
795 - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
796 - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
797 - 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.
798 If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
799 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
800 - 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.
801 NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
802 Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
803 - 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).
804 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
805 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
806 - 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.
807 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
808 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
809 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
810 - 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.).
811 old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports.
812 when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call.
813 in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
814 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
815 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
816 - 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.
817 If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
818 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.
819 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.
820 - 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",
821 consistent with other functions. Kept redirection functions (will obsolete).
822 - 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.
823 - 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).
824 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
825 - 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.
826 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
827 - 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.
828 - 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.
829 - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
830 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
831 - removed Shutdown() function, as DestroyContext() serve this purpose.
832 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
833 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
834 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
835 - 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.
836 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
837 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
838 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
839 - 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.
840 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
841 - 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
842 - 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.
843 - 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.
844 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
845 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
846 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
847 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
848 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
849 - 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.
850 - 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.
851 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.
852 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
853 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
854 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
855 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
856 - 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.
857 - 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.
858 - 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.
859 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
860 IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
861 IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
862 IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
863 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
864 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
865 - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
866 - 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).
867 - 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)".
868 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
869 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
870 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
871 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
872 - 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.
873 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
874 - 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.
875 - 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).
876 - 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).
877 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
878 - 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.
879 - 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.
880 - 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))'
881 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
882 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
883 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
884 - 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().
885 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
886 - 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.
887 - 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.
888 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
889 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 to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
890 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:
891 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); }
892 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.
893 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
894 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
895 - 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).
896 - 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.
897 - 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).
898 - 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)
899 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
900 - 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.
901 - 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.
902 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
903 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
904 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
905 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.
906 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!
907 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
908 - 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.
909 - 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
910 - 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.
911 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
912 - 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.
913 this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
914 - 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.
915 - the signature of the io.RenderDrawListsFn handler has changed!
916 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
917 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
918 parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
919 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
920 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
921 - 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.
922 - 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!
923 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
924 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
925 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
926 - 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.
927 - 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
928 - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely used. Sorry!
929 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
930 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
931 - 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.
932 - 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.
933 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
934 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
935 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
936 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
937 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
938 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
939 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
940 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
941 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
942 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
943 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
944 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
945 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
946 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
947 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
948 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
949 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
950 - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
951 - old: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
952 - new: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier);
953 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.
954 - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to call io.Fonts->SetTexID()
955 - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
956 - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
957 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
958 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
959 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
960 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
961 - 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)
962 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
963 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
964 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
965 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
966 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
967 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
968
969
970 FREQUENTLY ASKED QUESTIONS (FAQ)
971 ================================
972
973 Read all answers online:
974 https://www.dearimgui.com/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
975 Read all answers locally (with a text editor or ideally a Markdown viewer):
976 docs/FAQ.md
977 Some answers are copied down here to facilitate searching in code.
978
979 Q&A: Basics
980 ===========
981
982 Q: Where is the documentation?
983 A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++.
984 - Run the examples/ applications and explore them.
985 - Read Getting Started (https://github.com/ocornut/imgui/wiki/Getting-Started) guide.
986 - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
987 - The demo covers most features of Dear ImGui, so you can read the code and see its output.
988 - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
989 - 20+ standalone example applications using e.g. OpenGL/DirectX are provided in the
990 examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
991 - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
992 - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
993 - Your programming IDE is your friend, find the type or function declaration to find comments
994 associated with it.
995
996 Q: What is this library called?
997 Q: Which version should I get?
998 >> This library is called "Dear ImGui", please don't call it "ImGui" :)
999 >> See https://www.dearimgui.com/faq for details.
1000
1001 Q&A: Integration
1002 ================
1003
1004 Q: How to get started?
1005 A: Read https://github.com/ocornut/imgui/wiki/Getting-Started. Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
1006
1007 Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?
1008 A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
1009 >> See https://www.dearimgui.com/faq for a fully detailed answer. You really want to read this.
1010
1011 Q. How can I enable keyboard or gamepad controls?
1012 Q: How can I use this on a machine without mouse, keyboard or screen? (input share, remote display)
1013 Q: I integrated Dear ImGui in my engine and little squares are showing instead of text...
1014 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...
1015 Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...
1016 >> See https://www.dearimgui.com/faq
1017
1018 Q&A: Usage
1019 ----------
1020
1021 Q: About the ID Stack system..
1022 - Why is my widget not reacting when I click on it?
1023 - How can I have widgets with an empty label?
1024 - How can I have multiple widgets with the same label?
1025 - How can I have multiple windows with the same label?
1026 Q: How can I display an image? What is ImTextureID, how does it work?
1027 Q: How can I use my own math types instead of ImVec2?
1028 Q: How can I interact with standard C++ types (such as std::string and std::vector)?
1029 Q: How can I display custom shapes? (using low-level ImDrawList API)
1030 >> See https://www.dearimgui.com/faq
1031
1032 Q&A: Fonts, Text
1033 ================
1034
1035 Q: How should I handle DPI in my application?
1036 Q: How can I load a different font than the default?
1037 Q: How can I easily use icons in my application?
1038 Q: How can I load multiple fonts?
1039 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
1040 >> See https://www.dearimgui.com/faq and https://github.com/ocornut/imgui/blob/master/docs/FONTS.md
1041
1042 Q&A: Concerns
1043 =============
1044
1045 Q: Who uses Dear ImGui?
1046 Q: Can you create elaborate/serious tools with Dear ImGui?
1047 Q: Can you reskin the look of Dear ImGui?
1048 Q: Why using C++ (as opposed to C)?
1049 >> See https://www.dearimgui.com/faq
1050
1051 Q&A: Community
1052 ==============
1053
1054 Q: How can I help?
1055 A: - Businesses: please reach out to "omar AT dearimgui DOT com" if you work in a place using Dear ImGui!
1056 We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
1057 This is among the most useful thing you can do for Dear ImGui. With increased funding, we sustain and grow work on this project.
1058 >>> See https://github.com/ocornut/imgui/wiki/Funding
1059 - Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.
1060 - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, and see how you want to help and can help!
1061 - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
1062 You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers.
1063 But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions.
1064 - 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).
1065
1066*/
1067
1068//-------------------------------------------------------------------------
1069// [SECTION] INCLUDES
1070//-------------------------------------------------------------------------
1071
1072#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
1073#define _CRT_SECURE_NO_WARNINGS
1074#endif
1075
1076#ifndef IMGUI_DEFINE_MATH_OPERATORS
1077#define IMGUI_DEFINE_MATH_OPERATORS
1078#endif
1079
1080#include "imgui.h"
1081#ifndef IMGUI_DISABLE
1082#include "imgui_internal.h"
1083
1084// System includes
1085#include <stdio.h> // vsnprintf, sscanf, printf
1086#include <stdint.h> // intptr_t
1087
1088// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
1089#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
1090#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
1091#endif
1092
1093// [Windows] OS specific includes (optional)
1094#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_DEFAULT_SHELL_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
1095#define IMGUI_DISABLE_WIN32_FUNCTIONS
1096#endif
1097#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
1098#ifndef WIN32_LEAN_AND_MEAN
1099#define WIN32_LEAN_AND_MEAN
1100#endif
1101#ifndef NOMINMAX
1102#define NOMINMAX
1103#endif
1104#ifndef __MINGW32__
1105#include <Windows.h> // _wfopen, OpenClipboard
1106#else
1107#include <windows.h>
1108#endif
1109#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP || WINAPI_FAMILY == WINAPI_FAMILY_GAMES)
1110// The UWP and GDK Win32 API subsets don't support clipboard nor IME functions
1111#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
1112#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
1113#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
1114#endif
1115#endif
1116
1117// [Apple] OS specific includes
1118#if defined(__APPLE__)
1119#include <TargetConditionals.h>
1120#endif
1121
1122// Visual Studio warnings
1123#ifdef _MSC_VER
1124#pragma warning (disable: 4127) // condition expression is constant
1125#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
1126#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
1127#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
1128#endif
1129#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to an 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
1130#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
1131#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
1132#endif
1133
1134// Clang/GCC warnings with -Weverything
1135#if defined(__clang__)
1136#if __has_warning("-Wunknown-warning-option")
1137#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!
1138#endif
1139#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
1140#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
1141#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.
1142#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.
1143#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.
1144#pragma clang diagnostic ignored "-Wglobal-constructors" // warning: declaration requires a global destructor // similar to above, not sure what the exact difference is.
1145#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
1146#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.
1147#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type 'int'
1148#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
1149#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.
1150#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
1151#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
1152#pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
1153#elif defined(__GNUC__)
1154// We disable -Wpragmas because GCC doesn't provide a has_warning equivalent and some forks/patches may not follow the warning/version association.
1155#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
1156#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
1157#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
1158#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
1159#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
1160#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
1161#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
1162#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
1163#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
1164#endif
1165
1166// Debug options
1167#define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Hold CTRL to display for all candidates. CTRL+Arrow to change last direction.
1168#define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window
1169
1170// 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.
1171static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in
1172static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
1173
1174static const float NAV_ACTIVATE_HIGHLIGHT_TIMER = 0.10f; // Time to highlight an item activated by a shortcut.
1175
1176// Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend)
1177static const float WINDOWS_HOVER_PADDING = 4.0f; // Extend outside window for hovering/resizing (maxxed with TouchPadding) and inside windows for borders. Affect FindHoveredWindow().
1178static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
1179static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 0.70f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved.
1180
1181// Tooltip offset
1182static const ImVec2 TOOLTIP_DEFAULT_OFFSET_MOUSE = ImVec2(16, 10); // Multiplied by g.Style.MouseCursorScale
1183static const ImVec2 TOOLTIP_DEFAULT_OFFSET_TOUCH = ImVec2(0, -20); // Multiplied by g.Style.MouseCursorScale
1184static const ImVec2 TOOLTIP_DEFAULT_PIVOT_TOUCH = ImVec2(0.5f, 1.0f); // Multiplied by g.Style.MouseCursorScale
1185
1186// Docking
1187static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA = 0.50f; // For use with io.ConfigDockingTransparentPayload. Apply to Viewport _or_ WindowBg in host viewport.
1188
1189//-------------------------------------------------------------------------
1190// [SECTION] FORWARD DECLARATIONS
1191//-------------------------------------------------------------------------
1192
1193static void SetCurrentWindow(ImGuiWindow* window);
1194static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags);
1195static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
1196
1197static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
1198
1199// Settings
1200static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
1201static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
1202static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
1203static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
1204static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
1205
1206// Platform Dependents default implementation for ImGuiPlatformIO functions
1207static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx);
1208static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext* ctx, const char* text);
1209static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);
1210static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext* ctx, const char* path);
1211
1212namespace ImGui
1213{
1214// Item
1215static void ItemHandleShortcut(ImGuiID id);
1216
1217// Navigation
1218static void NavUpdate();
1219static void NavUpdateWindowing();
1220static void NavUpdateWindowingOverlay();
1221static void NavUpdateCancelRequest();
1222static void NavUpdateCreateMoveRequest();
1223static void NavUpdateCreateTabbingRequest();
1224static float NavUpdatePageUpPageDown();
1225static inline void NavUpdateAnyRequestFlag();
1226static void NavUpdateCreateWrappingRequest();
1227static void NavEndFrame();
1228static bool NavScoreItem(ImGuiNavItemData* result);
1229static void NavApplyItemToResult(ImGuiNavItemData* result);
1230static void NavProcessItem();
1231static void NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags);
1232static ImGuiInputSource NavCalcPreferredRefPosSource();
1233static ImVec2 NavCalcPreferredRefPos();
1234static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
1235static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
1236static void NavRestoreLayer(ImGuiNavLayer layer);
1237static int FindWindowFocusIndex(ImGuiWindow* window);
1238
1239// Error Checking and Debug Tools
1240static void ErrorCheckNewFrameSanityChecks();
1241static void ErrorCheckEndFrameSanityChecks();
1242#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1243static void UpdateDebugToolItemPicker();
1244static void UpdateDebugToolStackQueries();
1245static void UpdateDebugToolFlashStyleColor();
1246#endif
1247
1248// Inputs
1249static void UpdateKeyboardInputs();
1250static void UpdateMouseInputs();
1251static void UpdateMouseWheel();
1252static void UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt);
1253
1254// Misc
1255static void UpdateSettings();
1256static int UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
1257static void RenderWindowOuterBorders(ImGuiWindow* window);
1258static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);
1259static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
1260static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col);
1261static void RenderDimmedBackgrounds();
1262static void SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect);
1263
1264// Viewports
1265const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter.
1266static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags);
1267static void DestroyViewport(ImGuiViewportP* viewport);
1268static void UpdateViewportsNewFrame();
1269static void UpdateViewportsEndFrame();
1270static void WindowSelectViewport(ImGuiWindow* window);
1271static void WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_window_in_stack);
1272static bool UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport);
1273static bool UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window);
1274static bool GetWindowAlwaysWantOwnViewport(ImGuiWindow* window);
1275static int FindPlatformMonitorForPos(const ImVec2& pos);
1276static int FindPlatformMonitorForRect(const ImRect& r);
1277static void UpdateViewportPlatformMonitor(ImGuiViewportP* viewport);
1278
1279}
1280
1281//-----------------------------------------------------------------------------
1282// [SECTION] CONTEXT AND MEMORY ALLOCATORS
1283//-----------------------------------------------------------------------------
1284
1285// DLL users:
1286// - Heaps and globals are not shared across DLL boundaries!
1287// - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from.
1288// - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL).
1289// - Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
1290// - Confused? In a debugger: add GImGui to your watch window and notice how its value changes depending on your current location (which DLL boundary you are in).
1291
1292// Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
1293// - ImGui::CreateContext() will automatically set this pointer if it is NULL.
1294// Change to a different context by calling ImGui::SetCurrentContext().
1295// - Important: Dear ImGui functions are not thread-safe because of this pointer.
1296// If you want thread-safety to allow N threads to access N different contexts:
1297// - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h:
1298// struct ImGuiContext;
1299// extern thread_local ImGuiContext* MyImGuiTLS;
1300// #define GImGui MyImGuiTLS
1301// And then define MyImGuiTLS in one of your cpp files. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword.
1302// - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
1303// - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace.
1304// - DLL users: read comments above.
1305#ifndef GImGui
1306ImGuiContext* GImGui = NULL;
1307#endif
1308
1309// Memory Allocator functions. Use SetAllocatorFunctions() to change them.
1310// - You probably don't want to modify that mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction.
1311// - DLL users: read comments above.
1312#ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
1313static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size: size); }
1314static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr: ptr); }
1315#else
1316static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; }
1317static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
1318#endif
1319static ImGuiMemAllocFunc GImAllocatorAllocFunc = MallocWrapper;
1320static ImGuiMemFreeFunc GImAllocatorFreeFunc = FreeWrapper;
1321static void* GImAllocatorUserData = NULL;
1322
1323//-----------------------------------------------------------------------------
1324// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO)
1325//-----------------------------------------------------------------------------
1326
1327ImGuiStyle::ImGuiStyle()
1328{
1329 Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui.
1330 DisabledAlpha = 0.60f; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.
1331 WindowPadding = ImVec2(8,8); // Padding within a window
1332 WindowRounding = 0.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.
1333 WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1334 WindowMinSize = ImVec2(32,32); // Minimum window size
1335 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
1336 WindowMenuButtonPosition = ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
1337 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
1338 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1339 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
1340 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1341 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
1342 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
1343 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
1344 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
1345 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
1346 CellPadding = ImVec2(4,2); // Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows.
1347 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!
1348 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
1349 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
1350 ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
1351 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
1352 GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar
1353 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1354 LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
1355 TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
1356 TabBorderSize = 0.0f; // Thickness of border around tabs.
1357 TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appear 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.
1358 TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus.
1359 TabBarOverlineSize = 2.0f; // Thickness of tab-bar overline, which highlights the selected tab-bar.
1360 TableAngledHeadersAngle = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees).
1361 TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell
1362 ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
1363 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1364 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.
1365 SeparatorTextBorderSize = 3.0f; // Thickness of border in SeparatorText()
1366 SeparatorTextAlign = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center).
1367 SeparatorTextPadding = ImVec2(20.0f,3.f);// Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y.
1368 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.
1369 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.
1370 DockingSeparatorSize = 2.0f; // Thickness of resizing border between docked windows
1371 MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1372 AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
1373 AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).
1374 AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
1375 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.
1376 CircleTessellationMaxError = 0.30f; // 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.
1377
1378 // Behaviors
1379 HoverStationaryDelay = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.
1380 HoverDelayShort = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay.
1381 HoverDelayNormal = 0.40f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). "
1382 HoverFlagsForTooltipMouse = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.
1383 HoverFlagsForTooltipNav = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.
1384
1385 // Default theme
1386 ImGui::StyleColorsDark(dst: this);
1387}
1388
1389// 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.
1390// 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.
1391void ImGuiStyle::ScaleAllSizes(float scale_factor)
1392{
1393 WindowPadding = ImTrunc(v: WindowPadding * scale_factor);
1394 WindowRounding = ImTrunc(f: WindowRounding * scale_factor);
1395 WindowMinSize = ImTrunc(v: WindowMinSize * scale_factor);
1396 ChildRounding = ImTrunc(f: ChildRounding * scale_factor);
1397 PopupRounding = ImTrunc(f: PopupRounding * scale_factor);
1398 FramePadding = ImTrunc(v: FramePadding * scale_factor);
1399 FrameRounding = ImTrunc(f: FrameRounding * scale_factor);
1400 ItemSpacing = ImTrunc(v: ItemSpacing * scale_factor);
1401 ItemInnerSpacing = ImTrunc(v: ItemInnerSpacing * scale_factor);
1402 CellPadding = ImTrunc(v: CellPadding * scale_factor);
1403 TouchExtraPadding = ImTrunc(v: TouchExtraPadding * scale_factor);
1404 IndentSpacing = ImTrunc(f: IndentSpacing * scale_factor);
1405 ColumnsMinSpacing = ImTrunc(f: ColumnsMinSpacing * scale_factor);
1406 ScrollbarSize = ImTrunc(f: ScrollbarSize * scale_factor);
1407 ScrollbarRounding = ImTrunc(f: ScrollbarRounding * scale_factor);
1408 GrabMinSize = ImTrunc(f: GrabMinSize * scale_factor);
1409 GrabRounding = ImTrunc(f: GrabRounding * scale_factor);
1410 LogSliderDeadzone = ImTrunc(f: LogSliderDeadzone * scale_factor);
1411 TabRounding = ImTrunc(f: TabRounding * scale_factor);
1412 TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImTrunc(f: TabMinWidthForCloseButton * scale_factor) : FLT_MAX;
1413 TabBarOverlineSize = ImTrunc(f: TabBarOverlineSize * scale_factor);
1414 SeparatorTextPadding = ImTrunc(v: SeparatorTextPadding * scale_factor);
1415 DockingSeparatorSize = ImTrunc(f: DockingSeparatorSize * scale_factor);
1416 DisplayWindowPadding = ImTrunc(v: DisplayWindowPadding * scale_factor);
1417 DisplaySafeAreaPadding = ImTrunc(v: DisplaySafeAreaPadding * scale_factor);
1418 MouseCursorScale = ImTrunc(f: MouseCursorScale * scale_factor);
1419}
1420
1421ImGuiIO::ImGuiIO()
1422{
1423 // Most fields are initialized with zero
1424 memset(s: this, c: 0, n: sizeof(*this));
1425 IM_STATIC_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT);
1426
1427 // Settings
1428 ConfigFlags = ImGuiConfigFlags_None;
1429 BackendFlags = ImGuiBackendFlags_None;
1430 DisplaySize = ImVec2(-1.0f, -1.0f);
1431 DeltaTime = 1.0f / 60.0f;
1432 IniSavingRate = 5.0f;
1433 IniFilename = "imgui.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables).
1434 LogFilename = "imgui_log.txt";
1435 UserData = NULL;
1436
1437 Fonts = NULL;
1438 FontGlobalScale = 1.0f;
1439 FontDefault = NULL;
1440 FontAllowUserScaling = false;
1441 DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1442
1443 // Keyboard/Gamepad Navigation options
1444 ConfigNavSwapGamepadButtons = false;
1445 ConfigNavMoveSetMousePos = false;
1446 ConfigNavCaptureKeyboard = true;
1447 ConfigNavEscapeClearFocusItem = true;
1448 ConfigNavEscapeClearFocusWindow = false;
1449 ConfigNavCursorVisibleAuto = true;
1450 ConfigNavCursorVisibleAlways = false;
1451
1452 // Docking options (when ImGuiConfigFlags_DockingEnable is set)
1453 ConfigDockingNoSplit = false;
1454 ConfigDockingWithShift = false;
1455 ConfigDockingAlwaysTabBar = false;
1456 ConfigDockingTransparentPayload = false;
1457
1458 // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set)
1459 ConfigViewportsNoAutoMerge = false;
1460 ConfigViewportsNoTaskBarIcon = false;
1461 ConfigViewportsNoDecoration = true;
1462 ConfigViewportsNoDefaultParent = false;
1463
1464 // Miscellaneous options
1465 MouseDrawCursor = false;
1466#ifdef __APPLE__
1467 ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
1468#else
1469 ConfigMacOSXBehaviors = false;
1470#endif
1471 ConfigInputTrickleEventQueue = true;
1472 ConfigInputTextCursorBlink = true;
1473 ConfigInputTextEnterKeepActive = false;
1474 ConfigDragClickToInputText = false;
1475 ConfigWindowsResizeFromEdges = true;
1476 ConfigWindowsMoveFromTitleBarOnly = false;
1477 ConfigWindowsCopyContentsWithCtrlC = false;
1478 ConfigScrollbarScrollByPage = true;
1479 ConfigMemoryCompactTimer = 60.0f;
1480 ConfigDebugIsDebuggerPresent = false;
1481 ConfigDebugHighlightIdConflicts = true;
1482 ConfigDebugBeginReturnValueOnce = false;
1483 ConfigDebugBeginReturnValueLoop = false;
1484
1485 ConfigErrorRecovery = true;
1486 ConfigErrorRecoveryEnableAssert = true;
1487 ConfigErrorRecoveryEnableDebugLog = true;
1488 ConfigErrorRecoveryEnableTooltip = true;
1489
1490 // Inputs Behaviors
1491 MouseDoubleClickTime = 0.30f;
1492 MouseDoubleClickMaxDist = 6.0f;
1493 MouseDragThreshold = 6.0f;
1494 KeyRepeatDelay = 0.275f;
1495 KeyRepeatRate = 0.050f;
1496
1497 // Platform Functions
1498 // Note: Initialize() will setup default clipboard/ime handlers.
1499 BackendPlatformName = BackendRendererName = NULL;
1500 BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1501
1502 // Input (NB: we already have memset zero the entire structure!)
1503 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1504 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1505 MouseSource = ImGuiMouseSource_Mouse;
1506 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1507 for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; }
1508 AppAcceptingEvents = true;
1509}
1510
1511// Pass in translated ASCII characters for text input.
1512// - with glfw you can get those from the callback set in glfwSetCharCallback()
1513// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
1514// FIXME: Should in theory be called "AddCharacterEvent()" to be consistent with new API
1515void ImGuiIO::AddInputCharacter(unsigned int c)
1516{
1517 IM_ASSERT(Ctx != NULL);
1518 ImGuiContext& g = *Ctx;
1519 if (c == 0 || !AppAcceptingEvents)
1520 return;
1521
1522 ImGuiInputEvent e;
1523 e.Type = ImGuiInputEventType_Text;
1524 e.Source = ImGuiInputSource_Keyboard;
1525 e.EventId = g.InputEventsNextEventId++;
1526 e.Text.Char = c;
1527 g.InputEventsQueue.push_back(v: e);
1528}
1529
1530// UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
1531// we should save the high surrogate.
1532void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
1533{
1534 if ((c == 0 && InputQueueSurrogate == 0) || !AppAcceptingEvents)
1535 return;
1536
1537 if ((c & 0xFC00) == 0xD800) // High surrogate, must save
1538 {
1539 if (InputQueueSurrogate != 0)
1540 AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);
1541 InputQueueSurrogate = c;
1542 return;
1543 }
1544
1545 ImWchar cp = c;
1546 if (InputQueueSurrogate != 0)
1547 {
1548 if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
1549 {
1550 AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);
1551 }
1552 else
1553 {
1554#if IM_UNICODE_CODEPOINT_MAX == 0xFFFF
1555 cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar
1556#else
1557 cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
1558#endif
1559 }
1560
1561 InputQueueSurrogate = 0;
1562 }
1563 AddInputCharacter(c: (unsigned)cp);
1564}
1565
1566void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1567{
1568 if (!AppAcceptingEvents)
1569 return;
1570 while (*utf8_chars != 0)
1571 {
1572 unsigned int c = 0;
1573 utf8_chars += ImTextCharFromUtf8(out_char: &c, in_text: utf8_chars, NULL);
1574 AddInputCharacter(c);
1575 }
1576}
1577
1578// Clear all incoming events.
1579void ImGuiIO::ClearEventsQueue()
1580{
1581 IM_ASSERT(Ctx != NULL);
1582 ImGuiContext& g = *Ctx;
1583 g.InputEventsQueue.clear();
1584}
1585
1586// Clear current keyboard/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.
1587void ImGuiIO::ClearInputKeys()
1588{
1589 ImGuiContext& g = *Ctx;
1590 for (int key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key++)
1591 {
1592 if (ImGui::IsMouseKey(key: (ImGuiKey)key))
1593 continue;
1594 ImGuiKeyData* key_data = &g.IO.KeysData[key - ImGuiKey_NamedKey_BEGIN];
1595 key_data->Down = false;
1596 key_data->DownDuration = -1.0f;
1597 key_data->DownDurationPrev = -1.0f;
1598 }
1599 KeyCtrl = KeyShift = KeyAlt = KeySuper = false;
1600 KeyMods = ImGuiMod_None;
1601 InputQueueCharacters.resize(new_size: 0); // Behavior of old ClearInputCharacters().
1602}
1603
1604void ImGuiIO::ClearInputMouse()
1605{
1606 for (ImGuiKey key = ImGuiKey_Mouse_BEGIN; key < ImGuiKey_Mouse_END; key = (ImGuiKey)(key + 1))
1607 {
1608 ImGuiKeyData* key_data = &KeysData[key - ImGuiKey_NamedKey_BEGIN];
1609 key_data->Down = false;
1610 key_data->DownDuration = -1.0f;
1611 key_data->DownDurationPrev = -1.0f;
1612 }
1613 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1614 for (int n = 0; n < IM_ARRAYSIZE(MouseDown); n++)
1615 {
1616 MouseDown[n] = false;
1617 MouseDownDuration[n] = MouseDownDurationPrev[n] = -1.0f;
1618 }
1619 MouseWheel = MouseWheelH = 0.0f;
1620}
1621
1622// Removed this as it is ambiguous/misleading and generally incorrect to use with the existence of a higher-level input queue.
1623// Current frame character buffer is now also cleared by ClearInputKeys().
1624#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1625void ImGuiIO::ClearInputCharacters()
1626{
1627 InputQueueCharacters.resize(new_size: 0);
1628}
1629#endif
1630
1631static ImGuiInputEvent* FindLatestInputEvent(ImGuiContext* ctx, ImGuiInputEventType type, int arg = -1)
1632{
1633 ImGuiContext& g = *ctx;
1634 for (int n = g.InputEventsQueue.Size - 1; n >= 0; n--)
1635 {
1636 ImGuiInputEvent* e = &g.InputEventsQueue[n];
1637 if (e->Type != type)
1638 continue;
1639 if (type == ImGuiInputEventType_Key && e->Key.Key != arg)
1640 continue;
1641 if (type == ImGuiInputEventType_MouseButton && e->MouseButton.Button != arg)
1642 continue;
1643 return e;
1644 }
1645 return NULL;
1646}
1647
1648// Queue a new key down/up event.
1649// - ImGuiKey key: Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character)
1650// - bool down: Is the key down? use false to signify a key release.
1651// - float analog_value: 0.0f..1.0f
1652// IMPORTANT: THIS FUNCTION AND OTHER "ADD" GRABS THE CONTEXT FROM OUR INSTANCE.
1653// WE NEED TO ENSURE THAT ALL FUNCTION CALLS ARE FULFILLING THIS, WHICH IS WHY GetKeyData() HAS AN EXPLICIT CONTEXT.
1654void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value)
1655{
1656 //if (e->Down) { IMGUI_DEBUG_LOG_IO("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); }
1657 IM_ASSERT(Ctx != NULL);
1658 if (key == ImGuiKey_None || !AppAcceptingEvents)
1659 return;
1660 ImGuiContext& g = *Ctx;
1661 IM_ASSERT(ImGui::IsNamedKeyOrMod(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API.
1662 IM_ASSERT(ImGui::IsAliasKey(key) == false); // Backend cannot submit ImGuiKey_MouseXXX values they are automatically inferred from AddMouseXXX() events.
1663
1664 // MacOS: swap Cmd(Super) and Ctrl
1665 if (g.IO.ConfigMacOSXBehaviors)
1666 {
1667 if (key == ImGuiMod_Super) { key = ImGuiMod_Ctrl; }
1668 else if (key == ImGuiMod_Ctrl) { key = ImGuiMod_Super; }
1669 else if (key == ImGuiKey_LeftSuper) { key = ImGuiKey_LeftCtrl; }
1670 else if (key == ImGuiKey_RightSuper){ key = ImGuiKey_RightCtrl; }
1671 else if (key == ImGuiKey_LeftCtrl) { key = ImGuiKey_LeftSuper; }
1672 else if (key == ImGuiKey_RightCtrl) { key = ImGuiKey_RightSuper; }
1673 }
1674
1675 // Filter duplicate (in particular: key mods and gamepad analog values are commonly spammed)
1676 const ImGuiInputEvent* latest_event = FindLatestInputEvent(ctx: &g, type: ImGuiInputEventType_Key, arg: (int)key);
1677 const ImGuiKeyData* key_data = ImGui::GetKeyData(ctx: &g, key);
1678 const bool latest_key_down = latest_event ? latest_event->Key.Down : key_data->Down;
1679 const float latest_key_analog = latest_event ? latest_event->Key.AnalogValue : key_data->AnalogValue;
1680 if (latest_key_down == down && latest_key_analog == analog_value)
1681 return;
1682
1683 // Add event
1684 ImGuiInputEvent e;
1685 e.Type = ImGuiInputEventType_Key;
1686 e.Source = ImGui::IsGamepadKey(key) ? ImGuiInputSource_Gamepad : ImGuiInputSource_Keyboard;
1687 e.EventId = g.InputEventsNextEventId++;
1688 e.Key.Key = key;
1689 e.Key.Down = down;
1690 e.Key.AnalogValue = analog_value;
1691 g.InputEventsQueue.push_back(v: e);
1692}
1693
1694void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down)
1695{
1696 if (!AppAcceptingEvents)
1697 return;
1698 AddKeyAnalogEvent(key, down, analog_value: down ? 1.0f : 0.0f);
1699}
1700
1701// [Optional] Call after AddKeyEvent().
1702// Specify native keycode, scancode + Specify index for legacy <1.87 IsKeyXXX() functions with native indices.
1703// If you are writing a backend in 2022 or don't use IsKeyXXX() with native values that are not ImGuiKey values, you can avoid calling this.
1704void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index)
1705{
1706 if (key == ImGuiKey_None)
1707 return;
1708 IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512
1709 IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey((ImGuiKey)native_legacy_index)); // >= 0 && <= 511
1710 IM_UNUSED(key); // Yet unused
1711 IM_UNUSED(native_keycode); // Yet unused
1712 IM_UNUSED(native_scancode); // Yet unused
1713 IM_UNUSED(native_legacy_index); // Yet unused
1714}
1715
1716// Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen.
1717void ImGuiIO::SetAppAcceptingEvents(bool accepting_events)
1718{
1719 AppAcceptingEvents = accepting_events;
1720}
1721
1722// Queue a mouse move event
1723void ImGuiIO::AddMousePosEvent(float x, float y)
1724{
1725 IM_ASSERT(Ctx != NULL);
1726 ImGuiContext& g = *Ctx;
1727 if (!AppAcceptingEvents)
1728 return;
1729
1730 // Apply same flooring as UpdateMouseInputs()
1731 ImVec2 pos((x > -FLT_MAX) ? ImFloor(f: x) : x, (y > -FLT_MAX) ? ImFloor(f: y) : y);
1732
1733 // Filter duplicate
1734 const ImGuiInputEvent* latest_event = FindLatestInputEvent(ctx: &g, type: ImGuiInputEventType_MousePos);
1735 const ImVec2 latest_pos = latest_event ? ImVec2(latest_event->MousePos.PosX, latest_event->MousePos.PosY) : g.IO.MousePos;
1736 if (latest_pos.x == pos.x && latest_pos.y == pos.y)
1737 return;
1738
1739 ImGuiInputEvent e;
1740 e.Type = ImGuiInputEventType_MousePos;
1741 e.Source = ImGuiInputSource_Mouse;
1742 e.EventId = g.InputEventsNextEventId++;
1743 e.MousePos.PosX = pos.x;
1744 e.MousePos.PosY = pos.y;
1745 e.MousePos.MouseSource = g.InputEventsNextMouseSource;
1746 g.InputEventsQueue.push_back(v: e);
1747}
1748
1749void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down)
1750{
1751 IM_ASSERT(Ctx != NULL);
1752 ImGuiContext& g = *Ctx;
1753 IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT);
1754 if (!AppAcceptingEvents)
1755 return;
1756
1757 // On MacOS X: Convert Ctrl(Super)+Left click into Right-click: handle held button.
1758 if (ConfigMacOSXBehaviors && mouse_button == 0 && MouseCtrlLeftAsRightClick)
1759 {
1760 // Order of both statements matterns: this event will still release mouse button 1
1761 mouse_button = 1;
1762 if (!down)
1763 MouseCtrlLeftAsRightClick = false;
1764 }
1765
1766 // Filter duplicate
1767 const ImGuiInputEvent* latest_event = FindLatestInputEvent(ctx: &g, type: ImGuiInputEventType_MouseButton, arg: (int)mouse_button);
1768 const bool latest_button_down = latest_event ? latest_event->MouseButton.Down : g.IO.MouseDown[mouse_button];
1769 if (latest_button_down == down)
1770 return;
1771
1772 // On MacOS X: Convert Ctrl(Super)+Left click into Right-click.
1773 // - Note that this is actual physical Ctrl which is ImGuiMod_Super for us.
1774 // - At this point we want from !down to down, so this is handling the initial press.
1775 if (ConfigMacOSXBehaviors && mouse_button == 0 && down)
1776 {
1777 const ImGuiInputEvent* latest_super_event = FindLatestInputEvent(ctx: &g, type: ImGuiInputEventType_Key, arg: (int)ImGuiMod_Super);
1778 if (latest_super_event ? latest_super_event->Key.Down : g.IO.KeySuper)
1779 {
1780 IMGUI_DEBUG_LOG_IO("[io] Super+Left Click aliased into Right Click\n");
1781 MouseCtrlLeftAsRightClick = true;
1782 AddMouseButtonEvent(mouse_button: 1, down: true); // This is just quicker to write that passing through, as we need to filter duplicate again.
1783 return;
1784 }
1785 }
1786
1787 ImGuiInputEvent e;
1788 e.Type = ImGuiInputEventType_MouseButton;
1789 e.Source = ImGuiInputSource_Mouse;
1790 e.EventId = g.InputEventsNextEventId++;
1791 e.MouseButton.Button = mouse_button;
1792 e.MouseButton.Down = down;
1793 e.MouseButton.MouseSource = g.InputEventsNextMouseSource;
1794 g.InputEventsQueue.push_back(v: e);
1795}
1796
1797// Queue a mouse wheel event (some mouse/API may only have a Y component)
1798void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y)
1799{
1800 IM_ASSERT(Ctx != NULL);
1801 ImGuiContext& g = *Ctx;
1802
1803 // Filter duplicate (unlike most events, wheel values are relative and easy to filter)
1804 if (!AppAcceptingEvents || (wheel_x == 0.0f && wheel_y == 0.0f))
1805 return;
1806
1807 ImGuiInputEvent e;
1808 e.Type = ImGuiInputEventType_MouseWheel;
1809 e.Source = ImGuiInputSource_Mouse;
1810 e.EventId = g.InputEventsNextEventId++;
1811 e.MouseWheel.WheelX = wheel_x;
1812 e.MouseWheel.WheelY = wheel_y;
1813 e.MouseWheel.MouseSource = g.InputEventsNextMouseSource;
1814 g.InputEventsQueue.push_back(v: e);
1815}
1816
1817// This is not a real event, the data is latched in order to be stored in actual Mouse events.
1818// This is so that duplicate events (e.g. Windows sending extraneous WM_MOUSEMOVE) gets filtered and are not leading to actual source changes.
1819void ImGuiIO::AddMouseSourceEvent(ImGuiMouseSource source)
1820{
1821 IM_ASSERT(Ctx != NULL);
1822 ImGuiContext& g = *Ctx;
1823 g.InputEventsNextMouseSource = source;
1824}
1825
1826void ImGuiIO::AddMouseViewportEvent(ImGuiID viewport_id)
1827{
1828 IM_ASSERT(Ctx != NULL);
1829 ImGuiContext& g = *Ctx;
1830 //IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport);
1831 if (!AppAcceptingEvents)
1832 return;
1833
1834 // Filter duplicate
1835 const ImGuiInputEvent* latest_event = FindLatestInputEvent(ctx: &g, type: ImGuiInputEventType_MouseViewport);
1836 const ImGuiID latest_viewport_id = latest_event ? latest_event->MouseViewport.HoveredViewportID : g.IO.MouseHoveredViewport;
1837 if (latest_viewport_id == viewport_id)
1838 return;
1839
1840 ImGuiInputEvent e;
1841 e.Type = ImGuiInputEventType_MouseViewport;
1842 e.Source = ImGuiInputSource_Mouse;
1843 e.MouseViewport.HoveredViewportID = viewport_id;
1844 g.InputEventsQueue.push_back(v: e);
1845}
1846
1847void ImGuiIO::AddFocusEvent(bool focused)
1848{
1849 IM_ASSERT(Ctx != NULL);
1850 ImGuiContext& g = *Ctx;
1851
1852 // Filter duplicate
1853 const ImGuiInputEvent* latest_event = FindLatestInputEvent(ctx: &g, type: ImGuiInputEventType_Focus);
1854 const bool latest_focused = latest_event ? latest_event->AppFocused.Focused : !g.IO.AppFocusLost;
1855 if (latest_focused == focused || (ConfigDebugIgnoreFocusLoss && !focused))
1856 return;
1857
1858 ImGuiInputEvent e;
1859 e.Type = ImGuiInputEventType_Focus;
1860 e.EventId = g.InputEventsNextEventId++;
1861 e.AppFocused.Focused = focused;
1862 g.InputEventsQueue.push_back(v: e);
1863}
1864
1865ImGuiPlatformIO::ImGuiPlatformIO()
1866{
1867 // Most fields are initialized with zero
1868 memset(s: this, c: 0, n: sizeof(*this));
1869 Platform_LocaleDecimalPoint = '.';
1870}
1871
1872//-----------------------------------------------------------------------------
1873// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1874//-----------------------------------------------------------------------------
1875
1876ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1877{
1878 IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau()
1879 ImVec2 p_last = p1;
1880 ImVec2 p_closest;
1881 float p_closest_dist2 = FLT_MAX;
1882 float t_step = 1.0f / (float)num_segments;
1883 for (int i_step = 1; i_step <= num_segments; i_step++)
1884 {
1885 ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t: t_step * i_step);
1886 ImVec2 p_line = ImLineClosestPoint(a: p_last, b: p_current, p);
1887 float dist2 = ImLengthSqr(lhs: p - p_line);
1888 if (dist2 < p_closest_dist2)
1889 {
1890 p_closest = p_line;
1891 p_closest_dist2 = dist2;
1892 }
1893 p_last = p_current;
1894 }
1895 return p_closest;
1896}
1897
1898// Closely mimics PathBezierToCasteljau() in imgui_draw.cpp
1899static 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)
1900{
1901 float dx = x4 - x1;
1902 float dy = y4 - y1;
1903 float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
1904 float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
1905 d2 = (d2 >= 0) ? d2 : -d2;
1906 d3 = (d3 >= 0) ? d3 : -d3;
1907 if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
1908 {
1909 ImVec2 p_current(x4, y4);
1910 ImVec2 p_line = ImLineClosestPoint(a: p_last, b: p_current, p);
1911 float dist2 = ImLengthSqr(lhs: p - p_line);
1912 if (dist2 < p_closest_dist2)
1913 {
1914 p_closest = p_line;
1915 p_closest_dist2 = dist2;
1916 }
1917 p_last = p_current;
1918 }
1919 else if (level < 10)
1920 {
1921 float x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f;
1922 float x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f;
1923 float x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f;
1924 float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f;
1925 float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f;
1926 float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
1927 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x2: x12, y2: y12, x3: x123, y3: y123, x4: x1234, y4: y1234, tess_tol, level: level + 1);
1928 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1: x1234, y1: y1234, x2: x234, y2: y234, x3: x34, y3: y34, x4, y4, tess_tol, level: level + 1);
1929 }
1930}
1931
1932// tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
1933// Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.
1934ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
1935{
1936 IM_ASSERT(tess_tol > 0.0f);
1937 ImVec2 p_last = p1;
1938 ImVec2 p_closest;
1939 float p_closest_dist2 = FLT_MAX;
1940 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y, x3: p3.x, y3: p3.y, x4: p4.x, y4: p4.y, tess_tol, level: 0);
1941 return p_closest;
1942}
1943
1944ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1945{
1946 ImVec2 ap = p - a;
1947 ImVec2 ab_dir = b - a;
1948 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1949 if (dot < 0.0f)
1950 return a;
1951 float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1952 if (dot > ab_len_sqr)
1953 return b;
1954 return a + ab_dir * dot / ab_len_sqr;
1955}
1956
1957bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1958{
1959 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1960 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1961 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1962 return ((b1 == b2) && (b2 == b3));
1963}
1964
1965void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1966{
1967 ImVec2 v0 = b - a;
1968 ImVec2 v1 = c - a;
1969 ImVec2 v2 = p - a;
1970 const float denom = v0.x * v1.y - v1.x * v0.y;
1971 out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1972 out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1973 out_u = 1.0f - out_v - out_w;
1974}
1975
1976ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1977{
1978 ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1979 ImVec2 proj_bc = ImLineClosestPoint(a: b, b: c, p);
1980 ImVec2 proj_ca = ImLineClosestPoint(a: c, b: a, p);
1981 float dist2_ab = ImLengthSqr(lhs: p - proj_ab);
1982 float dist2_bc = ImLengthSqr(lhs: p - proj_bc);
1983 float dist2_ca = ImLengthSqr(lhs: p - proj_ca);
1984 float m = ImMin(lhs: dist2_ab, rhs: ImMin(lhs: dist2_bc, rhs: dist2_ca));
1985 if (m == dist2_ab)
1986 return proj_ab;
1987 if (m == dist2_bc)
1988 return proj_bc;
1989 return proj_ca;
1990}
1991
1992//-----------------------------------------------------------------------------
1993// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
1994//-----------------------------------------------------------------------------
1995
1996// Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.
1997int ImStricmp(const char* str1, const char* str2)
1998{
1999 int d;
2000 while ((d = ImToUpper(c: *str2) - ImToUpper(c: *str1)) == 0 && *str1) { str1++; str2++; }
2001 return d;
2002}
2003
2004int ImStrnicmp(const char* str1, const char* str2, size_t count)
2005{
2006 int d = 0;
2007 while (count > 0 && (d = ImToUpper(c: *str2) - ImToUpper(c: *str1)) == 0 && *str1) { str1++; str2++; count--; }
2008 return d;
2009}
2010
2011void ImStrncpy(char* dst, const char* src, size_t count)
2012{
2013 if (count < 1)
2014 return;
2015 if (count > 1)
2016 strncpy(dest: dst, src: src, n: count - 1);
2017 dst[count - 1] = 0;
2018}
2019
2020char* ImStrdup(const char* str)
2021{
2022 size_t len = strlen(s: str);
2023 void* buf = IM_ALLOC(len + 1);
2024 return (char*)memcpy(dest: buf, src: (const void*)str, n: len + 1);
2025}
2026
2027char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
2028{
2029 size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(s: dst) + 1;
2030 size_t src_size = strlen(s: src) + 1;
2031 if (dst_buf_size < src_size)
2032 {
2033 IM_FREE(dst);
2034 dst = (char*)IM_ALLOC(src_size);
2035 if (p_dst_size)
2036 *p_dst_size = src_size;
2037 }
2038 return (char*)memcpy(dest: dst, src: (const void*)src, n: src_size);
2039}
2040
2041const char* ImStrchrRange(const char* str, const char* str_end, char c)
2042{
2043 const char* p = (const char*)memchr(s: str, c: (int)c, n: str_end - str);
2044 return p;
2045}
2046
2047int ImStrlenW(const ImWchar* str)
2048{
2049 //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bit
2050 int n = 0;
2051 while (*str++) n++;
2052 return n;
2053}
2054
2055// Find end-of-line. Return pointer will point to either first \n, either str_end.
2056const char* ImStreolRange(const char* str, const char* str_end)
2057{
2058 const char* p = (const char*)memchr(s: str, c: '\n', n: str_end - str);
2059 return p ? p : str_end;
2060}
2061
2062const char* ImStrbol(const char* buf_mid_line, const char* buf_begin) // find beginning-of-line
2063{
2064 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
2065 buf_mid_line--;
2066 return buf_mid_line;
2067}
2068
2069const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
2070{
2071 if (!needle_end)
2072 needle_end = needle + strlen(s: needle);
2073
2074 const char un0 = (char)ImToUpper(c: *needle);
2075 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
2076 {
2077 if (ImToUpper(c: *haystack) == un0)
2078 {
2079 const char* b = needle + 1;
2080 for (const char* a = haystack + 1; b < needle_end; a++, b++)
2081 if (ImToUpper(c: *a) != ImToUpper(c: *b))
2082 break;
2083 if (b == needle_end)
2084 return haystack;
2085 }
2086 haystack++;
2087 }
2088 return NULL;
2089}
2090
2091// 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.
2092void ImStrTrimBlanks(char* buf)
2093{
2094 char* p = buf;
2095 while (p[0] == ' ' || p[0] == '\t') // Leading blanks
2096 p++;
2097 char* p_start = p;
2098 while (*p != 0) // Find end of string
2099 p++;
2100 while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks
2101 p--;
2102 if (p_start != buf) // Copy memory if we had leading blanks
2103 memmove(dest: buf, src: p_start, n: p - p_start);
2104 buf[p - p_start] = 0; // Zero terminate
2105}
2106
2107const char* ImStrSkipBlank(const char* str)
2108{
2109 while (str[0] == ' ' || str[0] == '\t')
2110 str++;
2111 return str;
2112}
2113
2114// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
2115// 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.
2116// B) When buf==NULL vsnprintf() will return the output size.
2117#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2118
2119// We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
2120// You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2121// and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
2122// designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
2123#ifdef IMGUI_USE_STB_SPRINTF
2124#ifndef IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION
2125#define STB_SPRINTF_IMPLEMENTATION
2126#endif
2127#ifdef IMGUI_STB_SPRINTF_FILENAME
2128#include IMGUI_STB_SPRINTF_FILENAME
2129#else
2130#include "stb_sprintf.h"
2131#endif
2132#endif // #ifdef IMGUI_USE_STB_SPRINTF
2133
2134#if defined(_MSC_VER) && !defined(vsnprintf)
2135#define vsnprintf _vsnprintf
2136#endif
2137
2138int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
2139{
2140 va_list args;
2141 va_start(args, fmt);
2142#ifdef IMGUI_USE_STB_SPRINTF
2143 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
2144#else
2145 int w = vsnprintf(s: buf, maxlen: buf_size, format: fmt, arg: args);
2146#endif
2147 va_end(args);
2148 if (buf == NULL)
2149 return w;
2150 if (w == -1 || w >= (int)buf_size)
2151 w = (int)buf_size - 1;
2152 buf[w] = 0;
2153 return w;
2154}
2155
2156int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
2157{
2158#ifdef IMGUI_USE_STB_SPRINTF
2159 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
2160#else
2161 int w = vsnprintf(s: buf, maxlen: buf_size, format: fmt, arg: args);
2162#endif
2163 if (buf == NULL)
2164 return w;
2165 if (w == -1 || w >= (int)buf_size)
2166 w = (int)buf_size - 1;
2167 buf[w] = 0;
2168 return w;
2169}
2170#endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2171
2172void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...)
2173{
2174 va_list args;
2175 va_start(args, fmt);
2176 ImFormatStringToTempBufferV(out_buf, out_buf_end, fmt, args);
2177 va_end(args);
2178}
2179
2180// FIXME: Should rework API toward allowing multiple in-flight temp buffers (easier and safer for caller)
2181// by making the caller acquire a temp buffer token, with either explicit or destructor release, e.g.
2182// ImGuiTempBufferToken token;
2183// ImFormatStringToTempBuffer(token, ...);
2184void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args)
2185{
2186 ImGuiContext& g = *GImGui;
2187 if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0)
2188 {
2189 const char* buf = va_arg(args, const char*); // Skip formatting when using "%s"
2190 if (buf == NULL)
2191 buf = "(null)";
2192 *out_buf = buf;
2193 if (out_buf_end) { *out_buf_end = buf + strlen(s: buf); }
2194 }
2195 else if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 's' && fmt[4] == 0)
2196 {
2197 int buf_len = va_arg(args, int); // Skip formatting when using "%.*s"
2198 const char* buf = va_arg(args, const char*);
2199 if (buf == NULL)
2200 {
2201 buf = "(null)";
2202 buf_len = ImMin(lhs: buf_len, rhs: 6);
2203 }
2204 *out_buf = buf;
2205 *out_buf_end = buf + buf_len; // Disallow not passing 'out_buf_end' here. User is expected to use it.
2206 }
2207 else
2208 {
2209 int buf_len = ImFormatStringV(buf: g.TempBuffer.Data, buf_size: g.TempBuffer.Size, fmt, args);
2210 *out_buf = g.TempBuffer.Data;
2211 if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; }
2212 }
2213}
2214
2215#ifndef IMGUI_ENABLE_SSE4_2_CRC
2216// CRC32 needs a 1KB lookup table (not cache friendly)
2217// Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
2218// - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
2219static const ImU32 GCrc32LookupTable[256] =
2220{
2221#ifdef IMGUI_USE_LEGACY_CRC32_ADLER
2222 // Legacy CRC32-adler table used pre 1.91.6 (before 2024/11/27). Only use if you cannot afford invalidating old .ini data.
2223 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
2224 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
2225 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
2226 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
2227 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
2228 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
2229 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
2230 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
2231 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
2232 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
2233 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
2234 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
2235 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
2236 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
2237 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
2238 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
2239#else
2240 // CRC32c table compatible with SSE 4.2 instructions
2241 0x00000000,0xF26B8303,0xE13B70F7,0x1350F3F4,0xC79A971F,0x35F1141C,0x26A1E7E8,0xD4CA64EB,0x8AD958CF,0x78B2DBCC,0x6BE22838,0x9989AB3B,0x4D43CFD0,0xBF284CD3,0xAC78BF27,0x5E133C24,
2242 0x105EC76F,0xE235446C,0xF165B798,0x030E349B,0xD7C45070,0x25AFD373,0x36FF2087,0xC494A384,0x9A879FA0,0x68EC1CA3,0x7BBCEF57,0x89D76C54,0x5D1D08BF,0xAF768BBC,0xBC267848,0x4E4DFB4B,
2243 0x20BD8EDE,0xD2D60DDD,0xC186FE29,0x33ED7D2A,0xE72719C1,0x154C9AC2,0x061C6936,0xF477EA35,0xAA64D611,0x580F5512,0x4B5FA6E6,0xB93425E5,0x6DFE410E,0x9F95C20D,0x8CC531F9,0x7EAEB2FA,
2244 0x30E349B1,0xC288CAB2,0xD1D83946,0x23B3BA45,0xF779DEAE,0x05125DAD,0x1642AE59,0xE4292D5A,0xBA3A117E,0x4851927D,0x5B016189,0xA96AE28A,0x7DA08661,0x8FCB0562,0x9C9BF696,0x6EF07595,
2245 0x417B1DBC,0xB3109EBF,0xA0406D4B,0x522BEE48,0x86E18AA3,0x748A09A0,0x67DAFA54,0x95B17957,0xCBA24573,0x39C9C670,0x2A993584,0xD8F2B687,0x0C38D26C,0xFE53516F,0xED03A29B,0x1F682198,
2246 0x5125DAD3,0xA34E59D0,0xB01EAA24,0x42752927,0x96BF4DCC,0x64D4CECF,0x77843D3B,0x85EFBE38,0xDBFC821C,0x2997011F,0x3AC7F2EB,0xC8AC71E8,0x1C661503,0xEE0D9600,0xFD5D65F4,0x0F36E6F7,
2247 0x61C69362,0x93AD1061,0x80FDE395,0x72966096,0xA65C047D,0x5437877E,0x4767748A,0xB50CF789,0xEB1FCBAD,0x197448AE,0x0A24BB5A,0xF84F3859,0x2C855CB2,0xDEEEDFB1,0xCDBE2C45,0x3FD5AF46,
2248 0x7198540D,0x83F3D70E,0x90A324FA,0x62C8A7F9,0xB602C312,0x44694011,0x5739B3E5,0xA55230E6,0xFB410CC2,0x092A8FC1,0x1A7A7C35,0xE811FF36,0x3CDB9BDD,0xCEB018DE,0xDDE0EB2A,0x2F8B6829,
2249 0x82F63B78,0x709DB87B,0x63CD4B8F,0x91A6C88C,0x456CAC67,0xB7072F64,0xA457DC90,0x563C5F93,0x082F63B7,0xFA44E0B4,0xE9141340,0x1B7F9043,0xCFB5F4A8,0x3DDE77AB,0x2E8E845F,0xDCE5075C,
2250 0x92A8FC17,0x60C37F14,0x73938CE0,0x81F80FE3,0x55326B08,0xA759E80B,0xB4091BFF,0x466298FC,0x1871A4D8,0xEA1A27DB,0xF94AD42F,0x0B21572C,0xDFEB33C7,0x2D80B0C4,0x3ED04330,0xCCBBC033,
2251 0xA24BB5A6,0x502036A5,0x4370C551,0xB11B4652,0x65D122B9,0x97BAA1BA,0x84EA524E,0x7681D14D,0x2892ED69,0xDAF96E6A,0xC9A99D9E,0x3BC21E9D,0xEF087A76,0x1D63F975,0x0E330A81,0xFC588982,
2252 0xB21572C9,0x407EF1CA,0x532E023E,0xA145813D,0x758FE5D6,0x87E466D5,0x94B49521,0x66DF1622,0x38CC2A06,0xCAA7A905,0xD9F75AF1,0x2B9CD9F2,0xFF56BD19,0x0D3D3E1A,0x1E6DCDEE,0xEC064EED,
2253 0xC38D26C4,0x31E6A5C7,0x22B65633,0xD0DDD530,0x0417B1DB,0xF67C32D8,0xE52CC12C,0x1747422F,0x49547E0B,0xBB3FFD08,0xA86F0EFC,0x5A048DFF,0x8ECEE914,0x7CA56A17,0x6FF599E3,0x9D9E1AE0,
2254 0xD3D3E1AB,0x21B862A8,0x32E8915C,0xC083125F,0x144976B4,0xE622F5B7,0xF5720643,0x07198540,0x590AB964,0xAB613A67,0xB831C993,0x4A5A4A90,0x9E902E7B,0x6CFBAD78,0x7FAB5E8C,0x8DC0DD8F,
2255 0xE330A81A,0x115B2B19,0x020BD8ED,0xF0605BEE,0x24AA3F05,0xD6C1BC06,0xC5914FF2,0x37FACCF1,0x69E9F0D5,0x9B8273D6,0x88D28022,0x7AB90321,0xAE7367CA,0x5C18E4C9,0x4F48173D,0xBD23943E,
2256 0xF36E6F75,0x0105EC76,0x12551F82,0xE03E9C81,0x34F4F86A,0xC69F7B69,0xD5CF889D,0x27A40B9E,0x79B737BA,0x8BDCB4B9,0x988C474D,0x6AE7C44E,0xBE2DA0A5,0x4C4623A6,0x5F16D052,0xAD7D5351
2257#endif
2258};
2259#endif
2260
2261// Known size hash
2262// It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
2263// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
2264ImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed)
2265{
2266 ImU32 crc = ~seed;
2267 const unsigned char* data = (const unsigned char*)data_p;
2268 const unsigned char *data_end = (const unsigned char*)data_p + data_size;
2269#ifndef IMGUI_ENABLE_SSE4_2_CRC
2270 const ImU32* crc32_lut = GCrc32LookupTable;
2271 while (data < data_end)
2272 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
2273 return ~crc;
2274#else
2275 while (data + 4 <= data_end)
2276 {
2277 crc = _mm_crc32_u32(crc, *(ImU32*)data);
2278 data += 4;
2279 }
2280 while (data < data_end)
2281 crc = _mm_crc32_u8(crc, *data++);
2282 return ~crc;
2283#endif
2284}
2285
2286// Zero-terminated string hash, with support for ### to reset back to seed value
2287// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
2288// Because this syntax is rarely used we are optimizing for the common case.
2289// - If we reach ### in the string we discard the hash so far and reset to the seed.
2290// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
2291// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
2292ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)
2293{
2294 seed = ~seed;
2295 ImU32 crc = seed;
2296 const unsigned char* data = (const unsigned char*)data_p;
2297#ifndef IMGUI_ENABLE_SSE4_2_CRC
2298 const ImU32* crc32_lut = GCrc32LookupTable;
2299#endif
2300 if (data_size != 0)
2301 {
2302 while (data_size-- != 0)
2303 {
2304 unsigned char c = *data++;
2305 if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
2306 crc = seed;
2307#ifndef IMGUI_ENABLE_SSE4_2_CRC
2308 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
2309#else
2310 crc = _mm_crc32_u8(crc, c);
2311#endif
2312 }
2313 }
2314 else
2315 {
2316 while (unsigned char c = *data++)
2317 {
2318 if (c == '#' && data[0] == '#' && data[1] == '#')
2319 crc = seed;
2320#ifndef IMGUI_ENABLE_SSE4_2_CRC
2321 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
2322#else
2323 crc = _mm_crc32_u8(crc, c);
2324#endif
2325 }
2326 }
2327 return ~crc;
2328}
2329
2330//-----------------------------------------------------------------------------
2331// [SECTION] MISC HELPERS/UTILITIES (File functions)
2332//-----------------------------------------------------------------------------
2333
2334// Default file functions
2335#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
2336
2337ImFileHandle ImFileOpen(const char* filename, const char* mode)
2338{
2339#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
2340 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
2341 // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
2342 const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
2343 const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
2344
2345 // Use stack buffer if possible, otherwise heap buffer. Sizes include zero terminator.
2346 // We don't rely on current ImGuiContext as this is implied to be a helper function which doesn't depend on it (see #7314).
2347 wchar_t local_temp_stack[FILENAME_MAX];
2348 ImVector<wchar_t> local_temp_heap;
2349 if (filename_wsize + mode_wsize > IM_ARRAYSIZE(local_temp_stack))
2350 local_temp_heap.resize(filename_wsize + mode_wsize);
2351 wchar_t* filename_wbuf = local_temp_heap.Data ? local_temp_heap.Data : local_temp_stack;
2352 wchar_t* mode_wbuf = filename_wbuf + filename_wsize;
2353 ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_wbuf, filename_wsize);
2354 ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_wbuf, mode_wsize);
2355 return ::_wfopen(filename_wbuf, mode_wbuf);
2356#else
2357 return fopen(filename: filename, modes: mode);
2358#endif
2359}
2360
2361// 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.
2362bool ImFileClose(ImFileHandle f) { return fclose(stream: f) == 0; }
2363ImU64 ImFileGetSize(ImFileHandle f) { long off = 0, sz = 0; return ((off = ftell(stream: f)) != -1 && !fseek(stream: f, off: 0, SEEK_END) && (sz = ftell(stream: f)) != -1 && !fseek(stream: f, off: off, SEEK_SET)) ? (ImU64)sz : (ImU64)-1; }
2364ImU64 ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fread(ptr: data, size: (size_t)sz, n: (size_t)count, stream: f); }
2365ImU64 ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fwrite(ptr: data, size: (size_t)sz, n: (size_t)count, s: f); }
2366#endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
2367
2368// Helper: Load file content into memory
2369// Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
2370// This can't really be used with "rt" because fseek size won't match read size.
2371void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
2372{
2373 IM_ASSERT(filename && mode);
2374 if (out_file_size)
2375 *out_file_size = 0;
2376
2377 ImFileHandle f;
2378 if ((f = ImFileOpen(filename, mode)) == NULL)
2379 return NULL;
2380
2381 size_t file_size = (size_t)ImFileGetSize(f);
2382 if (file_size == (size_t)-1)
2383 {
2384 ImFileClose(f);
2385 return NULL;
2386 }
2387
2388 void* file_data = IM_ALLOC(file_size + padding_bytes);
2389 if (file_data == NULL)
2390 {
2391 ImFileClose(f);
2392 return NULL;
2393 }
2394 if (ImFileRead(data: file_data, sz: 1, count: file_size, f) != file_size)
2395 {
2396 ImFileClose(f);
2397 IM_FREE(file_data);
2398 return NULL;
2399 }
2400 if (padding_bytes > 0)
2401 memset(s: (void*)(((char*)file_data) + file_size), c: 0, n: (size_t)padding_bytes);
2402
2403 ImFileClose(f);
2404 if (out_file_size)
2405 *out_file_size = file_size;
2406
2407 return file_data;
2408}
2409
2410//-----------------------------------------------------------------------------
2411// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
2412//-----------------------------------------------------------------------------
2413
2414IM_MSVC_RUNTIME_CHECKS_OFF
2415
2416// Convert UTF-8 to 32-bit character, process single character input.
2417// A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).
2418// We handle UTF-8 decoding error by skipping forward.
2419int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
2420{
2421 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 };
2422 static const int masks[] = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 };
2423 static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 };
2424 static const int shiftc[] = { 0, 18, 12, 6, 0 };
2425 static const int shifte[] = { 0, 6, 4, 2, 0 };
2426 int len = lengths[*(const unsigned char*)in_text >> 3];
2427 int wanted = len + (len ? 0 : 1);
2428
2429 if (in_text_end == NULL)
2430 in_text_end = in_text + wanted; // Max length, nulls will be taken into account.
2431
2432 // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here,
2433 // so it is fast even with excessive branching.
2434 unsigned char s[4];
2435 s[0] = in_text + 0 < in_text_end ? in_text[0] : 0;
2436 s[1] = in_text + 1 < in_text_end ? in_text[1] : 0;
2437 s[2] = in_text + 2 < in_text_end ? in_text[2] : 0;
2438 s[3] = in_text + 3 < in_text_end ? in_text[3] : 0;
2439
2440 // Assume a four-byte character and load four bytes. Unused bits are shifted out.
2441 *out_char = (uint32_t)(s[0] & masks[len]) << 18;
2442 *out_char |= (uint32_t)(s[1] & 0x3f) << 12;
2443 *out_char |= (uint32_t)(s[2] & 0x3f) << 6;
2444 *out_char |= (uint32_t)(s[3] & 0x3f) << 0;
2445 *out_char >>= shiftc[len];
2446
2447 // Accumulate the various error conditions.
2448 int e = 0;
2449 e = (*out_char < mins[len]) << 6; // non-canonical encoding
2450 e |= ((*out_char >> 11) == 0x1b) << 7; // surrogate half?
2451 e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8; // out of range?
2452 e |= (s[1] & 0xc0) >> 2;
2453 e |= (s[2] & 0xc0) >> 4;
2454 e |= (s[3] ) >> 6;
2455 e ^= 0x2a; // top two bits of each tail byte correct?
2456 e >>= shifte[len];
2457
2458 if (e)
2459 {
2460 // No bytes are consumed when *in_text == 0 || in_text == in_text_end.
2461 // One byte is consumed in case of invalid first byte of in_text.
2462 // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes.
2463 // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s.
2464 wanted = ImMin(lhs: wanted, rhs: !!s[0] + !!s[1] + !!s[2] + !!s[3]);
2465 *out_char = IM_UNICODE_CODEPOINT_INVALID;
2466 }
2467
2468 return wanted;
2469}
2470
2471int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
2472{
2473 ImWchar* buf_out = buf;
2474 ImWchar* buf_end = buf + buf_size;
2475 while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
2476 {
2477 unsigned int c;
2478 in_text += ImTextCharFromUtf8(out_char: &c, in_text, in_text_end);
2479 *buf_out++ = (ImWchar)c;
2480 }
2481 *buf_out = 0;
2482 if (in_text_remaining)
2483 *in_text_remaining = in_text;
2484 return (int)(buf_out - buf);
2485}
2486
2487int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
2488{
2489 int char_count = 0;
2490 while ((!in_text_end || in_text < in_text_end) && *in_text)
2491 {
2492 unsigned int c;
2493 in_text += ImTextCharFromUtf8(out_char: &c, in_text, in_text_end);
2494 char_count++;
2495 }
2496 return char_count;
2497}
2498
2499// Based on stb_to_utf8() from github.com/nothings/stb/
2500static inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c)
2501{
2502 if (c < 0x80)
2503 {
2504 buf[0] = (char)c;
2505 return 1;
2506 }
2507 if (c < 0x800)
2508 {
2509 if (buf_size < 2) return 0;
2510 buf[0] = (char)(0xc0 + (c >> 6));
2511 buf[1] = (char)(0x80 + (c & 0x3f));
2512 return 2;
2513 }
2514 if (c < 0x10000)
2515 {
2516 if (buf_size < 3) return 0;
2517 buf[0] = (char)(0xe0 + (c >> 12));
2518 buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
2519 buf[2] = (char)(0x80 + ((c ) & 0x3f));
2520 return 3;
2521 }
2522 if (c <= 0x10FFFF)
2523 {
2524 if (buf_size < 4) return 0;
2525 buf[0] = (char)(0xf0 + (c >> 18));
2526 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
2527 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
2528 buf[3] = (char)(0x80 + ((c ) & 0x3f));
2529 return 4;
2530 }
2531 // Invalid code point, the max unicode is 0x10FFFF
2532 return 0;
2533}
2534
2535const char* ImTextCharToUtf8(char out_buf[5], unsigned int c)
2536{
2537 int count = ImTextCharToUtf8_inline(buf: out_buf, buf_size: 5, c);
2538 out_buf[count] = 0;
2539 return out_buf;
2540}
2541
2542// Not optimal but we very rarely use this function.
2543int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
2544{
2545 unsigned int unused = 0;
2546 return ImTextCharFromUtf8(out_char: &unused, in_text, in_text_end);
2547}
2548
2549static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
2550{
2551 if (c < 0x80) return 1;
2552 if (c < 0x800) return 2;
2553 if (c < 0x10000) return 3;
2554 if (c <= 0x10FFFF) return 4;
2555 return 3;
2556}
2557
2558int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
2559{
2560 char* buf_p = out_buf;
2561 const char* buf_end = out_buf + out_buf_size;
2562 while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
2563 {
2564 unsigned int c = (unsigned int)(*in_text++);
2565 if (c < 0x80)
2566 *buf_p++ = (char)c;
2567 else
2568 buf_p += ImTextCharToUtf8_inline(buf: buf_p, buf_size: (int)(buf_end - buf_p - 1), c);
2569 }
2570 *buf_p = 0;
2571 return (int)(buf_p - out_buf);
2572}
2573
2574int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
2575{
2576 int bytes_count = 0;
2577 while ((!in_text_end || in_text < in_text_end) && *in_text)
2578 {
2579 unsigned int c = (unsigned int)(*in_text++);
2580 if (c < 0x80)
2581 bytes_count++;
2582 else
2583 bytes_count += ImTextCountUtf8BytesFromChar(c);
2584 }
2585 return bytes_count;
2586}
2587
2588const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr)
2589{
2590 while (in_text_curr > in_text_start)
2591 {
2592 in_text_curr--;
2593 if ((*in_text_curr & 0xC0) != 0x80)
2594 return in_text_curr;
2595 }
2596 return in_text_start;
2597}
2598
2599int ImTextCountLines(const char* in_text, const char* in_text_end)
2600{
2601 if (in_text_end == NULL)
2602 in_text_end = in_text + strlen(s: in_text); // FIXME-OPT: Not optimal approach, discourage use for now.
2603 int count = 0;
2604 while (in_text < in_text_end)
2605 {
2606 const char* line_end = (const char*)memchr(s: in_text, c: '\n', n: in_text_end - in_text);
2607 in_text = line_end ? line_end + 1 : in_text_end;
2608 count++;
2609 }
2610 return count;
2611}
2612
2613IM_MSVC_RUNTIME_CHECKS_RESTORE
2614
2615//-----------------------------------------------------------------------------
2616// [SECTION] MISC HELPERS/UTILITIES (Color functions)
2617// Note: The Convert functions are early design which are not consistent with other API.
2618//-----------------------------------------------------------------------------
2619
2620IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
2621{
2622 float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
2623 int r = ImLerp(a: (int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, b: (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
2624 int g = ImLerp(a: (int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, b: (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
2625 int b = ImLerp(a: (int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, b: (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
2626 return IM_COL32(r, g, b, 0xFF);
2627}
2628
2629ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
2630{
2631 float s = 1.0f / 255.0f;
2632 return ImVec4(
2633 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
2634 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
2635 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
2636 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
2637}
2638
2639ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
2640{
2641 ImU32 out;
2642 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
2643 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
2644 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
2645 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
2646 return out;
2647}
2648
2649// Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
2650// Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
2651void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
2652{
2653 float K = 0.f;
2654 if (g < b)
2655 {
2656 ImSwap(a&: g, b);
2657 K = -1.f;
2658 }
2659 if (r < g)
2660 {
2661 ImSwap(a&: r, b&: g);
2662 K = -2.f / 6.f - K;
2663 }
2664
2665 const float chroma = r - (g < b ? g : b);
2666 out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
2667 out_s = chroma / (r + 1e-20f);
2668 out_v = r;
2669}
2670
2671// Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
2672// also http://en.wikipedia.org/wiki/HSL_and_HSV
2673void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
2674{
2675 if (s == 0.0f)
2676 {
2677 // gray
2678 out_r = out_g = out_b = v;
2679 return;
2680 }
2681
2682 h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
2683 int i = (int)h;
2684 float f = h - (float)i;
2685 float p = v * (1.0f - s);
2686 float q = v * (1.0f - s * f);
2687 float t = v * (1.0f - s * (1.0f - f));
2688
2689 switch (i)
2690 {
2691 case 0: out_r = v; out_g = t; out_b = p; break;
2692 case 1: out_r = q; out_g = v; out_b = p; break;
2693 case 2: out_r = p; out_g = v; out_b = t; break;
2694 case 3: out_r = p; out_g = q; out_b = v; break;
2695 case 4: out_r = t; out_g = p; out_b = v; break;
2696 case 5: default: out_r = v; out_g = p; out_b = q; break;
2697 }
2698}
2699
2700//-----------------------------------------------------------------------------
2701// [SECTION] ImGuiStorage
2702// Helper: Key->value storage
2703//-----------------------------------------------------------------------------
2704
2705// std::lower_bound but without the bullshit
2706ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key)
2707{
2708 ImGuiStoragePair* in_p = in_begin;
2709 for (size_t count = (size_t)(in_end - in_p); count > 0; )
2710 {
2711 size_t count2 = count >> 1;
2712 ImGuiStoragePair* mid = in_p + count2;
2713 if (mid->key < key)
2714 {
2715 in_p = ++mid;
2716 count -= count2 + 1;
2717 }
2718 else
2719 {
2720 count = count2;
2721 }
2722 }
2723 return in_p;
2724}
2725
2726IM_MSVC_RUNTIME_CHECKS_OFF
2727static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs)
2728{
2729 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
2730 ImGuiID lhs_v = ((const ImGuiStoragePair*)lhs)->key;
2731 ImGuiID rhs_v = ((const ImGuiStoragePair*)rhs)->key;
2732 return (lhs_v > rhs_v ? +1 : lhs_v < rhs_v ? -1 : 0);
2733}
2734
2735// For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
2736void ImGuiStorage::BuildSortByKey()
2737{
2738 ImQsort(base: Data.Data, count: (size_t)Data.Size, size_of_element: sizeof(ImGuiStoragePair), compare_func: PairComparerByID);
2739}
2740
2741int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
2742{
2743 ImGuiStoragePair* it = ImLowerBound(in_begin: const_cast<ImGuiStoragePair*>(Data.Data), in_end: const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
2744 if (it == Data.Data + Data.Size || it->key != key)
2745 return default_val;
2746 return it->val_i;
2747}
2748
2749bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
2750{
2751 return GetInt(key, default_val: default_val ? 1 : 0) != 0;
2752}
2753
2754float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
2755{
2756 ImGuiStoragePair* it = ImLowerBound(in_begin: const_cast<ImGuiStoragePair*>(Data.Data), in_end: const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
2757 if (it == Data.Data + Data.Size || it->key != key)
2758 return default_val;
2759 return it->val_f;
2760}
2761
2762void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
2763{
2764 ImGuiStoragePair* it = ImLowerBound(in_begin: const_cast<ImGuiStoragePair*>(Data.Data), in_end: const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
2765 if (it == Data.Data + Data.Size || it->key != key)
2766 return NULL;
2767 return it->val_p;
2768}
2769
2770// 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.
2771int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
2772{
2773 ImGuiStoragePair* it = ImLowerBound(in_begin: Data.Data, in_end: Data.Data + Data.Size, key);
2774 if (it == Data.Data + Data.Size || it->key != key)
2775 it = Data.insert(it, v: ImGuiStoragePair(key, default_val));
2776 return &it->val_i;
2777}
2778
2779bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
2780{
2781 return (bool*)GetIntRef(key, default_val: default_val ? 1 : 0);
2782}
2783
2784float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
2785{
2786 ImGuiStoragePair* it = ImLowerBound(in_begin: Data.Data, in_end: Data.Data + Data.Size, key);
2787 if (it == Data.Data + Data.Size || it->key != key)
2788 it = Data.insert(it, v: ImGuiStoragePair(key, default_val));
2789 return &it->val_f;
2790}
2791
2792void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
2793{
2794 ImGuiStoragePair* it = ImLowerBound(in_begin: Data.Data, in_end: Data.Data + Data.Size, key);
2795 if (it == Data.Data + Data.Size || it->key != key)
2796 it = Data.insert(it, v: ImGuiStoragePair(key, default_val));
2797 return &it->val_p;
2798}
2799
2800// 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)
2801void ImGuiStorage::SetInt(ImGuiID key, int val)
2802{
2803 ImGuiStoragePair* it = ImLowerBound(in_begin: Data.Data, in_end: Data.Data + Data.Size, key);
2804 if (it == Data.Data + Data.Size || it->key != key)
2805 Data.insert(it, v: ImGuiStoragePair(key, val));
2806 else
2807 it->val_i = val;
2808}
2809
2810void ImGuiStorage::SetBool(ImGuiID key, bool val)
2811{
2812 SetInt(key, val: val ? 1 : 0);
2813}
2814
2815void ImGuiStorage::SetFloat(ImGuiID key, float val)
2816{
2817 ImGuiStoragePair* it = ImLowerBound(in_begin: Data.Data, in_end: Data.Data + Data.Size, key);
2818 if (it == Data.Data + Data.Size || it->key != key)
2819 Data.insert(it, v: ImGuiStoragePair(key, val));
2820 else
2821 it->val_f = val;
2822}
2823
2824void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
2825{
2826 ImGuiStoragePair* it = ImLowerBound(in_begin: Data.Data, in_end: Data.Data + Data.Size, key);
2827 if (it == Data.Data + Data.Size || it->key != key)
2828 Data.insert(it, v: ImGuiStoragePair(key, val));
2829 else
2830 it->val_p = val;
2831}
2832
2833void ImGuiStorage::SetAllInt(int v)
2834{
2835 for (int i = 0; i < Data.Size; i++)
2836 Data[i].val_i = v;
2837}
2838IM_MSVC_RUNTIME_CHECKS_RESTORE
2839
2840//-----------------------------------------------------------------------------
2841// [SECTION] ImGuiTextFilter
2842//-----------------------------------------------------------------------------
2843
2844// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
2845ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077
2846{
2847 InputBuf[0] = 0;
2848 CountGrep = 0;
2849 if (default_filter)
2850 {
2851 ImStrncpy(dst: InputBuf, src: default_filter, IM_ARRAYSIZE(InputBuf));
2852 Build();
2853 }
2854}
2855
2856bool ImGuiTextFilter::Draw(const char* label, float width)
2857{
2858 if (width != 0.0f)
2859 ImGui::SetNextItemWidth(width);
2860 bool value_changed = ImGui::InputText(label, buf: InputBuf, IM_ARRAYSIZE(InputBuf));
2861 if (value_changed)
2862 Build();
2863 return value_changed;
2864}
2865
2866void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
2867{
2868 out->resize(new_size: 0);
2869 const char* wb = b;
2870 const char* we = wb;
2871 while (we < e)
2872 {
2873 if (*we == separator)
2874 {
2875 out->push_back(v: ImGuiTextRange(wb, we));
2876 wb = we + 1;
2877 }
2878 we++;
2879 }
2880 if (wb != we)
2881 out->push_back(v: ImGuiTextRange(wb, we));
2882}
2883
2884void ImGuiTextFilter::Build()
2885{
2886 Filters.resize(new_size: 0);
2887 ImGuiTextRange input_range(InputBuf, InputBuf + strlen(s: InputBuf));
2888 input_range.split(separator: ',', out: &Filters);
2889
2890 CountGrep = 0;
2891 for (ImGuiTextRange& f : Filters)
2892 {
2893 while (f.b < f.e && ImCharIsBlankA(c: f.b[0]))
2894 f.b++;
2895 while (f.e > f.b && ImCharIsBlankA(c: f.e[-1]))
2896 f.e--;
2897 if (f.empty())
2898 continue;
2899 if (f.b[0] != '-')
2900 CountGrep += 1;
2901 }
2902}
2903
2904bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2905{
2906 if (Filters.Size == 0)
2907 return true;
2908
2909 if (text == NULL)
2910 text = text_end = "";
2911
2912 for (const ImGuiTextRange& f : Filters)
2913 {
2914 if (f.b == f.e)
2915 continue;
2916 if (f.b[0] == '-')
2917 {
2918 // Subtract
2919 if (ImStristr(haystack: text, haystack_end: text_end, needle: f.b + 1, needle_end: f.e) != NULL)
2920 return false;
2921 }
2922 else
2923 {
2924 // Grep
2925 if (ImStristr(haystack: text, haystack_end: text_end, needle: f.b, needle_end: f.e) != NULL)
2926 return true;
2927 }
2928 }
2929
2930 // Implicit * grep
2931 if (CountGrep == 0)
2932 return true;
2933
2934 return false;
2935}
2936
2937//-----------------------------------------------------------------------------
2938// [SECTION] ImGuiTextBuffer, ImGuiTextIndex
2939//-----------------------------------------------------------------------------
2940
2941// On some platform vsnprintf() takes va_list by reference and modifies it.
2942// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2943#ifndef va_copy
2944#if defined(__GNUC__) || defined(__clang__)
2945#define va_copy(dest, src) __builtin_va_copy(dest, src)
2946#else
2947#define va_copy(dest, src) (dest = src)
2948#endif
2949#endif
2950
2951char ImGuiTextBuffer::EmptyString[1] = { 0 };
2952
2953void ImGuiTextBuffer::append(const char* str, const char* str_end)
2954{
2955 int len = str_end ? (int)(str_end - str) : (int)strlen(s: str);
2956
2957 // Add zero-terminator the first time
2958 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2959 const int needed_sz = write_off + len;
2960 if (write_off + len >= Buf.Capacity)
2961 {
2962 int new_capacity = Buf.Capacity * 2;
2963 Buf.reserve(new_capacity: needed_sz > new_capacity ? needed_sz : new_capacity);
2964 }
2965
2966 Buf.resize(new_size: needed_sz);
2967 memcpy(dest: &Buf[write_off - 1], src: str, n: (size_t)len);
2968 Buf[write_off - 1 + len] = 0;
2969}
2970
2971void ImGuiTextBuffer::appendf(const char* fmt, ...)
2972{
2973 va_list args;
2974 va_start(args, fmt);
2975 appendfv(fmt, args);
2976 va_end(args);
2977}
2978
2979// Helper: Text buffer for logging/accumulating text
2980void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2981{
2982 va_list args_copy;
2983 va_copy(args_copy, args);
2984
2985 int len = ImFormatStringV(NULL, buf_size: 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2986 if (len <= 0)
2987 {
2988 va_end(args_copy);
2989 return;
2990 }
2991
2992 // Add zero-terminator the first time
2993 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2994 const int needed_sz = write_off + len;
2995 if (write_off + len >= Buf.Capacity)
2996 {
2997 int new_capacity = Buf.Capacity * 2;
2998 Buf.reserve(new_capacity: needed_sz > new_capacity ? needed_sz : new_capacity);
2999 }
3000
3001 Buf.resize(new_size: needed_sz);
3002 ImFormatStringV(buf: &Buf[write_off - 1], buf_size: (size_t)len + 1, fmt, args: args_copy);
3003 va_end(args_copy);
3004}
3005
3006void ImGuiTextIndex::append(const char* base, int old_size, int new_size)
3007{
3008 IM_ASSERT(old_size >= 0 && new_size >= old_size && new_size >= EndOffset);
3009 if (old_size == new_size)
3010 return;
3011 if (EndOffset == 0 || base[EndOffset - 1] == '\n')
3012 LineOffsets.push_back(v: EndOffset);
3013 const char* base_end = base + new_size;
3014 for (const char* p = base + old_size; (p = (const char*)memchr(s: p, c: '\n', n: base_end - p)) != 0; )
3015 if (++p < base_end) // Don't push a trailing offset on last \n
3016 LineOffsets.push_back(v: (int)(intptr_t)(p - base));
3017 EndOffset = ImMax(lhs: EndOffset, rhs: new_size);
3018}
3019
3020//-----------------------------------------------------------------------------
3021// [SECTION] ImGuiListClipper
3022//-----------------------------------------------------------------------------
3023
3024// FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.
3025// The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.
3026static bool GetSkipItemForListClipping()
3027{
3028 ImGuiContext& g = *GImGui;
3029 return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);
3030}
3031
3032static void ImGuiListClipper_SortAndFuseRanges(ImVector<ImGuiListClipperRange>& ranges, int offset = 0)
3033{
3034 if (ranges.Size - offset <= 1)
3035 return;
3036
3037 // Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries)
3038 for (int sort_end = ranges.Size - offset - 1; sort_end > 0; --sort_end)
3039 for (int i = offset; i < sort_end + offset; ++i)
3040 if (ranges[i].Min > ranges[i + 1].Min)
3041 ImSwap(a&: ranges[i], b&: ranges[i + 1]);
3042
3043 // Now fuse ranges together as much as possible.
3044 for (int i = 1 + offset; i < ranges.Size; i++)
3045 {
3046 IM_ASSERT(!ranges[i].PosToIndexConvert && !ranges[i - 1].PosToIndexConvert);
3047 if (ranges[i - 1].Max < ranges[i].Min)
3048 continue;
3049 ranges[i - 1].Min = ImMin(lhs: ranges[i - 1].Min, rhs: ranges[i].Min);
3050 ranges[i - 1].Max = ImMax(lhs: ranges[i - 1].Max, rhs: ranges[i].Max);
3051 ranges.erase(it: ranges.Data + i);
3052 i--;
3053 }
3054}
3055
3056static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_height)
3057{
3058 // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
3059 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
3060 // The clipper should probably have a final step to display the last item in a regular manner, maybe with an opt-out flag for data sets which may have costly seek?
3061 ImGuiContext& g = *GImGui;
3062 ImGuiWindow* window = g.CurrentWindow;
3063 float off_y = pos_y - window->DC.CursorPos.y;
3064 window->DC.CursorPos.y = pos_y;
3065 window->DC.CursorMaxPos.y = ImMax(lhs: window->DC.CursorMaxPos.y, rhs: pos_y - g.Style.ItemSpacing.y);
3066 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.
3067 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.
3068 if (ImGuiOldColumns* columns = window->DC.CurrentColumns)
3069 columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
3070 if (ImGuiTable* table = g.CurrentTable)
3071 {
3072 if (table->IsInsideRow)
3073 ImGui::TableEndRow(table);
3074 table->RowPosY2 = window->DC.CursorPos.y;
3075 const int row_increase = (int)((off_y / line_height) + 0.5f);
3076 //table->CurrentRow += row_increase; // Can't do without fixing TableEndRow()
3077 table->RowBgColorCounter += row_increase;
3078 }
3079}
3080
3081ImGuiListClipper::ImGuiListClipper()
3082{
3083 memset(s: this, c: 0, n: sizeof(*this));
3084}
3085
3086ImGuiListClipper::~ImGuiListClipper()
3087{
3088 End();
3089}
3090
3091void ImGuiListClipper::Begin(int items_count, float items_height)
3092{
3093 if (Ctx == NULL)
3094 Ctx = ImGui::GetCurrentContext();
3095
3096 ImGuiContext& g = *Ctx;
3097 ImGuiWindow* window = g.CurrentWindow;
3098 IMGUI_DEBUG_LOG_CLIPPER("Clipper: Begin(%d,%.2f) in '%s'\n", items_count, items_height, window->Name);
3099
3100 if (ImGuiTable* table = g.CurrentTable)
3101 if (table->IsInsideRow)
3102 ImGui::TableEndRow(table);
3103
3104 StartPosY = window->DC.CursorPos.y;
3105 ItemsHeight = items_height;
3106 ItemsCount = items_count;
3107 DisplayStart = -1;
3108 DisplayEnd = 0;
3109
3110 // Acquire temporary buffer
3111 if (++g.ClipperTempDataStacked > g.ClipperTempData.Size)
3112 g.ClipperTempData.resize(new_size: g.ClipperTempDataStacked, v: ImGuiListClipperData());
3113 ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
3114 data->Reset(clipper: this);
3115 data->LossynessOffset = window->DC.CursorStartPosLossyness.y;
3116 TempData = data;
3117 StartSeekOffsetY = data->LossynessOffset;
3118}
3119
3120void ImGuiListClipper::End()
3121{
3122 if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData)
3123 {
3124 // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user.
3125 ImGuiContext& g = *Ctx;
3126 IMGUI_DEBUG_LOG_CLIPPER("Clipper: End() in '%s'\n", g.CurrentWindow->Name);
3127 if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0)
3128 SeekCursorForItem(item_index: ItemsCount);
3129
3130 // Restore temporary buffer and fix back pointers which may be invalidated when nesting
3131 IM_ASSERT(data->ListClipper == this);
3132 data->StepNo = data->Ranges.Size;
3133 if (--g.ClipperTempDataStacked > 0)
3134 {
3135 data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
3136 data->ListClipper->TempData = data;
3137 }
3138 TempData = NULL;
3139 }
3140 ItemsCount = -1;
3141}
3142
3143void ImGuiListClipper::IncludeItemsByIndex(int item_begin, int item_end)
3144{
3145 ImGuiListClipperData* data = (ImGuiListClipperData*)TempData;
3146 IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet.
3147 IM_ASSERT(item_begin <= item_end);
3148 if (item_begin < item_end)
3149 data->Ranges.push_back(v: ImGuiListClipperRange::FromIndices(min: item_begin, max: item_end));
3150}
3151
3152// This is already called while stepping.
3153// The ONLY reason you may want to call this is if you passed INT_MAX to ImGuiListClipper::Begin() because you couldn't step item count beforehand.
3154void ImGuiListClipper::SeekCursorForItem(int item_n)
3155{
3156 // - Perform the add and multiply with double to allow seeking through larger ranges.
3157 // - StartPosY starts from ItemsFrozen, by adding SeekOffsetY we generally cancel that out (SeekOffsetY == LossynessOffset - ItemsFrozen * ItemsHeight).
3158 // - The reason we store SeekOffsetY instead of inferring it, is because we want to allow user to perform Seek after the last step, where ImGuiListClipperData is already done.
3159 float pos_y = (float)((double)StartPosY + StartSeekOffsetY + (double)item_n * ItemsHeight);
3160 ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, line_height: ItemsHeight);
3161}
3162
3163static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
3164{
3165 ImGuiContext& g = *clipper->Ctx;
3166 ImGuiWindow* window = g.CurrentWindow;
3167 ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData;
3168 IM_ASSERT(data != NULL && "Called ImGuiListClipper::Step() too many times, or before ImGuiListClipper::Begin() ?");
3169
3170 ImGuiTable* table = g.CurrentTable;
3171 if (table && table->IsInsideRow)
3172 ImGui::TableEndRow(table);
3173
3174 // No items
3175 if (clipper->ItemsCount == 0 || GetSkipItemForListClipping())
3176 return false;
3177
3178 // While we are in frozen row state, keep displaying items one by one, unclipped
3179 // FIXME: Could be stored as a table-agnostic state.
3180 if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows)
3181 {
3182 clipper->DisplayStart = data->ItemsFrozen;
3183 clipper->DisplayEnd = ImMin(lhs: data->ItemsFrozen + 1, rhs: clipper->ItemsCount);
3184 if (clipper->DisplayStart < clipper->DisplayEnd)
3185 data->ItemsFrozen++;
3186 return true;
3187 }
3188
3189 // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
3190 bool calc_clipping = false;
3191 if (data->StepNo == 0)
3192 {
3193 clipper->StartPosY = window->DC.CursorPos.y;
3194 if (clipper->ItemsHeight <= 0.0f)
3195 {
3196 // Submit the first item (or range) so we can measure its height (generally the first range is 0..1)
3197 data->Ranges.push_front(v: ImGuiListClipperRange::FromIndices(min: data->ItemsFrozen, max: data->ItemsFrozen + 1));
3198 clipper->DisplayStart = ImMax(lhs: data->Ranges[0].Min, rhs: data->ItemsFrozen);
3199 clipper->DisplayEnd = ImMin(lhs: data->Ranges[0].Max, rhs: clipper->ItemsCount);
3200 data->StepNo = 1;
3201 return true;
3202 }
3203 calc_clipping = true; // If on the first step with known item height, calculate clipping.
3204 }
3205
3206 // Step 1: Let the clipper infer height from first range
3207 if (clipper->ItemsHeight <= 0.0f)
3208 {
3209 IM_ASSERT(data->StepNo == 1);
3210 if (table)
3211 IM_ASSERT(table->RowPosY1 == clipper->StartPosY && table->RowPosY2 == window->DC.CursorPos.y);
3212
3213 clipper->ItemsHeight = (window->DC.CursorPos.y - clipper->StartPosY) / (float)(clipper->DisplayEnd - clipper->DisplayStart);
3214 bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(f: clipper->StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(f: window->DC.CursorPos.y);
3215 if (affected_by_floating_point_precision)
3216 clipper->ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries.
3217 if (clipper->ItemsHeight == 0.0f && clipper->ItemsCount == INT_MAX) // Accept that no item have been submitted if in indeterminate mode.
3218 return false;
3219 IM_ASSERT(clipper->ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
3220 calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards.
3221 }
3222
3223 // Step 0 or 1: Calculate the actual ranges of visible elements.
3224 const int already_submitted = clipper->DisplayEnd;
3225 if (calc_clipping)
3226 {
3227 // Record seek offset, this is so ImGuiListClipper::Seek() can be called after ImGuiListClipperData is done
3228 clipper->StartSeekOffsetY = (double)data->LossynessOffset - data->ItemsFrozen * (double)clipper->ItemsHeight;
3229
3230 if (g.LogEnabled)
3231 {
3232 // If logging is active, do not perform any clipping
3233 data->Ranges.push_back(v: ImGuiListClipperRange::FromIndices(min: 0, max: clipper->ItemsCount));
3234 }
3235 else
3236 {
3237 // Add range selected to be included for navigation
3238 const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
3239 if (is_nav_request)
3240 data->Ranges.push_back(v: ImGuiListClipperRange::FromPositions(y1: g.NavScoringNoClipRect.Min.y, y2: g.NavScoringNoClipRect.Max.y, off_min: 0, off_max: 0));
3241 if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1)
3242 data->Ranges.push_back(v: ImGuiListClipperRange::FromIndices(min: clipper->ItemsCount - 1, max: clipper->ItemsCount));
3243
3244 // Add focused/active item
3245 ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, r: window->NavRectRel[0]);
3246 if (g.NavId != 0 && window->NavLastIds[0] == g.NavId)
3247 data->Ranges.push_back(v: ImGuiListClipperRange::FromPositions(y1: nav_rect_abs.Min.y, y2: nav_rect_abs.Max.y, off_min: 0, off_max: 0));
3248
3249 // Add visible range
3250 float min_y = window->ClipRect.Min.y;
3251 float max_y = window->ClipRect.Max.y;
3252
3253 // Add box selection range
3254 ImGuiBoxSelectState* bs = &g.BoxSelectState;
3255 if (bs->IsActive && bs->Window == window)
3256 {
3257 // FIXME: Selectable() use of half-ItemSpacing isn't consistent in matter of layout, as ItemAdd(bb) stray above ItemSize()'s CursorPos.
3258 // RangeSelect's BoxSelect relies on comparing overlap of previous and current rectangle and is sensitive to that.
3259 // As a workaround we currently half ItemSpacing worth on each side.
3260 min_y -= g.Style.ItemSpacing.y;
3261 max_y += g.Style.ItemSpacing.y;
3262
3263 // Box-select on 2D area requires different clipping.
3264 if (bs->UnclipMode)
3265 data->Ranges.push_back(v: ImGuiListClipperRange::FromPositions(y1: bs->UnclipRect.Min.y, y2: bs->UnclipRect.Max.y, off_min: 0, off_max: 0));
3266 }
3267
3268 const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0;
3269 const int off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0;
3270 data->Ranges.push_back(v: ImGuiListClipperRange::FromPositions(y1: min_y, y2: max_y, off_min, off_max));
3271 }
3272
3273 // Convert position ranges to item index ranges
3274 // - Very important: when a starting position is after our maximum item, we set Min to (ItemsCount - 1). This allows us to handle most forms of wrapping.
3275 // - Due to how Selectable extra padding they tend to be "unaligned" with exact unit in the item list,
3276 // which with the flooring/ceiling tend to lead to 2 items instead of one being submitted.
3277 for (ImGuiListClipperRange& range : data->Ranges)
3278 if (range.PosToIndexConvert)
3279 {
3280 int m1 = (int)(((double)range.Min - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight);
3281 int m2 = (int)((((double)range.Max - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight) + 0.999999f);
3282 range.Min = ImClamp(v: already_submitted + m1 + range.PosToIndexOffsetMin, mn: already_submitted, mx: clipper->ItemsCount - 1);
3283 range.Max = ImClamp(v: already_submitted + m2 + range.PosToIndexOffsetMax, mn: range.Min + 1, mx: clipper->ItemsCount);
3284 range.PosToIndexConvert = false;
3285 }
3286 ImGuiListClipper_SortAndFuseRanges(ranges&: data->Ranges, offset: data->StepNo);
3287 }
3288
3289 // Step 0+ (if item height is given in advance) or 1+: Display the next range in line.
3290 while (data->StepNo < data->Ranges.Size)
3291 {
3292 clipper->DisplayStart = ImMax(lhs: data->Ranges[data->StepNo].Min, rhs: already_submitted);
3293 clipper->DisplayEnd = ImMin(lhs: data->Ranges[data->StepNo].Max, rhs: clipper->ItemsCount);
3294 if (clipper->DisplayStart > already_submitted) //-V1051
3295 clipper->SeekCursorForItem(item_n: clipper->DisplayStart);
3296 data->StepNo++;
3297 if (clipper->DisplayStart == clipper->DisplayEnd && data->StepNo < data->Ranges.Size)
3298 continue;
3299 return true;
3300 }
3301
3302 // After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),
3303 // Advance the cursor to the end of the list and then returns 'false' to end the loop.
3304 if (clipper->ItemsCount < INT_MAX)
3305 clipper->SeekCursorForItem(item_n: clipper->ItemsCount);
3306
3307 return false;
3308}
3309
3310bool ImGuiListClipper::Step()
3311{
3312 ImGuiContext& g = *Ctx;
3313 bool need_items_height = (ItemsHeight <= 0.0f);
3314 bool ret = ImGuiListClipper_StepInternal(clipper: this);
3315 if (ret && (DisplayStart == DisplayEnd))
3316 ret = false;
3317 if (g.CurrentTable && g.CurrentTable->IsUnfrozenRows == false)
3318 IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): inside frozen table row.\n");
3319 if (need_items_height && ItemsHeight > 0.0f)
3320 IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): computed ItemsHeight: %.2f.\n", ItemsHeight);
3321 if (ret)
3322 {
3323 IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): display %d to %d.\n", DisplayStart, DisplayEnd);
3324 }
3325 else
3326 {
3327 IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): End.\n");
3328 End();
3329 }
3330 return ret;
3331}
3332
3333//-----------------------------------------------------------------------------
3334// [SECTION] STYLING
3335//-----------------------------------------------------------------------------
3336
3337ImGuiStyle& ImGui::GetStyle()
3338{
3339 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3340 return GImGui->Style;
3341}
3342
3343ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
3344{
3345 ImGuiStyle& style = GImGui->Style;
3346 ImVec4 c = style.Colors[idx];
3347 c.w *= style.Alpha * alpha_mul;
3348 return ColorConvertFloat4ToU32(in: c);
3349}
3350
3351ImU32 ImGui::GetColorU32(const ImVec4& col)
3352{
3353 ImGuiStyle& style = GImGui->Style;
3354 ImVec4 c = col;
3355 c.w *= style.Alpha;
3356 return ColorConvertFloat4ToU32(in: c);
3357}
3358
3359const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
3360{
3361 ImGuiStyle& style = GImGui->Style;
3362 return style.Colors[idx];
3363}
3364
3365ImU32 ImGui::GetColorU32(ImU32 col, float alpha_mul)
3366{
3367 ImGuiStyle& style = GImGui->Style;
3368 alpha_mul *= style.Alpha;
3369 if (alpha_mul >= 1.0f)
3370 return col;
3371 ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
3372 a = (ImU32)(a * alpha_mul); // We don't need to clamp 0..255 because alpha is in 0..1 range.
3373 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
3374}
3375
3376// 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
3377void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
3378{
3379 ImGuiContext& g = *GImGui;
3380 ImGuiColorMod backup;
3381 backup.Col = idx;
3382 backup.BackupValue = g.Style.Colors[idx];
3383 g.ColorStack.push_back(v: backup);
3384 if (g.DebugFlashStyleColorIdx != idx)
3385 g.Style.Colors[idx] = ColorConvertU32ToFloat4(in: col);
3386}
3387
3388void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
3389{
3390 ImGuiContext& g = *GImGui;
3391 ImGuiColorMod backup;
3392 backup.Col = idx;
3393 backup.BackupValue = g.Style.Colors[idx];
3394 g.ColorStack.push_back(v: backup);
3395 if (g.DebugFlashStyleColorIdx != idx)
3396 g.Style.Colors[idx] = col;
3397}
3398
3399void ImGui::PopStyleColor(int count)
3400{
3401 ImGuiContext& g = *GImGui;
3402 if (g.ColorStack.Size < count)
3403 {
3404 IM_ASSERT_USER_ERROR(0, "Calling PopStyleColor() too many times!");
3405 count = g.ColorStack.Size;
3406 }
3407 while (count > 0)
3408 {
3409 ImGuiColorMod& backup = g.ColorStack.back();
3410 g.Style.Colors[backup.Col] = backup.BackupValue;
3411 g.ColorStack.pop_back();
3412 count--;
3413 }
3414}
3415
3416static const ImGuiCol GWindowDockStyleColors[ImGuiWindowDockStyleCol_COUNT] =
3417{
3418 ImGuiCol_Text, ImGuiCol_TabHovered, ImGuiCol_Tab, ImGuiCol_TabSelected, ImGuiCol_TabSelectedOverline, ImGuiCol_TabDimmed, ImGuiCol_TabDimmedSelected, ImGuiCol_TabDimmedSelectedOverline,
3419};
3420
3421static const ImGuiDataVarInfo GStyleVarInfo[] =
3422{
3423 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
3424 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha
3425 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)offsetof(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
3426 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
3427 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
3428 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)offsetof(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
3429 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)offsetof(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
3430 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
3431 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
3432 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
3433 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
3434 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)offsetof(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
3435 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
3436 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
3437 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)offsetof(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
3438 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)offsetof(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
3439 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
3440 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)offsetof(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding
3441 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
3442 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
3443 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
3444 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
3445 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
3446 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, TabBorderSize) }, // ImGuiStyleVar_TabBorderSize
3447 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) }, // ImGuiStyleVar_TabBarBorderSize
3448 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, TabBarOverlineSize) }, // ImGuiStyleVar_TabBarOverlineSize
3449 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, TableAngledHeadersAngle)}, // ImGuiStyleVar_TableAngledHeadersAngle
3450 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign
3451 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
3452 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
3453 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)}, // ImGuiStyleVar_SeparatorTextBorderSize
3454 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign
3455 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) }, // ImGuiStyleVar_SeparatorTextPadding
3456 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)offsetof(ImGuiStyle, DockingSeparatorSize) }, // ImGuiStyleVar_DockingSeparatorSize
3457};
3458
3459const ImGuiDataVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx)
3460{
3461 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
3462 IM_STATIC_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
3463 return &GStyleVarInfo[idx];
3464}
3465
3466void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
3467{
3468 ImGuiContext& g = *GImGui;
3469 const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);
3470 if (var_info->Type != ImGuiDataType_Float || var_info->Count != 1)
3471 {
3472 IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3473 return;
3474 }
3475 float* pvar = (float*)var_info->GetVarPtr(parent: &g.Style);
3476 g.StyleVarStack.push_back(v: ImGuiStyleMod(idx, *pvar));
3477 *pvar = val;
3478}
3479
3480void ImGui::PushStyleVarX(ImGuiStyleVar idx, float val_x)
3481{
3482 ImGuiContext& g = *GImGui;
3483 const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);
3484 if (var_info->Type != ImGuiDataType_Float || var_info->Count != 2)
3485 {
3486 IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3487 return;
3488 }
3489 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(parent: &g.Style);
3490 g.StyleVarStack.push_back(v: ImGuiStyleMod(idx, *pvar));
3491 pvar->x = val_x;
3492}
3493
3494void ImGui::PushStyleVarY(ImGuiStyleVar idx, float val_y)
3495{
3496 ImGuiContext& g = *GImGui;
3497 const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);
3498 if (var_info->Type != ImGuiDataType_Float || var_info->Count != 2)
3499 {
3500 IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3501 return;
3502 }
3503 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(parent: &g.Style);
3504 g.StyleVarStack.push_back(v: ImGuiStyleMod(idx, *pvar));
3505 pvar->y = val_y;
3506}
3507
3508void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
3509{
3510 ImGuiContext& g = *GImGui;
3511 const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);
3512 if (var_info->Type != ImGuiDataType_Float || var_info->Count != 2)
3513 {
3514 IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3515 return;
3516 }
3517 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(parent: &g.Style);
3518 g.StyleVarStack.push_back(v: ImGuiStyleMod(idx, *pvar));
3519 *pvar = val;
3520}
3521
3522void ImGui::PopStyleVar(int count)
3523{
3524 ImGuiContext& g = *GImGui;
3525 if (g.StyleVarStack.Size < count)
3526 {
3527 IM_ASSERT_USER_ERROR(0, "Calling PopStyleVar() too many times!");
3528 count = g.StyleVarStack.Size;
3529 }
3530 while (count > 0)
3531 {
3532 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
3533 ImGuiStyleMod& backup = g.StyleVarStack.back();
3534 const ImGuiDataVarInfo* info = GetStyleVarInfo(idx: backup.VarIdx);
3535 void* data = info->GetVarPtr(parent: &g.Style);
3536 if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; }
3537 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
3538 g.StyleVarStack.pop_back();
3539 count--;
3540 }
3541}
3542
3543const char* ImGui::GetStyleColorName(ImGuiCol idx)
3544{
3545 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
3546 switch (idx)
3547 {
3548 case ImGuiCol_Text: return "Text";
3549 case ImGuiCol_TextDisabled: return "TextDisabled";
3550 case ImGuiCol_WindowBg: return "WindowBg";
3551 case ImGuiCol_ChildBg: return "ChildBg";
3552 case ImGuiCol_PopupBg: return "PopupBg";
3553 case ImGuiCol_Border: return "Border";
3554 case ImGuiCol_BorderShadow: return "BorderShadow";
3555 case ImGuiCol_FrameBg: return "FrameBg";
3556 case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
3557 case ImGuiCol_FrameBgActive: return "FrameBgActive";
3558 case ImGuiCol_TitleBg: return "TitleBg";
3559 case ImGuiCol_TitleBgActive: return "TitleBgActive";
3560 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
3561 case ImGuiCol_MenuBarBg: return "MenuBarBg";
3562 case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
3563 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
3564 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
3565 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
3566 case ImGuiCol_CheckMark: return "CheckMark";
3567 case ImGuiCol_SliderGrab: return "SliderGrab";
3568 case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
3569 case ImGuiCol_Button: return "Button";
3570 case ImGuiCol_ButtonHovered: return "ButtonHovered";
3571 case ImGuiCol_ButtonActive: return "ButtonActive";
3572 case ImGuiCol_Header: return "Header";
3573 case ImGuiCol_HeaderHovered: return "HeaderHovered";
3574 case ImGuiCol_HeaderActive: return "HeaderActive";
3575 case ImGuiCol_Separator: return "Separator";
3576 case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
3577 case ImGuiCol_SeparatorActive: return "SeparatorActive";
3578 case ImGuiCol_ResizeGrip: return "ResizeGrip";
3579 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
3580 case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
3581 case ImGuiCol_TabHovered: return "TabHovered";
3582 case ImGuiCol_Tab: return "Tab";
3583 case ImGuiCol_TabSelected: return "TabSelected";
3584 case ImGuiCol_TabSelectedOverline: return "TabSelectedOverline";
3585 case ImGuiCol_TabDimmed: return "TabDimmed";
3586 case ImGuiCol_TabDimmedSelected: return "TabDimmedSelected";
3587 case ImGuiCol_TabDimmedSelectedOverline: return "TabDimmedSelectedOverline";
3588 case ImGuiCol_DockingPreview: return "DockingPreview";
3589 case ImGuiCol_DockingEmptyBg: return "DockingEmptyBg";
3590 case ImGuiCol_PlotLines: return "PlotLines";
3591 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
3592 case ImGuiCol_PlotHistogram: return "PlotHistogram";
3593 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
3594 case ImGuiCol_TableHeaderBg: return "TableHeaderBg";
3595 case ImGuiCol_TableBorderStrong: return "TableBorderStrong";
3596 case ImGuiCol_TableBorderLight: return "TableBorderLight";
3597 case ImGuiCol_TableRowBg: return "TableRowBg";
3598 case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt";
3599 case ImGuiCol_TextLink: return "TextLink";
3600 case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
3601 case ImGuiCol_DragDropTarget: return "DragDropTarget";
3602 case ImGuiCol_NavCursor: return "NavCursor";
3603 case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
3604 case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
3605 case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
3606 }
3607 IM_ASSERT(0);
3608 return "Unknown";
3609}
3610
3611
3612//-----------------------------------------------------------------------------
3613// [SECTION] RENDER HELPERS
3614// Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
3615// we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
3616// Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
3617//-----------------------------------------------------------------------------
3618
3619const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
3620{
3621 const char* text_display_end = text;
3622 if (!text_end)
3623 text_end = (const char*)-1;
3624
3625 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
3626 text_display_end++;
3627 return text_display_end;
3628}
3629
3630// Internal ImGui functions to render text
3631// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
3632void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
3633{
3634 ImGuiContext& g = *GImGui;
3635 ImGuiWindow* window = g.CurrentWindow;
3636
3637 // Hide anything after a '##' string
3638 const char* text_display_end;
3639 if (hide_text_after_hash)
3640 {
3641 text_display_end = FindRenderedTextEnd(text, text_end);
3642 }
3643 else
3644 {
3645 if (!text_end)
3646 text_end = text + strlen(s: text); // FIXME-OPT
3647 text_display_end = text_end;
3648 }
3649
3650 if (text != text_display_end)
3651 {
3652 window->DrawList->AddText(font: g.Font, font_size: g.FontSize, pos, col: GetColorU32(idx: ImGuiCol_Text), text_begin: text, text_end: text_display_end);
3653 if (g.LogEnabled)
3654 LogRenderedText(ref_pos: &pos, text, text_end: text_display_end);
3655 }
3656}
3657
3658void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
3659{
3660 ImGuiContext& g = *GImGui;
3661 ImGuiWindow* window = g.CurrentWindow;
3662
3663 if (!text_end)
3664 text_end = text + strlen(s: text); // FIXME-OPT
3665
3666 if (text != text_end)
3667 {
3668 window->DrawList->AddText(font: g.Font, font_size: g.FontSize, pos, col: GetColorU32(idx: ImGuiCol_Text), text_begin: text, text_end, wrap_width);
3669 if (g.LogEnabled)
3670 LogRenderedText(ref_pos: &pos, text, text_end);
3671 }
3672}
3673
3674// Default clip_rect uses (pos_min,pos_max)
3675// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
3676// FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especally for text above draw_list->DrawList.
3677// Effectively as this is called from widget doing their own coarse clipping it's not very valuable presently. Next time function will take
3678// better advantage of the render function taking size into account for coarse clipping.
3679void 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)
3680{
3681 // Perform CPU side clipping for single clipped element to avoid using scissor state
3682 ImVec2 pos = pos_min;
3683 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end: text_display_end, hide_text_after_double_hash: false, wrap_width: 0.0f);
3684
3685 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
3686 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
3687 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
3688 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
3689 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
3690
3691 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
3692 if (align.x > 0.0f) pos.x = ImMax(lhs: pos.x, rhs: pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
3693 if (align.y > 0.0f) pos.y = ImMax(lhs: pos.y, rhs: pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
3694
3695 // Render
3696 if (need_clipping)
3697 {
3698 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
3699 draw_list->AddText(NULL, font_size: 0.0f, pos, col: GetColorU32(idx: ImGuiCol_Text), text_begin: text, text_end: text_display_end, wrap_width: 0.0f, cpu_fine_clip_rect: &fine_clip_rect);
3700 }
3701 else
3702 {
3703 draw_list->AddText(NULL, font_size: 0.0f, pos, col: GetColorU32(idx: ImGuiCol_Text), text_begin: text, text_end: text_display_end, wrap_width: 0.0f, NULL);
3704 }
3705}
3706
3707void 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)
3708{
3709 // Hide anything after a '##' string
3710 const char* text_display_end = FindRenderedTextEnd(text, text_end);
3711 const int text_len = (int)(text_display_end - text);
3712 if (text_len == 0)
3713 return;
3714
3715 ImGuiContext& g = *GImGui;
3716 ImGuiWindow* window = g.CurrentWindow;
3717 RenderTextClippedEx(draw_list: window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
3718 if (g.LogEnabled)
3719 LogRenderedText(ref_pos: &pos_min, text, text_end: text_display_end);
3720}
3721
3722// Another overly complex function until we reorganize everything into a nice all-in-one helper.
3723// 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.
3724// 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.
3725void 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)
3726{
3727 ImGuiContext& g = *GImGui;
3728 if (text_end_full == NULL)
3729 text_end_full = FindRenderedTextEnd(text);
3730 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end: text_end_full, hide_text_after_double_hash: false, wrap_width: 0.0f);
3731
3732 //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));
3733 //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));
3734 //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
3735 // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
3736 if (text_size.x > pos_max.x - pos_min.x)
3737 {
3738 // Hello wo...
3739 // | | |
3740 // min max ellipsis_max
3741 // <-> this is generally some padding value
3742
3743 ImFont* font = draw_list->_Data->Font;
3744 const float font_size = draw_list->_Data->FontSize;
3745 const float font_scale = draw_list->_Data->FontScale;
3746 const char* text_end_ellipsis = NULL;
3747 const float ellipsis_width = font->EllipsisWidth * font_scale;
3748
3749 // We can now claim the space between pos_max.x and ellipsis_max.x
3750 const float text_avail_width = ImMax(lhs: (ImMax(lhs: pos_max.x, rhs: ellipsis_max_x) - ellipsis_width) - pos_min.x, rhs: 1.0f);
3751 float text_size_clipped_x = font->CalcTextSizeA(size: font_size, max_width: text_avail_width, wrap_width: 0.0f, text_begin: text, text_end: text_end_full, remaining: &text_end_ellipsis).x;
3752 if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
3753 {
3754 // Always display at least 1 character if there's no room for character + ellipsis
3755 text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(in_text: text, in_text_end: text_end_full);
3756 text_size_clipped_x = font->CalcTextSizeA(size: font_size, FLT_MAX, wrap_width: 0.0f, text_begin: text, text_end: text_end_ellipsis).x;
3757 }
3758 while (text_end_ellipsis > text && ImCharIsBlankA(c: text_end_ellipsis[-1]))
3759 {
3760 // 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)
3761 text_end_ellipsis--;
3762 text_size_clipped_x -= font->CalcTextSizeA(size: font_size, FLT_MAX, wrap_width: 0.0f, text_begin: text_end_ellipsis, text_end: text_end_ellipsis + 1).x; // Ascii blanks are always 1 byte
3763 }
3764
3765 // Render text, render ellipsis
3766 RenderTextClippedEx(draw_list, pos_min, pos_max: ImVec2(clip_max_x, pos_max.y), text, text_display_end: text_end_ellipsis, text_size_if_known: &text_size, align: ImVec2(0.0f, 0.0f));
3767 ImVec2 ellipsis_pos = ImTrunc(v: ImVec2(pos_min.x + text_size_clipped_x, pos_min.y));
3768 if (ellipsis_pos.x + ellipsis_width <= ellipsis_max_x)
3769 for (int i = 0; i < font->EllipsisCharCount; i++, ellipsis_pos.x += font->EllipsisCharStep * font_scale)
3770 font->RenderChar(draw_list, size: font_size, pos: ellipsis_pos, col: GetColorU32(idx: ImGuiCol_Text), c: font->EllipsisChar);
3771 }
3772 else
3773 {
3774 RenderTextClippedEx(draw_list, pos_min, pos_max: ImVec2(clip_max_x, pos_max.y), text, text_display_end: text_end_full, text_size_if_known: &text_size, align: ImVec2(0.0f, 0.0f));
3775 }
3776
3777 if (g.LogEnabled)
3778 LogRenderedText(ref_pos: &pos_min, text, text_end: text_end_full);
3779}
3780
3781// Render a rectangle shaped with optional rounding and borders
3782void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders, float rounding)
3783{
3784 ImGuiContext& g = *GImGui;
3785 ImGuiWindow* window = g.CurrentWindow;
3786 window->DrawList->AddRectFilled(p_min, p_max, col: fill_col, rounding);
3787 const float border_size = g.Style.FrameBorderSize;
3788 if (borders && border_size > 0.0f)
3789 {
3790 window->DrawList->AddRect(p_min: p_min + ImVec2(1, 1), p_max: p_max + ImVec2(1, 1), col: GetColorU32(idx: ImGuiCol_BorderShadow), rounding, flags: 0, thickness: border_size);
3791 window->DrawList->AddRect(p_min, p_max, col: GetColorU32(idx: ImGuiCol_Border), rounding, flags: 0, thickness: border_size);
3792 }
3793}
3794
3795void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
3796{
3797 ImGuiContext& g = *GImGui;
3798 ImGuiWindow* window = g.CurrentWindow;
3799 const float border_size = g.Style.FrameBorderSize;
3800 if (border_size > 0.0f)
3801 {
3802 window->DrawList->AddRect(p_min: p_min + ImVec2(1, 1), p_max: p_max + ImVec2(1, 1), col: GetColorU32(idx: ImGuiCol_BorderShadow), rounding, flags: 0, thickness: border_size);
3803 window->DrawList->AddRect(p_min, p_max, col: GetColorU32(idx: ImGuiCol_Border), rounding, flags: 0, thickness: border_size);
3804 }
3805}
3806
3807void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags)
3808{
3809 ImGuiContext& g = *GImGui;
3810 if (id != g.NavId)
3811 return;
3812 if (!g.NavCursorVisible && !(flags & ImGuiNavRenderCursorFlags_AlwaysDraw))
3813 return;
3814 if (id == g.LastItemData.ID && (g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))
3815 return;
3816 ImGuiWindow* window = g.CurrentWindow;
3817 if (window->DC.NavHideHighlightOneFrame)
3818 return;
3819
3820 float rounding = (flags & ImGuiNavRenderCursorFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
3821 ImRect display_rect = bb;
3822 display_rect.ClipWith(r: window->ClipRect);
3823 const float thickness = 2.0f;
3824 if (flags & ImGuiNavRenderCursorFlags_Compact)
3825 {
3826 window->DrawList->AddRect(p_min: display_rect.Min, p_max: display_rect.Max, col: GetColorU32(idx: ImGuiCol_NavCursor), rounding, flags: 0, thickness);
3827 }
3828 else
3829 {
3830 const float distance = 3.0f + thickness * 0.5f;
3831 display_rect.Expand(amount: ImVec2(distance, distance));
3832 bool fully_visible = window->ClipRect.Contains(r: display_rect);
3833 if (!fully_visible)
3834 window->DrawList->PushClipRect(clip_rect_min: display_rect.Min, clip_rect_max: display_rect.Max);
3835 window->DrawList->AddRect(p_min: display_rect.Min, p_max: display_rect.Max, col: GetColorU32(idx: ImGuiCol_NavCursor), rounding, flags: 0, thickness);
3836 if (!fully_visible)
3837 window->DrawList->PopClipRect();
3838 }
3839}
3840
3841void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow)
3842{
3843 ImGuiContext& g = *GImGui;
3844 if (mouse_cursor <= ImGuiMouseCursor_None || mouse_cursor >= ImGuiMouseCursor_COUNT) // We intentionally accept out of bound values.
3845 mouse_cursor = ImGuiMouseCursor_Arrow;
3846 ImFontAtlas* font_atlas = g.DrawListSharedData.Font->ContainerAtlas;
3847 for (ImGuiViewportP* viewport : g.Viewports)
3848 {
3849 // We scale cursor with current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor.
3850 ImVec2 offset, size, uv[4];
3851 if (!font_atlas->GetMouseCursorTexData(cursor: mouse_cursor, out_offset: &offset, out_size: &size, out_uv_border: &uv[0], out_uv_fill: &uv[2]))
3852 continue;
3853 const ImVec2 pos = base_pos - offset;
3854 const float scale = base_scale * viewport->DpiScale;
3855 if (!viewport->GetMainRect().Overlaps(r: ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale)))
3856 continue;
3857 ImDrawList* draw_list = GetForegroundDrawList(viewport);
3858 ImTextureID tex_id = font_atlas->TexID;
3859 draw_list->PushTextureID(texture_id: tex_id);
3860 draw_list->AddImage(user_texture_id: tex_id, p_min: pos + ImVec2(1, 0) * scale, p_max: pos + (ImVec2(1, 0) + size) * scale, uv_min: uv[2], uv_max: uv[3], col: col_shadow);
3861 draw_list->AddImage(user_texture_id: tex_id, p_min: pos + ImVec2(2, 0) * scale, p_max: pos + (ImVec2(2, 0) + size) * scale, uv_min: uv[2], uv_max: uv[3], col: col_shadow);
3862 draw_list->AddImage(user_texture_id: tex_id, p_min: pos, p_max: pos + size * scale, uv_min: uv[2], uv_max: uv[3], col: col_border);
3863 draw_list->AddImage(user_texture_id: tex_id, p_min: pos, p_max: pos + size * scale, uv_min: uv[0], uv_max: uv[1], col: col_fill);
3864 draw_list->PopTextureID();
3865 }
3866}
3867
3868//-----------------------------------------------------------------------------
3869// [SECTION] INITIALIZATION, SHUTDOWN
3870//-----------------------------------------------------------------------------
3871
3872// Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3873// 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
3874ImGuiContext* ImGui::GetCurrentContext()
3875{
3876 return GImGui;
3877}
3878
3879void ImGui::SetCurrentContext(ImGuiContext* ctx)
3880{
3881#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3882 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3883#else
3884 GImGui = ctx;
3885#endif
3886}
3887
3888void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data)
3889{
3890 GImAllocatorAllocFunc = alloc_func;
3891 GImAllocatorFreeFunc = free_func;
3892 GImAllocatorUserData = user_data;
3893}
3894
3895// This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space)
3896void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data)
3897{
3898 *p_alloc_func = GImAllocatorAllocFunc;
3899 *p_free_func = GImAllocatorFreeFunc;
3900 *p_user_data = GImAllocatorUserData;
3901}
3902
3903ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3904{
3905 ImGuiContext* prev_ctx = GetCurrentContext();
3906 ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3907 SetCurrentContext(ctx);
3908 Initialize();
3909 if (prev_ctx != NULL)
3910 SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one.
3911 return ctx;
3912}
3913
3914void ImGui::DestroyContext(ImGuiContext* ctx)
3915{
3916 ImGuiContext* prev_ctx = GetCurrentContext();
3917 if (ctx == NULL) //-V1051
3918 ctx = prev_ctx;
3919 SetCurrentContext(ctx);
3920 Shutdown();
3921 SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL);
3922 IM_DELETE(p: ctx);
3923}
3924
3925// IMPORTANT: interactive elements requires a fixed ###xxx suffix, it must be same in ALL languages to allow for automation.
3926static const ImGuiLocEntry GLocalizationEntriesEnUS[] =
3927{
3928 { .Key: ImGuiLocKey_VersionStr, .Text: "Dear ImGui " IMGUI_VERSION " (" IM_STRINGIFY(IMGUI_VERSION_NUM) ")" },
3929 { .Key: ImGuiLocKey_TableSizeOne, .Text: "Size column to fit###SizeOne" },
3930 { .Key: ImGuiLocKey_TableSizeAllFit, .Text: "Size all columns to fit###SizeAll" },
3931 { .Key: ImGuiLocKey_TableSizeAllDefault, .Text: "Size all columns to default###SizeAll" },
3932 { .Key: ImGuiLocKey_TableResetOrder, .Text: "Reset order###ResetOrder" },
3933 { .Key: ImGuiLocKey_WindowingMainMenuBar, .Text: "(Main menu bar)" },
3934 { .Key: ImGuiLocKey_WindowingPopup, .Text: "(Popup)" },
3935 { .Key: ImGuiLocKey_WindowingUntitled, .Text: "(Untitled)" },
3936 { .Key: ImGuiLocKey_OpenLink_s, .Text: "Open '%s'" },
3937 { .Key: ImGuiLocKey_CopyLink, .Text: "Copy Link###CopyLink" },
3938 { .Key: ImGuiLocKey_DockingHideTabBar, .Text: "Hide tab bar###HideTabBar" },
3939 { .Key: ImGuiLocKey_DockingHoldShiftToDock, .Text: "Hold SHIFT to enable Docking window." },
3940 { .Key: ImGuiLocKey_DockingDragToUndockOrMoveNode,.Text: "Click and drag to move or undock whole node." },
3941};
3942
3943ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
3944{
3945 IO.Ctx = this;
3946 InputTextState.Ctx = this;
3947
3948 Initialized = false;
3949 ConfigFlagsCurrFrame = ConfigFlagsLastFrame = ImGuiConfigFlags_None;
3950 FontAtlasOwnedByContext = shared_font_atlas ? false : true;
3951 Font = NULL;
3952 FontSize = FontBaseSize = FontScale = CurrentDpiScale = 0.0f;
3953 IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();
3954 Time = 0.0f;
3955 FrameCount = 0;
3956 FrameCountEnded = FrameCountPlatformEnded = FrameCountRendered = -1;
3957 WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false;
3958 GcCompactAll = false;
3959 TestEngineHookItems = false;
3960 TestEngine = NULL;
3961 memset(s: ContextName, c: 0, n: sizeof(ContextName));
3962
3963 InputEventsNextMouseSource = ImGuiMouseSource_Mouse;
3964 InputEventsNextEventId = 1;
3965
3966 WindowsActiveCount = 0;
3967 CurrentWindow = NULL;
3968 HoveredWindow = NULL;
3969 HoveredWindowUnderMovingWindow = NULL;
3970 HoveredWindowBeforeClear = NULL;
3971 MovingWindow = NULL;
3972 WheelingWindow = NULL;
3973 WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1;
3974 WheelingWindowReleaseTimer = 0.0f;
3975
3976 DebugDrawIdConflicts = 0;
3977 DebugHookIdInfo = 0;
3978 HoveredId = HoveredIdPreviousFrame = 0;
3979 HoveredIdPreviousFrameItemCount = 0;
3980 HoveredIdAllowOverlap = false;
3981 HoveredIdIsDisabled = false;
3982 HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f;
3983 ItemUnclipByLog = false;
3984 ActiveId = 0;
3985 ActiveIdIsAlive = 0;
3986 ActiveIdTimer = 0.0f;
3987 ActiveIdIsJustActivated = false;
3988 ActiveIdAllowOverlap = false;
3989 ActiveIdNoClearOnFocusLoss = false;
3990 ActiveIdHasBeenPressedBefore = false;
3991 ActiveIdHasBeenEditedBefore = false;
3992 ActiveIdHasBeenEditedThisFrame = false;
3993 ActiveIdFromShortcut = false;
3994 ActiveIdClickOffset = ImVec2(-1, -1);
3995 ActiveIdWindow = NULL;
3996 ActiveIdSource = ImGuiInputSource_None;
3997 ActiveIdMouseButton = -1;
3998 ActiveIdPreviousFrame = 0;
3999 ActiveIdPreviousFrameIsAlive = false;
4000 ActiveIdPreviousFrameHasBeenEditedBefore = false;
4001 ActiveIdPreviousFrameWindow = NULL;
4002 LastActiveId = 0;
4003 LastActiveIdTimer = 0.0f;
4004
4005 LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0;
4006
4007 ActiveIdUsingNavDirMask = 0x00;
4008 ActiveIdUsingAllKeyboardKeys = false;
4009
4010 CurrentFocusScopeId = 0;
4011 CurrentItemFlags = ImGuiItemFlags_None;
4012 DebugShowGroupRects = false;
4013
4014 CurrentViewport = NULL;
4015 MouseViewport = MouseLastHoveredViewport = NULL;
4016 PlatformLastFocusedViewportId = 0;
4017 ViewportCreatedCount = PlatformWindowsCreatedCount = 0;
4018 ViewportFocusedStampCount = 0;
4019
4020 NavCursorVisible = false;
4021 NavHighlightItemUnderNav = false;
4022 NavMousePosDirty = false;
4023 NavIdIsAlive = false;
4024 NavId = 0;
4025 NavWindow = NULL;
4026 NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0;
4027 NavLayer = ImGuiNavLayer_Main;
4028 NavNextActivateId = 0;
4029 NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;
4030 NavHighlightActivatedId = 0;
4031 NavHighlightActivatedTimer = 0.0f;
4032 NavInputSource = ImGuiInputSource_Keyboard;
4033 NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
4034 NavCursorHideFrames = 0;
4035
4036 NavAnyRequest = false;
4037 NavInitRequest = false;
4038 NavInitRequestFromMove = false;
4039 NavMoveSubmitted = false;
4040 NavMoveScoringItems = false;
4041 NavMoveForwardToNextFrame = false;
4042 NavMoveFlags = ImGuiNavMoveFlags_None;
4043 NavMoveScrollFlags = ImGuiScrollFlags_None;
4044 NavMoveKeyMods = ImGuiMod_None;
4045 NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None;
4046 NavScoringDebugCount = 0;
4047 NavTabbingDir = 0;
4048 NavTabbingCounter = 0;
4049
4050 NavJustMovedFromFocusScopeId = NavJustMovedToId = NavJustMovedToFocusScopeId = 0;
4051 NavJustMovedToKeyMods = ImGuiMod_None;
4052 NavJustMovedToIsTabbing = false;
4053 NavJustMovedToHasSelectionData = false;
4054
4055 // All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac...
4056 // FIXME: Because this value is stored, it annoyingly interfere with toggling io.ConfigMacOSXBehaviors updating this..
4057 ConfigNavWindowingKeyNext = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiKey_Tab);
4058 ConfigNavWindowingKeyPrev = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab);
4059 NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL;
4060 NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;
4061 NavWindowingToggleLayer = false;
4062 NavWindowingToggleKey = ImGuiKey_None;
4063
4064 DimBgRatio = 0.0f;
4065
4066 DragDropActive = DragDropWithinSource = DragDropWithinTarget = false;
4067 DragDropSourceFlags = ImGuiDragDropFlags_None;
4068 DragDropSourceFrameCount = -1;
4069 DragDropMouseButton = -1;
4070 DragDropTargetId = 0;
4071 DragDropAcceptFlags = ImGuiDragDropFlags_None;
4072 DragDropAcceptIdCurrRectSurface = 0.0f;
4073 DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0;
4074 DragDropAcceptFrameCount = -1;
4075 DragDropHoldJustPressedId = 0;
4076 memset(s: DragDropPayloadBufLocal, c: 0, n: sizeof(DragDropPayloadBufLocal));
4077
4078 ClipperTempDataStacked = 0;
4079
4080 CurrentTable = NULL;
4081 TablesTempDataStacked = 0;
4082 CurrentTabBar = NULL;
4083 CurrentMultiSelect = NULL;
4084 MultiSelectTempDataStacked = 0;
4085
4086 HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0;
4087 HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f;
4088
4089 MouseCursor = ImGuiMouseCursor_Arrow;
4090 MouseStationaryTimer = 0.0f;
4091
4092 TempInputId = 0;
4093 memset(s: &DataTypeZeroValue, c: 0, n: sizeof(DataTypeZeroValue));
4094 BeginMenuDepth = BeginComboDepth = 0;
4095 ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_;
4096 ColorEditCurrentID = ColorEditSavedID = 0;
4097 ColorEditSavedHue = ColorEditSavedSat = 0.0f;
4098 ColorEditSavedColor = 0;
4099 WindowResizeRelativeMode = false;
4100 ScrollbarSeekMode = 0;
4101 ScrollbarClickDeltaToGrabCenter = 0.0f;
4102 SliderGrabClickOffset = 0.0f;
4103 SliderCurrentAccum = 0.0f;
4104 SliderCurrentAccumDirty = false;
4105 DragCurrentAccumDirty = false;
4106 DragCurrentAccum = 0.0f;
4107 DragSpeedDefaultRatio = 1.0f / 100.0f;
4108 DisabledAlphaBackup = 0.0f;
4109 DisabledStackSize = 0;
4110 TooltipOverrideCount = 0;
4111 TooltipPreviousWindow = NULL;
4112
4113 PlatformImeData.InputPos = ImVec2(0.0f, 0.0f);
4114 PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission
4115 PlatformImeViewport = 0;
4116
4117 DockNodeWindowMenuHandler = NULL;
4118
4119 SettingsLoaded = false;
4120 SettingsDirtyTimer = 0.0f;
4121 HookIdNext = 0;
4122
4123 memset(s: LocalizationTable, c: 0, n: sizeof(LocalizationTable));
4124
4125 LogEnabled = false;
4126 LogFlags = ImGuiLogFlags_None;
4127 LogWindow = NULL;
4128 LogNextPrefix = LogNextSuffix = NULL;
4129 LogFile = NULL;
4130 LogLinePosY = FLT_MAX;
4131 LogLineFirstItem = false;
4132 LogDepthRef = 0;
4133 LogDepthToExpand = LogDepthToExpandDefault = 2;
4134
4135 ErrorCallback = NULL;
4136 ErrorCallbackUserData = NULL;
4137 ErrorFirst = true;
4138 ErrorCountCurrentFrame = 0;
4139 StackSizesInBeginForCurrentWindow = NULL;
4140
4141 DebugDrawIdConflictsCount = 0;
4142 DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY | ImGuiDebugLogFlags_EventFont;
4143 DebugLocateId = 0;
4144 DebugLogSkippedErrors = 0;
4145 DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
4146 DebugLogAutoDisableFrames = 0;
4147 DebugLocateFrames = 0;
4148 DebugBeginReturnValueCullDepth = -1;
4149 DebugItemPickerActive = false;
4150 DebugItemPickerMouseButton = ImGuiMouseButton_Left;
4151 DebugItemPickerBreakId = 0;
4152 DebugFlashStyleColorTime = 0.0f;
4153 DebugFlashStyleColorIdx = ImGuiCol_COUNT;
4154 DebugHoveredDockNode = NULL;
4155
4156 // Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations
4157 DebugBreakInWindow = 0;
4158 DebugBreakInTable = 0;
4159 DebugBreakInLocateId = false;
4160 DebugBreakKeyChord = ImGuiKey_Pause;
4161 DebugBreakInShortcutRouting = ImGuiKey_None;
4162
4163 memset(s: FramerateSecPerFrame, c: 0, n: sizeof(FramerateSecPerFrame));
4164 FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0;
4165 FramerateSecPerFrameAccum = 0.0f;
4166 WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1;
4167 memset(s: TempKeychordName, c: 0, n: sizeof(TempKeychordName));
4168}
4169
4170void ImGui::Initialize()
4171{
4172 ImGuiContext& g = *GImGui;
4173 IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
4174
4175 // Add .ini handle for ImGuiWindow and ImGuiTable types
4176 {
4177 ImGuiSettingsHandler ini_handler;
4178 ini_handler.TypeName = "Window";
4179 ini_handler.TypeHash = ImHashStr(data_p: "Window");
4180 ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
4181 ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
4182 ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
4183 ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
4184 ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
4185 AddSettingsHandler(handler: &ini_handler);
4186 }
4187 TableSettingsAddSettingsHandler();
4188
4189 // Setup default localization table
4190 LocalizeRegisterEntries(entries: GLocalizationEntriesEnUS, IM_ARRAYSIZE(GLocalizationEntriesEnUS));
4191
4192 // Setup default ImGuiPlatformIO clipboard/IME handlers.
4193 g.PlatformIO.Platform_GetClipboardTextFn = Platform_GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
4194 g.PlatformIO.Platform_SetClipboardTextFn = Platform_SetClipboardTextFn_DefaultImpl;
4195 g.PlatformIO.Platform_OpenInShellFn = Platform_OpenInShellFn_DefaultImpl;
4196 g.PlatformIO.Platform_SetImeDataFn = Platform_SetImeDataFn_DefaultImpl;
4197
4198 // Create default viewport
4199 ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
4200 viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID;
4201 viewport->Idx = 0;
4202 viewport->PlatformWindowCreated = true;
4203 viewport->Flags = ImGuiViewportFlags_OwnedByApp;
4204 g.Viewports.push_back(v: viewport);
4205 g.TempBuffer.resize(new_size: 1024 * 3 + 1, v: 0);
4206 g.ViewportCreatedCount++;
4207 g.PlatformIO.Viewports.push_back(v: g.Viewports[0]);
4208
4209 // Build KeysMayBeCharInput[] lookup table (1 bool per named key)
4210 for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
4211 if ((key >= ImGuiKey_0 && key <= ImGuiKey_9) || (key >= ImGuiKey_A && key <= ImGuiKey_Z) || (key >= ImGuiKey_Keypad0 && key <= ImGuiKey_Keypad9)
4212 || key == ImGuiKey_Tab || key == ImGuiKey_Space || key == ImGuiKey_Apostrophe || key == ImGuiKey_Comma || key == ImGuiKey_Minus || key == ImGuiKey_Period
4213 || key == ImGuiKey_Slash || key == ImGuiKey_Semicolon || key == ImGuiKey_Equal || key == ImGuiKey_LeftBracket || key == ImGuiKey_RightBracket || key == ImGuiKey_GraveAccent
4214 || key == ImGuiKey_KeypadDecimal || key == ImGuiKey_KeypadDivide || key == ImGuiKey_KeypadMultiply || key == ImGuiKey_KeypadSubtract || key == ImGuiKey_KeypadAdd || key == ImGuiKey_KeypadEqual)
4215 g.KeysMayBeCharInput.SetBit(key);
4216
4217#ifdef IMGUI_HAS_DOCK
4218 // Initialize Docking
4219 DockContextInitialize(ctx: &g);
4220#endif
4221
4222 g.Initialized = true;
4223}
4224
4225// This function is merely here to free heap allocations.
4226void ImGui::Shutdown()
4227{
4228 ImGuiContext& g = *GImGui;
4229 IM_ASSERT_USER_ERROR(g.IO.BackendPlatformUserData == NULL, "Forgot to shutdown Platform backend?");
4230 IM_ASSERT_USER_ERROR(g.IO.BackendRendererUserData == NULL, "Forgot to shutdown Renderer backend?");
4231
4232 // 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)
4233 if (g.IO.Fonts && g.FontAtlasOwnedByContext)
4234 {
4235 g.IO.Fonts->Locked = false;
4236 IM_DELETE(p: g.IO.Fonts);
4237 }
4238 g.IO.Fonts = NULL;
4239 g.DrawListSharedData.TempBuffer.clear();
4240
4241 // Cleanup of other data are conditional on actually having initialized Dear ImGui.
4242 if (!g.Initialized)
4243 return;
4244
4245 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
4246 if (g.SettingsLoaded && g.IO.IniFilename != NULL)
4247 SaveIniSettingsToDisk(ini_filename: g.IO.IniFilename);
4248
4249 // Destroy platform windows
4250 DestroyPlatformWindows();
4251
4252 // Shutdown extensions
4253 DockContextShutdown(ctx: &g);
4254
4255 CallContextHooks(context: &g, type: ImGuiContextHookType_Shutdown);
4256
4257 // Clear everything else
4258 g.Windows.clear_delete();
4259 g.WindowsFocusOrder.clear();
4260 g.WindowsTempSortBuffer.clear();
4261 g.CurrentWindow = NULL;
4262 g.CurrentWindowStack.clear();
4263 g.WindowsById.Clear();
4264 g.NavWindow = NULL;
4265 g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
4266 g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
4267 g.MovingWindow = NULL;
4268
4269 g.KeysRoutingTable.Clear();
4270
4271 g.ColorStack.clear();
4272 g.StyleVarStack.clear();
4273 g.FontStack.clear();
4274 g.OpenPopupStack.clear();
4275 g.BeginPopupStack.clear();
4276 g.TreeNodeStack.clear();
4277
4278 g.CurrentViewport = g.MouseViewport = g.MouseLastHoveredViewport = NULL;
4279 g.Viewports.clear_delete();
4280
4281 g.TabBars.Clear();
4282 g.CurrentTabBarStack.clear();
4283 g.ShrinkWidthBuffer.clear();
4284
4285 g.ClipperTempData.clear_destruct();
4286
4287 g.Tables.Clear();
4288 g.TablesTempData.clear_destruct();
4289 g.DrawChannelsTempMergeBuffer.clear();
4290
4291 g.MultiSelectStorage.Clear();
4292 g.MultiSelectTempData.clear_destruct();
4293
4294 g.ClipboardHandlerData.clear();
4295 g.MenusIdSubmittedThisFrame.clear();
4296 g.InputTextState.ClearFreeMemory();
4297 g.InputTextDeactivatedState.ClearFreeMemory();
4298
4299 g.SettingsWindows.clear();
4300 g.SettingsHandlers.clear();
4301
4302 if (g.LogFile)
4303 {
4304#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
4305 if (g.LogFile != stdout)
4306#endif
4307 ImFileClose(f: g.LogFile);
4308 g.LogFile = NULL;
4309 }
4310 g.LogBuffer.clear();
4311 g.DebugLogBuf.clear();
4312 g.DebugLogIndex.clear();
4313
4314 g.Initialized = false;
4315}
4316
4317// No specific ordering/dependency support, will see as needed
4318ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook)
4319{
4320 ImGuiContext& g = *ctx;
4321 IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_);
4322 g.Hooks.push_back(v: *hook);
4323 g.Hooks.back().HookId = ++g.HookIdNext;
4324 return g.HookIdNext;
4325}
4326
4327// Deferred removal, avoiding issue with changing vector while iterating it
4328void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id)
4329{
4330 ImGuiContext& g = *ctx;
4331 IM_ASSERT(hook_id != 0);
4332 for (ImGuiContextHook& hook : g.Hooks)
4333 if (hook.HookId == hook_id)
4334 hook.Type = ImGuiContextHookType_PendingRemoval_;
4335}
4336
4337// Call context hooks (used by e.g. test engine)
4338// We assume a small number of hooks so all stored in same array
4339void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)
4340{
4341 ImGuiContext& g = *ctx;
4342 for (ImGuiContextHook& hook : g.Hooks)
4343 if (hook.Type == hook_type)
4344 hook.Callback(&g, &hook);
4345}
4346
4347
4348//-----------------------------------------------------------------------------
4349// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
4350//-----------------------------------------------------------------------------
4351
4352// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
4353ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NULL)
4354{
4355 memset(s: this, c: 0, n: sizeof(*this));
4356 Ctx = ctx;
4357 Name = ImStrdup(str: name);
4358 NameBufLen = (int)strlen(s: name) + 1;
4359 ID = ImHashStr(data_p: name);
4360 IDStack.push_back(v: ID);
4361 ViewportAllowPlatformMonitorExtend = -1;
4362 ViewportPos = ImVec2(FLT_MAX, FLT_MAX);
4363 MoveId = GetID(str: "#MOVE");
4364 TabId = GetID(str: "#TAB");
4365 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
4366 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
4367 AutoFitFramesX = AutoFitFramesY = -1;
4368 AutoPosLastDirection = ImGuiDir_None;
4369 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = SetWindowDockAllowFlags = 0;
4370 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
4371 LastFrameActive = -1;
4372 LastFrameJustFocused = -1;
4373 LastTimeActive = -1.0f;
4374 FontWindowScale = FontDpiScale = 1.0f;
4375 SettingsOffset = -1;
4376 DockOrder = -1;
4377 DrawList = &DrawListInst;
4378 DrawList->_OwnerName = Name;
4379 DrawList->_Data = &Ctx->DrawListSharedData;
4380 NavPreferredScoringPosRel[0] = NavPreferredScoringPosRel[1] = ImVec2(FLT_MAX, FLT_MAX);
4381 IM_PLACEMENT_NEW(&WindowClass) ImGuiWindowClass();
4382}
4383
4384ImGuiWindow::~ImGuiWindow()
4385{
4386 IM_ASSERT(DrawList == &DrawListInst);
4387 IM_DELETE(p: Name);
4388 ColumnsStorage.clear_destruct();
4389}
4390
4391static void SetCurrentWindow(ImGuiWindow* window)
4392{
4393 ImGuiContext& g = *GImGui;
4394 g.CurrentWindow = window;
4395 g.StackSizesInBeginForCurrentWindow = g.CurrentWindow ? &g.CurrentWindowStack.back().StackSizesInBegin : NULL;
4396 g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(n: window->DC.CurrentTableIdx) : NULL;
4397 if (window)
4398 {
4399 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
4400 g.FontScale = g.DrawListSharedData.FontScale = g.FontSize / g.Font->FontSize;
4401 ImGui::NavUpdateCurrentWindowIsScrollPushableX();
4402 }
4403}
4404
4405void ImGui::GcCompactTransientMiscBuffers()
4406{
4407 ImGuiContext& g = *GImGui;
4408 g.ItemFlagsStack.clear();
4409 g.GroupStack.clear();
4410 g.MultiSelectTempDataStacked = 0;
4411 g.MultiSelectTempData.clear_destruct();
4412 TableGcCompactSettings();
4413}
4414
4415// Free up/compact internal window buffers, we can use this when a window becomes unused.
4416// Not freed:
4417// - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)
4418// This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
4419void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
4420{
4421 window->MemoryCompacted = true;
4422 window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
4423 window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
4424 window->IDStack.clear();
4425 window->DrawList->_ClearFreeMemory();
4426 window->DC.ChildWindows.clear();
4427 window->DC.ItemWidthStack.clear();
4428 window->DC.TextWrapPosStack.clear();
4429}
4430
4431void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
4432{
4433 // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
4434 // The other buffers tends to amortize much faster.
4435 window->MemoryCompacted = false;
4436 window->DrawList->IdxBuffer.reserve(new_capacity: window->MemoryDrawListIdxCapacity);
4437 window->DrawList->VtxBuffer.reserve(new_capacity: window->MemoryDrawListVtxCapacity);
4438 window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
4439}
4440
4441void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
4442{
4443 ImGuiContext& g = *GImGui;
4444
4445 // Clear previous active id
4446 if (g.ActiveId != 0)
4447 {
4448 // While most behaved code would make an effort to not steal active id during window move/drag operations,
4449 // we at least need to be resilient to it. Canceling the move is rather aggressive and users of 'master' branch
4450 // may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that.
4451 if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId)
4452 {
4453 IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() cancel MovingWindow\n");
4454 g.MovingWindow = NULL;
4455 }
4456
4457 // This could be written in a more general way (e.g associate a hook to ActiveId),
4458 // but since this is currently quite an exception we'll leave it as is.
4459 // One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveId()
4460 if (g.InputTextState.ID == g.ActiveId)
4461 InputTextDeactivateHook(id: g.ActiveId);
4462 }
4463
4464 // Set active id
4465 g.ActiveIdIsJustActivated = (g.ActiveId != id);
4466 if (g.ActiveIdIsJustActivated)
4467 {
4468 IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() old:0x%08X (window \"%s\") -> new:0x%08X (window \"%s\")\n", g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "", id, window ? window->Name : "");
4469 g.ActiveIdTimer = 0.0f;
4470 g.ActiveIdHasBeenPressedBefore = false;
4471 g.ActiveIdHasBeenEditedBefore = false;
4472 g.ActiveIdMouseButton = -1;
4473 if (id != 0)
4474 {
4475 g.LastActiveId = id;
4476 g.LastActiveIdTimer = 0.0f;
4477 }
4478 }
4479 g.ActiveId = id;
4480 g.ActiveIdAllowOverlap = false;
4481 g.ActiveIdNoClearOnFocusLoss = false;
4482 g.ActiveIdWindow = window;
4483 g.ActiveIdHasBeenEditedThisFrame = false;
4484 g.ActiveIdFromShortcut = false;
4485 if (id)
4486 {
4487 g.ActiveIdIsAlive = id;
4488 g.ActiveIdSource = (g.NavActivateId == id || g.NavJustMovedToId == id) ? g.NavInputSource : ImGuiInputSource_Mouse;
4489 IM_ASSERT(g.ActiveIdSource != ImGuiInputSource_None);
4490 }
4491
4492 // Clear declaration of inputs claimed by the widget
4493 // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
4494 g.ActiveIdUsingNavDirMask = 0x00;
4495 g.ActiveIdUsingAllKeyboardKeys = false;
4496}
4497
4498void ImGui::ClearActiveID()
4499{
4500 SetActiveID(id: 0, NULL); // g.ActiveId = 0;
4501}
4502
4503void ImGui::SetHoveredID(ImGuiID id)
4504{
4505 ImGuiContext& g = *GImGui;
4506 g.HoveredId = id;
4507 g.HoveredIdAllowOverlap = false;
4508 if (id != 0 && g.HoveredIdPreviousFrame != id)
4509 g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
4510}
4511
4512ImGuiID ImGui::GetHoveredID()
4513{
4514 ImGuiContext& g = *GImGui;
4515 return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
4516}
4517
4518void ImGui::MarkItemEdited(ImGuiID id)
4519{
4520 // This marking is to be able to provide info for IsItemDeactivatedAfterEdit().
4521 // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need to fill the data.
4522 ImGuiContext& g = *GImGui;
4523 if (g.LastItemData.ItemFlags & ImGuiItemFlags_NoMarkEdited)
4524 return;
4525 if (g.ActiveId == id || g.ActiveId == 0)
4526 {
4527 g.ActiveIdHasBeenEditedThisFrame = true;
4528 g.ActiveIdHasBeenEditedBefore = true;
4529 }
4530
4531 // We accept a MarkItemEdited() on drag and drop targets (see https://github.com/ocornut/imgui/issues/1875#issuecomment-978243343)
4532 // We accept 'ActiveIdPreviousFrame == id' for InputText() returning an edit after it has been taken ActiveId away (#4714)
4533 IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id || (g.CurrentMultiSelect != NULL && g.BoxSelectState.IsActive));
4534
4535 //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
4536 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
4537}
4538
4539bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
4540{
4541 // An active popup disable hovering on other windows (apart from its own children)
4542 // FIXME-OPT: This could be cached/stored within the window.
4543 ImGuiContext& g = *GImGui;
4544 if (g.NavWindow)
4545 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindowDockTree)
4546 if (focused_root_window->WasActive && focused_root_window != window->RootWindowDockTree)
4547 {
4548 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
4549 // NB: The 'else' is important because Modal windows are also Popups.
4550 bool want_inhibit = false;
4551 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
4552 want_inhibit = true;
4553 else if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
4554 want_inhibit = true;
4555
4556 // Inhibit hover unless the window is within the stack of our modal/popup
4557 if (want_inhibit)
4558 if (!IsWindowWithinBeginStackOf(window: window->RootWindow, potential_parent: focused_root_window))
4559 return false;
4560 }
4561
4562 // Filter by viewport
4563 if (window->Viewport != g.MouseViewport)
4564 if (g.MovingWindow == NULL || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree)
4565 return false;
4566
4567 return true;
4568}
4569
4570static inline float CalcDelayFromHoveredFlags(ImGuiHoveredFlags flags)
4571{
4572 ImGuiContext& g = *GImGui;
4573 if (flags & ImGuiHoveredFlags_DelayNormal)
4574 return g.Style.HoverDelayNormal;
4575 if (flags & ImGuiHoveredFlags_DelayShort)
4576 return g.Style.HoverDelayShort;
4577 return 0.0f;
4578}
4579
4580static ImGuiHoveredFlags ApplyHoverFlagsForTooltip(ImGuiHoveredFlags user_flags, ImGuiHoveredFlags shared_flags)
4581{
4582 // Allow instance flags to override shared flags
4583 if (user_flags & (ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal))
4584 shared_flags &= ~(ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal);
4585 return user_flags | shared_flags;
4586}
4587
4588// This is roughly matching the behavior of internal-facing ItemHoverable()
4589// - 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()
4590// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
4591bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
4592{
4593 ImGuiContext& g = *GImGui;
4594 ImGuiWindow* window = g.CurrentWindow;
4595 IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0, "Invalid flags for IsItemHovered()!");
4596
4597 if (g.NavHighlightItemUnderNav && g.NavCursorVisible && !(flags & ImGuiHoveredFlags_NoNavOverride))
4598 {
4599 if (!IsItemFocused())
4600 return false;
4601 if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
4602 return false;
4603
4604 if (flags & ImGuiHoveredFlags_ForTooltip)
4605 flags = ApplyHoverFlagsForTooltip(user_flags: flags, shared_flags: g.Style.HoverFlagsForTooltipNav);
4606 }
4607 else
4608 {
4609 // Test for bounding box overlap, as updated as ItemAdd()
4610 ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags;
4611 if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))
4612 return false;
4613
4614 if (flags & ImGuiHoveredFlags_ForTooltip)
4615 flags = ApplyHoverFlagsForTooltip(user_flags: flags, shared_flags: g.Style.HoverFlagsForTooltipMouse);
4616
4617 // Done with rectangle culling so we can perform heavier checks now
4618 // Test if we are hovering the right window (our window could be behind another window)
4619 // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)
4620 // [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
4621 // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was
4622 // the test that has been running for a long while.
4623 if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0)
4624 if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByWindow) == 0)
4625 return false;
4626
4627 // Test if another item is active (e.g. being dragged)
4628 const ImGuiID id = g.LastItemData.ID;
4629 if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0)
4630 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
4631 if (g.ActiveId != window->MoveId && g.ActiveId != window->TabId)
4632 return false;
4633
4634 // Test if interactions on this window are blocked by an active popup or modal.
4635 // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
4636 if (!IsWindowContentHoverable(window, flags) && !(g.LastItemData.ItemFlags & ImGuiItemFlags_NoWindowHoverableCheck))
4637 return false;
4638
4639 // Test if the item is disabled
4640 if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
4641 return false;
4642
4643 // Special handling for calling after Begin() which represent the title bar or tab.
4644 // When the window is skipped/collapsed (SkipItems==true) that last item (always ->MoveId submitted by Begin)
4645 // will never be overwritten so we need to detect the case.
4646 if (id == window->MoveId && window->WriteAccessed)
4647 return false;
4648
4649 // Test if using AllowOverlap and overlapped
4650 if ((g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap) && id != 0)
4651 if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByItem) == 0)
4652 if (g.HoveredIdPreviousFrame != g.LastItemData.ID)
4653 return false;
4654 }
4655
4656 // Handle hover delay
4657 // (some ideas: https://www.nngroup.com/articles/timing-exposing-content)
4658 const float delay = CalcDelayFromHoveredFlags(flags);
4659 if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary))
4660 {
4661 ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromPos(p_abs: g.LastItemData.Rect.Min);
4662 if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id))
4663 g.HoverItemDelayTimer = 0.0f;
4664 g.HoverItemDelayId = hover_delay_id;
4665
4666 // When changing hovered item we requires a bit of stationary delay before activating hover timer,
4667 // but once unlocked on a given item we also moving.
4668 //if (g.HoverDelayTimer >= delay && (g.HoverDelayTimer - g.IO.DeltaTime < delay || g.MouseStationaryTimer - g.IO.DeltaTime < g.Style.HoverStationaryDelay)) { IMGUI_DEBUG_LOG("HoverDelayTimer = %f/%f, MouseStationaryTimer = %f\n", g.HoverDelayTimer, delay, g.MouseStationaryTimer); }
4669 if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverItemUnlockedStationaryId != hover_delay_id)
4670 return false;
4671
4672 if (g.HoverItemDelayTimer < delay)
4673 return false;
4674 }
4675
4676 return true;
4677}
4678
4679// Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
4680// (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call)
4681// FIXME-LEGACY: the 'ImGuiItemFlags item_flags' parameter was added on 2023-06-28.
4682// If you used this in your legacy/custom widgets code:
4683// - Commonly: if your ItemHoverable() call comes after an ItemAdd() call: pass 'item_flags = g.LastItemData.ItemFlags'.
4684// - Rare: otherwise you may pass 'item_flags = 0' (ImGuiItemFlags_None) unless you want to benefit from special behavior handled by ItemHoverable.
4685bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags)
4686{
4687 ImGuiContext& g = *GImGui;
4688 ImGuiWindow* window = g.CurrentWindow;
4689
4690 // Detect ID conflicts
4691#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4692 if (id != 0 && g.HoveredIdPreviousFrame == id && (item_flags & ImGuiItemFlags_AllowDuplicateId) == 0)
4693 {
4694 g.HoveredIdPreviousFrameItemCount++;
4695 if (g.DebugDrawIdConflicts == id)
4696 window->DrawList->AddRect(p_min: bb.Min - ImVec2(1,1), p_max: bb.Max + ImVec2(1,1), IM_COL32(255, 0, 0, 255), rounding: 0.0f, flags: ImDrawFlags_None, thickness: 2.0f);
4697 }
4698#endif
4699
4700 if (g.HoveredWindow != window)
4701 return false;
4702 if (!IsMouseHoveringRect(r_min: bb.Min, r_max: bb.Max))
4703 return false;
4704
4705 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
4706 return false;
4707 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
4708 if (!g.ActiveIdFromShortcut)
4709 return false;
4710
4711 // Done with rectangle culling so we can perform heavier checks now.
4712 if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, flags: ImGuiHoveredFlags_None))
4713 {
4714 g.HoveredIdIsDisabled = true;
4715 return false;
4716 }
4717
4718 // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
4719 // hover test in widgets code. We could also decide to split this function is two.
4720 if (id != 0)
4721 {
4722 // Drag source doesn't report as hovered
4723 if (g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover))
4724 return false;
4725
4726 SetHoveredID(id);
4727
4728 // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match.
4729 // This allows using patterns where a later submitted widget overlaps a previous one. Generally perceived as a front-to-back hit-test.
4730 if (item_flags & ImGuiItemFlags_AllowOverlap)
4731 {
4732 g.HoveredIdAllowOverlap = true;
4733 if (g.HoveredIdPreviousFrame != id)
4734 return false;
4735 }
4736
4737 // Display shortcut (only works with mouse)
4738 // (ImGuiItemStatusFlags_HasShortcut in LastItemData denotes we want a tooltip)
4739 if (id == g.LastItemData.ID && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasShortcut) && g.ActiveId != id)
4740 if (IsItemHovered(flags: ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal))
4741 SetTooltip("%s", GetKeyChordName(key_chord: g.LastItemData.Shortcut));
4742 }
4743
4744 // When disabled we'll return false but still set HoveredId
4745 if (item_flags & ImGuiItemFlags_Disabled)
4746 {
4747 // Release active id if turning disabled
4748 if (g.ActiveId == id && id != 0)
4749 ClearActiveID();
4750 g.HoveredIdIsDisabled = true;
4751 return false;
4752 }
4753
4754#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4755 if (id != 0)
4756 {
4757 // [DEBUG] Item Picker tool!
4758 // We perform the check here because reaching is path is rare (1~ time a frame),
4759 // making the cost of this tool near-zero! We could get better call-stack and support picking non-hovered
4760 // items if we performed the test in ItemAdd(), but that would incur a bigger runtime cost.
4761 if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
4762 GetForegroundDrawList()->AddRect(p_min: bb.Min, p_max: bb.Max, IM_COL32(255, 255, 0, 255));
4763 if (g.DebugItemPickerBreakId == id)
4764 IM_DEBUG_BREAK();
4765 }
4766#endif
4767
4768 if (g.NavHighlightItemUnderNav && (item_flags & ImGuiItemFlags_NoNavDisableMouseHover) == 0)
4769 return false;
4770
4771 return true;
4772}
4773
4774// FIXME: This is inlined/duplicated in ItemAdd()
4775// FIXME: The id != 0 path is not used by our codebase, may get rid of it?
4776bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id)
4777{
4778 ImGuiContext& g = *GImGui;
4779 ImGuiWindow* window = g.CurrentWindow;
4780 if (!bb.Overlaps(r: window->ClipRect))
4781 if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
4782 if (!g.ItemUnclipByLog)
4783 return true;
4784 return false;
4785}
4786
4787// This is also inlined in ItemAdd()
4788// Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set g.LastItemData.DisplayRect.
4789void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags item_flags, const ImRect& item_rect)
4790{
4791 ImGuiContext& g = *GImGui;
4792 g.LastItemData.ID = item_id;
4793 g.LastItemData.ItemFlags = in_flags;
4794 g.LastItemData.StatusFlags = item_flags;
4795 g.LastItemData.Rect = g.LastItemData.NavRect = item_rect;
4796}
4797
4798float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
4799{
4800 if (wrap_pos_x < 0.0f)
4801 return 0.0f;
4802
4803 ImGuiContext& g = *GImGui;
4804 ImGuiWindow* window = g.CurrentWindow;
4805 if (wrap_pos_x == 0.0f)
4806 {
4807 // We could decide to setup a default wrapping max point for auto-resizing windows,
4808 // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
4809 //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
4810 // wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
4811 //else
4812 wrap_pos_x = window->WorkRect.Max.x;
4813 }
4814 else if (wrap_pos_x > 0.0f)
4815 {
4816 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
4817 }
4818
4819 return ImMax(lhs: wrap_pos_x - pos.x, rhs: 1.0f);
4820}
4821
4822// IM_ALLOC() == ImGui::MemAlloc()
4823void* ImGui::MemAlloc(size_t size)
4824{
4825 void* ptr = (*GImAllocatorAllocFunc)(size, GImAllocatorUserData);
4826#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4827 if (ImGuiContext* ctx = GImGui)
4828 DebugAllocHook(info: &ctx->DebugAllocInfo, frame_count: ctx->FrameCount, ptr, size);
4829#endif
4830 return ptr;
4831}
4832
4833// IM_FREE() == ImGui::MemFree()
4834void ImGui::MemFree(void* ptr)
4835{
4836#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4837 if (ptr != NULL)
4838 if (ImGuiContext* ctx = GImGui)
4839 DebugAllocHook(info: &ctx->DebugAllocInfo, frame_count: ctx->FrameCount, ptr, size: (size_t)-1);
4840#endif
4841 return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData);
4842}
4843
4844// We record the number of allocation in recent frames, as a way to audit/sanitize our guiding principles of "no allocations on idle/repeating frames"
4845void ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size)
4846{
4847 ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[info->LastEntriesIdx];
4848 IM_UNUSED(ptr);
4849 if (entry->FrameCount != frame_count)
4850 {
4851 info->LastEntriesIdx = (info->LastEntriesIdx + 1) % IM_ARRAYSIZE(info->LastEntriesBuf);
4852 entry = &info->LastEntriesBuf[info->LastEntriesIdx];
4853 entry->FrameCount = frame_count;
4854 entry->AllocCount = entry->FreeCount = 0;
4855 }
4856 if (size != (size_t)-1)
4857 {
4858 //printf("[%05d] MemAlloc(%d) -> 0x%p\n", frame_count, (int)size, ptr);
4859 entry->AllocCount++;
4860 info->TotalAllocCount++;
4861 }
4862 else
4863 {
4864 //printf("[%05d] MemFree(0x%p)\n", frame_count, ptr);
4865 entry->FreeCount++;
4866 info->TotalFreeCount++;
4867 }
4868}
4869
4870const char* ImGui::GetClipboardText()
4871{
4872 ImGuiContext& g = *GImGui;
4873 return g.PlatformIO.Platform_GetClipboardTextFn ? g.PlatformIO.Platform_GetClipboardTextFn(&g) : "";
4874}
4875
4876void ImGui::SetClipboardText(const char* text)
4877{
4878 ImGuiContext& g = *GImGui;
4879 if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)
4880 g.PlatformIO.Platform_SetClipboardTextFn(&g, text);
4881}
4882
4883const char* ImGui::GetVersion()
4884{
4885 return IMGUI_VERSION;
4886}
4887
4888ImGuiIO& ImGui::GetIO()
4889{
4890 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
4891 return GImGui->IO;
4892}
4893
4894// This variant exists to facilitate backends experimenting with multi-threaded parallel context. (#8069, #6293, #5856)
4895ImGuiIO& ImGui::GetIOEx(ImGuiContext* ctx)
4896{
4897 IM_ASSERT(ctx != NULL);
4898 return ctx->IO;
4899}
4900
4901ImGuiPlatformIO& ImGui::GetPlatformIO()
4902{
4903 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext()?");
4904 return GImGui->PlatformIO;
4905}
4906
4907// This variant exists to facilitate backends experimenting with multi-threaded parallel context. (#8069, #6293, #5856)
4908ImGuiPlatformIO& ImGui::GetPlatformIOEx(ImGuiContext* ctx)
4909{
4910 IM_ASSERT(ctx != NULL);
4911 return ctx->PlatformIO;
4912}
4913
4914// Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
4915ImDrawData* ImGui::GetDrawData()
4916{
4917 ImGuiContext& g = *GImGui;
4918 ImGuiViewportP* viewport = g.Viewports[0];
4919 return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL;
4920}
4921
4922double ImGui::GetTime()
4923{
4924 return GImGui->Time;
4925}
4926
4927int ImGui::GetFrameCount()
4928{
4929 return GImGui->FrameCount;
4930}
4931
4932static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)
4933{
4934 // Create the draw list on demand, because they are not frequently used for all viewports
4935 ImGuiContext& g = *GImGui;
4936 IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->BgFgDrawLists));
4937 ImDrawList* draw_list = viewport->BgFgDrawLists[drawlist_no];
4938 if (draw_list == NULL)
4939 {
4940 draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);
4941 draw_list->_OwnerName = drawlist_name;
4942 viewport->BgFgDrawLists[drawlist_no] = draw_list;
4943 }
4944
4945 // Our ImDrawList system requires that there is always a command
4946 if (viewport->BgFgDrawListsLastFrame[drawlist_no] != g.FrameCount)
4947 {
4948 draw_list->_ResetForNewFrame();
4949 draw_list->PushTextureID(texture_id: g.IO.Fonts->TexID);
4950 draw_list->PushClipRect(clip_rect_min: viewport->Pos, clip_rect_max: viewport->Pos + viewport->Size, intersect_with_current_clip_rect: false);
4951 viewport->BgFgDrawListsLastFrame[drawlist_no] = g.FrameCount;
4952 }
4953 return draw_list;
4954}
4955
4956ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)
4957{
4958 if (viewport == NULL)
4959 viewport = GImGui->CurrentWindow->Viewport;
4960 return GetViewportBgFgDrawList(viewport: (ImGuiViewportP*)viewport, drawlist_no: 0, drawlist_name: "##Background");
4961}
4962
4963ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
4964{
4965 if (viewport == NULL)
4966 viewport = GImGui->CurrentWindow->Viewport;
4967 return GetViewportBgFgDrawList(viewport: (ImGuiViewportP*)viewport, drawlist_no: 1, drawlist_name: "##Foreground");
4968}
4969
4970ImDrawListSharedData* ImGui::GetDrawListSharedData()
4971{
4972 return &GImGui->DrawListSharedData;
4973}
4974
4975void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
4976{
4977 // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
4978 // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
4979 // This is because we want ActiveId to be set even when the window is not permitted to move.
4980 ImGuiContext& g = *GImGui;
4981 FocusWindow(window);
4982 SetActiveID(id: window->MoveId, window);
4983 if (g.IO.ConfigNavCursorVisibleAuto)
4984 g.NavCursorVisible = false;
4985 g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindowDockTree->Pos;
4986 g.ActiveIdNoClearOnFocusLoss = true;
4987 SetActiveIdUsingAllKeyboardKeys();
4988
4989 bool can_move_window = true;
4990 if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoMove))
4991 can_move_window = false;
4992 if (ImGuiDockNode* node = window->DockNodeAsHost)
4993 if (node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove))
4994 can_move_window = false;
4995 if (can_move_window)
4996 g.MovingWindow = window;
4997}
4998
4999// We use 'undock == false' when dragging from title bar to allow moving groups of floating nodes without undocking them.
5000void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock)
5001{
5002 ImGuiContext& g = *GImGui;
5003 bool can_undock_node = false;
5004 if (undock && node != NULL && node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove) == 0 && (node->MergedFlags & ImGuiDockNodeFlags_NoUndocking) == 0)
5005 {
5006 // Can undock if:
5007 // - part of a hierarchy with more than one visible node (if only one is visible, we'll just move the root window)
5008 // - part of a dockspace node hierarchy: so we can undock the last single visible node too. Undocking from a fixed/central node will create a new node and copy windows.
5009 ImGuiDockNode* root_node = DockNodeGetRootNode(node);
5010 if (root_node->OnlyNodeWithWindows != node || root_node->CentralNode != NULL) // -V1051 PVS-Studio thinks node should be root_node and is wrong about that.
5011 can_undock_node = true;
5012 }
5013
5014 const bool clicked = IsMouseClicked(button: 0);
5015 const bool dragging = IsMouseDragging(button: 0);
5016 if (can_undock_node && dragging)
5017 DockContextQueueUndockNode(ctx: &g, node); // Will lead to DockNodeStartMouseMovingWindow() -> StartMouseMovingWindow() being called next frame
5018 else if (!can_undock_node && (clicked || dragging) && g.MovingWindow != window)
5019 StartMouseMovingWindow(window);
5020}
5021
5022// Handle mouse moving window
5023// Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
5024// FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId.
5025// This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
5026// but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
5027void ImGui::UpdateMouseMovingWindowNewFrame()
5028{
5029 ImGuiContext& g = *GImGui;
5030 if (g.MovingWindow != NULL)
5031 {
5032 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
5033 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
5034 KeepAliveID(id: g.ActiveId);
5035 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindowDockTree);
5036 ImGuiWindow* moving_window = g.MovingWindow->RootWindowDockTree;
5037
5038 // When a window stop being submitted while being dragged, it may will its viewport until next Begin()
5039 const bool window_disappared = (!moving_window->WasActive && !moving_window->Active);
5040 if (g.IO.MouseDown[0] && IsMousePosValid(mouse_pos: &g.IO.MousePos) && !window_disappared)
5041 {
5042 ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
5043 if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
5044 {
5045 SetWindowPos(window: moving_window, pos, cond: ImGuiCond_Always);
5046 if (moving_window->Viewport && moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window.
5047 {
5048 moving_window->Viewport->Pos = pos;
5049 moving_window->Viewport->UpdateWorkRect();
5050 }
5051 }
5052 FocusWindow(window: g.MovingWindow);
5053 }
5054 else
5055 {
5056 if (!window_disappared)
5057 {
5058 // Try to merge the window back into the main viewport.
5059 // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports)
5060 if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
5061 UpdateTryMergeWindowIntoHostViewport(window: moving_window, host_viewport: g.MouseViewport);
5062
5063 // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button.
5064 if (moving_window->Viewport && !IsDragDropPayloadBeingAccepted())
5065 g.MouseViewport = moving_window->Viewport;
5066
5067 // Clear the NoInput window flag set by the Viewport system
5068 if (moving_window->Viewport)
5069 moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs;
5070 }
5071
5072 g.MovingWindow = NULL;
5073 ClearActiveID();
5074 }
5075 }
5076 else
5077 {
5078 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
5079 if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
5080 {
5081 KeepAliveID(id: g.ActiveId);
5082 if (!g.IO.MouseDown[0])
5083 ClearActiveID();
5084 }
5085 }
5086}
5087
5088// Initiate focusing and moving window when clicking on empty space or title bar.
5089// Initiate focusing window when clicking on a disabled item.
5090// Handle left-click and right-click focus.
5091void ImGui::UpdateMouseMovingWindowEndFrame()
5092{
5093 ImGuiContext& g = *GImGui;
5094 if (g.ActiveId != 0 || (g.HoveredId != 0 && !g.HoveredIdIsDisabled))
5095 return;
5096
5097 // Unless we just made a window/popup appear
5098 if (g.NavWindow && g.NavWindow->Appearing)
5099 return;
5100
5101 // Click on empty space to focus window and start moving
5102 // (after we're done with all our widgets, so e.g. clicking on docking tab-bar which have set HoveredId already and not get us here!)
5103 if (g.IO.MouseClicked[0])
5104 {
5105 // Handle the edge case of a popup being closed while clicking in its empty space.
5106 // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
5107 ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
5108 const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(id: root_window->PopupId, popup_flags: ImGuiPopupFlags_AnyPopupLevel);
5109
5110 if (root_window != NULL && !is_closed_popup)
5111 {
5112 StartMouseMovingWindow(window: g.HoveredWindow); //-V595
5113
5114 // Cancel moving if clicked outside of title bar
5115 if (g.IO.ConfigWindowsMoveFromTitleBarOnly)
5116 if (!(root_window->Flags & ImGuiWindowFlags_NoTitleBar) || root_window->DockIsActive)
5117 if (!root_window->TitleBarRect().Contains(p: g.IO.MouseClickedPos[0]))
5118 g.MovingWindow = NULL;
5119
5120 // Cancel moving if clicked over an item which was disabled or inhibited by popups
5121 // (when g.HoveredIdIsDisabled == true && g.HoveredId == 0 we are inhibited by popups, when g.HoveredIdIsDisabled == true && g.HoveredId != 0 we are over a disabled item)0 already)
5122 if (g.HoveredIdIsDisabled)
5123 g.MovingWindow = NULL;
5124 }
5125 else if (root_window == NULL && g.NavWindow != NULL)
5126 {
5127 // Clicking on void disable focus
5128 FocusWindow(NULL, flags: ImGuiFocusRequestFlags_UnlessBelowModal);
5129 }
5130 }
5131
5132 // With right mouse button we close popups without changing focus based on where the mouse is aimed
5133 // Instead, focus will be restored to the window under the bottom-most closed popup.
5134 // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
5135 if (g.IO.MouseClicked[1] && g.HoveredId == 0)
5136 {
5137 // Find the top-most window between HoveredWindow and the top-most Modal Window.
5138 // This is where we can trim the popup stack.
5139 ImGuiWindow* modal = GetTopMostPopupModal();
5140 bool hovered_window_above_modal = g.HoveredWindow && (modal == NULL || IsWindowAbove(potential_above: g.HoveredWindow, potential_below: modal));
5141 ClosePopupsOverWindow(ref_window: hovered_window_above_modal ? g.HoveredWindow : modal, restore_focus_to_window_under_popup: true);
5142 }
5143}
5144
5145// This is called during NewFrame()->UpdateViewportsNewFrame() only.
5146// Need to keep in sync with SetWindowPos()
5147static void TranslateWindow(ImGuiWindow* window, const ImVec2& delta)
5148{
5149 window->Pos += delta;
5150 window->ClipRect.Translate(d: delta);
5151 window->OuterRectClipped.Translate(d: delta);
5152 window->InnerRect.Translate(d: delta);
5153 window->DC.CursorPos += delta;
5154 window->DC.CursorStartPos += delta;
5155 window->DC.CursorMaxPos += delta;
5156 window->DC.IdealMaxPos += delta;
5157}
5158
5159static void ScaleWindow(ImGuiWindow* window, float scale)
5160{
5161 ImVec2 origin = window->Viewport->Pos;
5162 window->Pos = ImFloor(v: (window->Pos - origin) * scale + origin);
5163 window->Size = ImTrunc(v: window->Size * scale);
5164 window->SizeFull = ImTrunc(v: window->SizeFull * scale);
5165 window->ContentSize = ImTrunc(v: window->ContentSize * scale);
5166}
5167
5168static bool IsWindowActiveAndVisible(ImGuiWindow* window)
5169{
5170 return (window->Active) && (!window->Hidden);
5171}
5172
5173// 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)
5174void ImGui::UpdateHoveredWindowAndCaptureFlags()
5175{
5176 ImGuiContext& g = *GImGui;
5177 ImGuiIO& io = g.IO;
5178
5179 // FIXME-DPI: This storage was added on 2021/03/31 for test engine, but if we want to multiply WINDOWS_HOVER_PADDING
5180 // by DpiScale, we need to make this window-agnostic anyhow, maybe need storing inside ImGuiWindow.
5181 g.WindowsHoverPadding = ImMax(lhs: g.Style.TouchExtraPadding, rhs: ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING));
5182
5183 // Find the window hovered by mouse:
5184 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
5185 // - 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.
5186 // - 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.
5187 bool clear_hovered_windows = false;
5188 FindHoveredWindowEx(pos: g.IO.MousePos, find_first_and_in_any_viewport: false, out_hovered_window: &g.HoveredWindow, out_hovered_window_under_moving_window: &g.HoveredWindowUnderMovingWindow);
5189 IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseViewport);
5190 g.HoveredWindowBeforeClear = g.HoveredWindow;
5191
5192 // Modal windows prevents mouse from hovering behind them.
5193 ImGuiWindow* modal_window = GetTopMostPopupModal();
5194 if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(window: g.HoveredWindow->RootWindow, potential_parent: modal_window)) // FIXME-MERGE: RootWindowDockTree ?
5195 clear_hovered_windows = true;
5196
5197 // Disabled mouse hovering (we don't currently clear MousePos, we could)
5198 if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
5199 clear_hovered_windows = true;
5200
5201 // We track click ownership. When clicked outside of a window the click is owned by the application and
5202 // won't report hovering nor request capture even while dragging over our windows afterward.
5203 const bool has_open_popup = (g.OpenPopupStack.Size > 0);
5204 const bool has_open_modal = (modal_window != NULL);
5205 int mouse_earliest_down = -1;
5206 bool mouse_any_down = false;
5207 for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
5208 {
5209 if (io.MouseClicked[i])
5210 {
5211 io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup;
5212 io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal;
5213 }
5214 mouse_any_down |= io.MouseDown[i];
5215 if (io.MouseDown[i] || io.MouseReleased[i]) // Increase release frame for our evaluation of earliest button (#1392)
5216 if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down])
5217 mouse_earliest_down = i;
5218 }
5219 const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down];
5220 const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down];
5221
5222 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
5223 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
5224 const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
5225 if (!mouse_avail && !mouse_dragging_extern_payload)
5226 clear_hovered_windows = true;
5227
5228 if (clear_hovered_windows)
5229 g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
5230
5231 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app)
5232 // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag
5233 if (g.WantCaptureMouseNextFrame != -1)
5234 {
5235 io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0);
5236 }
5237 else
5238 {
5239 io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup;
5240 io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal;
5241 }
5242
5243 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)
5244 io.WantCaptureKeyboard = false;
5245 if ((io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) == 0)
5246 {
5247 if ((g.ActiveId != 0) || (modal_window != NULL))
5248 io.WantCaptureKeyboard = true;
5249 else if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && io.ConfigNavCaptureKeyboard)
5250 io.WantCaptureKeyboard = true;
5251 }
5252 if (g.WantCaptureKeyboardNextFrame != -1) // Manual override
5253 io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
5254
5255 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
5256 io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
5257}
5258
5259// Called once a frame. Followed by SetCurrentFont() which sets up the remaining data.
5260static void SetupDrawListSharedData()
5261{
5262 ImGuiContext& g = *GImGui;
5263 ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
5264 for (ImGuiViewportP* viewport : g.Viewports)
5265 virtual_space.Add(r: viewport->GetMainRect());
5266 g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();
5267 g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
5268 g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError);
5269 g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
5270 if (g.Style.AntiAliasedLines)
5271 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
5272 if (g.Style.AntiAliasedLinesUseTex && !(g.IO.Fonts->Flags & ImFontAtlasFlags_NoBakedLines))
5273 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
5274 if (g.Style.AntiAliasedFill)
5275 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
5276 if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
5277 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
5278}
5279
5280void ImGui::NewFrame()
5281{
5282 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
5283 ImGuiContext& g = *GImGui;
5284
5285 // Remove pending delete hooks before frame start.
5286 // This deferred removal avoid issues of removal while iterating the hook vector
5287 for (int n = g.Hooks.Size - 1; n >= 0; n--)
5288 if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_)
5289 g.Hooks.erase(it: &g.Hooks[n]);
5290
5291 CallContextHooks(ctx: &g, hook_type: ImGuiContextHookType_NewFramePre);
5292
5293 // Check and assert for various common IO and Configuration mistakes
5294 g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame;
5295 ErrorCheckNewFrameSanityChecks();
5296 g.ConfigFlagsCurrFrame = g.IO.ConfigFlags;
5297
5298 // Load settings on first frame, save settings when modified (after a delay)
5299 UpdateSettings();
5300
5301 g.Time += g.IO.DeltaTime;
5302 g.WithinFrameScope = true;
5303 g.FrameCount += 1;
5304 g.TooltipOverrideCount = 0;
5305 g.WindowsActiveCount = 0;
5306 g.MenusIdSubmittedThisFrame.resize(new_size: 0);
5307
5308 // Calculate frame-rate for the user, as a purely luxurious feature
5309 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
5310 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
5311 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
5312 g.FramerateSecPerFrameCount = ImMin(lhs: g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame));
5313 g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX;
5314
5315 // Process input queue (trickle as many events as possible), turn events into writes to IO structure
5316 g.InputEventsTrail.resize(new_size: 0);
5317 UpdateInputEvents(trickle_fast_inputs: g.IO.ConfigInputTrickleEventQueue);
5318
5319 // Update viewports (after processing input queue, so io.MouseHoveredViewport is set)
5320 UpdateViewportsNewFrame();
5321
5322 // Setup current font and draw list shared data
5323 // FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal!
5324 g.IO.Fonts->Locked = true;
5325 SetupDrawListSharedData();
5326 SetCurrentFont(GetDefaultFont());
5327 IM_ASSERT(g.Font->IsLoaded());
5328
5329 // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
5330 for (ImGuiViewportP* viewport : g.Viewports)
5331 {
5332 viewport->DrawData = NULL;
5333 viewport->DrawDataP.Valid = false;
5334 }
5335
5336 // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
5337 if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
5338 KeepAliveID(id: g.DragDropPayload.SourceId);
5339
5340 // [DEBUG]
5341 if (!g.IO.ConfigDebugHighlightIdConflicts || !g.IO.KeyCtrl) // Count is locked while holding CTRL
5342 g.DebugDrawIdConflicts = 0;
5343 if (g.IO.ConfigDebugHighlightIdConflicts && g.HoveredIdPreviousFrameItemCount > 1)
5344 g.DebugDrawIdConflicts = g.HoveredIdPreviousFrame;
5345
5346 // Update HoveredId data
5347 if (!g.HoveredIdPreviousFrame)
5348 g.HoveredIdTimer = 0.0f;
5349 if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
5350 g.HoveredIdNotActiveTimer = 0.0f;
5351 if (g.HoveredId)
5352 g.HoveredIdTimer += g.IO.DeltaTime;
5353 if (g.HoveredId && g.ActiveId != g.HoveredId)
5354 g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
5355 g.HoveredIdPreviousFrame = g.HoveredId;
5356 g.HoveredIdPreviousFrameItemCount = 0;
5357 g.HoveredId = 0;
5358 g.HoveredIdAllowOverlap = false;
5359 g.HoveredIdIsDisabled = false;
5360
5361 // Clear ActiveID if the item is not alive anymore.
5362 // In 1.87, the common most call to KeepAliveID() was moved from GetID() to ItemAdd().
5363 // As a result, custom widget using ButtonBehavior() _without_ ItemAdd() need to call KeepAliveID() themselves.
5364 if (g.ActiveId != 0 && g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId)
5365 {
5366 IMGUI_DEBUG_LOG_ACTIVEID("NewFrame(): ClearActiveID() because it isn't marked alive anymore!\n");
5367 ClearActiveID();
5368 }
5369
5370 // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
5371 if (g.ActiveId)
5372 g.ActiveIdTimer += g.IO.DeltaTime;
5373 g.LastActiveIdTimer += g.IO.DeltaTime;
5374 g.ActiveIdPreviousFrame = g.ActiveId;
5375 g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
5376 g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
5377 g.ActiveIdIsAlive = 0;
5378 g.ActiveIdHasBeenEditedThisFrame = false;
5379 g.ActiveIdPreviousFrameIsAlive = false;
5380 g.ActiveIdIsJustActivated = false;
5381 if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)
5382 g.TempInputId = 0;
5383 if (g.ActiveId == 0)
5384 {
5385 g.ActiveIdUsingNavDirMask = 0x00;
5386 g.ActiveIdUsingAllKeyboardKeys = false;
5387 }
5388
5389 // Record when we have been stationary as this state is preserved while over same item.
5390 // FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values.
5391 // To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function.
5392 if (g.HoverItemDelayId != 0 && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
5393 g.HoverItemUnlockedStationaryId = g.HoverItemDelayId;
5394 else if (g.HoverItemDelayId == 0)
5395 g.HoverItemUnlockedStationaryId = 0;
5396 if (g.HoveredWindow != NULL && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
5397 g.HoverWindowUnlockedStationaryId = g.HoveredWindow->ID;
5398 else if (g.HoveredWindow == NULL)
5399 g.HoverWindowUnlockedStationaryId = 0;
5400
5401 // Update hover delay for IsItemHovered() with delays and tooltips
5402 g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId;
5403 if (g.HoverItemDelayId != 0)
5404 {
5405 g.HoverItemDelayTimer += g.IO.DeltaTime;
5406 g.HoverItemDelayClearTimer = 0.0f;
5407 g.HoverItemDelayId = 0;
5408 }
5409 else if (g.HoverItemDelayTimer > 0.0f)
5410 {
5411 // This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps
5412 // We could expose 0.25f as style.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle.
5413 g.HoverItemDelayClearTimer += g.IO.DeltaTime;
5414 if (g.HoverItemDelayClearTimer >= ImMax(lhs: 0.25f, rhs: g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate
5415 g.HoverItemDelayTimer = g.HoverItemDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer.
5416 }
5417
5418 // Drag and drop
5419 g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
5420 g.DragDropAcceptIdCurr = 0;
5421 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
5422 g.DragDropWithinSource = false;
5423 g.DragDropWithinTarget = false;
5424 g.DragDropHoldJustPressedId = 0;
5425 g.TooltipPreviousWindow = NULL;
5426
5427 // Close popups on focus lost (currently wip/opt-in)
5428 //if (g.IO.AppFocusLost)
5429 // ClosePopupsExceptModals();
5430
5431 // Update keyboard input state
5432 UpdateKeyboardInputs();
5433
5434 //IM_ASSERT(g.IO.KeyCtrl == IsKeyDown(ImGuiKey_LeftCtrl) || IsKeyDown(ImGuiKey_RightCtrl));
5435 //IM_ASSERT(g.IO.KeyShift == IsKeyDown(ImGuiKey_LeftShift) || IsKeyDown(ImGuiKey_RightShift));
5436 //IM_ASSERT(g.IO.KeyAlt == IsKeyDown(ImGuiKey_LeftAlt) || IsKeyDown(ImGuiKey_RightAlt));
5437 //IM_ASSERT(g.IO.KeySuper == IsKeyDown(ImGuiKey_LeftSuper) || IsKeyDown(ImGuiKey_RightSuper));
5438
5439 // Update keyboard/gamepad navigation
5440 NavUpdate();
5441
5442 // Update mouse input state
5443 UpdateMouseInputs();
5444
5445 // Undocking
5446 // (needs to be before UpdateMouseMovingWindowNewFrame so the window is already offset and following the mouse on the detaching frame)
5447 DockContextNewFrameUpdateUndocking(ctx: &g);
5448
5449 // Mark all windows as not visible and compact unused memory.
5450 IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);
5451 const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
5452 for (ImGuiWindow* window : g.Windows)
5453 {
5454 window->WasActive = window->Active;
5455 window->Active = false;
5456 window->WriteAccessed = false;
5457 window->BeginCountPreviousFrame = window->BeginCount;
5458 window->BeginCount = 0;
5459
5460 // Garbage collect transient buffers of recently unused windows
5461 if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
5462 GcCompactTransientWindowBuffers(window);
5463 }
5464
5465 // Find hovered window
5466 // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
5467 // (currently needs to be done after the WasActive=Active loop and FindHoveredWindowEx uses ->Active)
5468 UpdateHoveredWindowAndCaptureFlags();
5469
5470 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
5471 UpdateMouseMovingWindowNewFrame();
5472
5473 // Background darkening/whitening
5474 if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
5475 g.DimBgRatio = ImMin(lhs: g.DimBgRatio + g.IO.DeltaTime * 6.0f, rhs: 1.0f);
5476 else
5477 g.DimBgRatio = ImMax(lhs: g.DimBgRatio - g.IO.DeltaTime * 10.0f, rhs: 0.0f);
5478
5479 g.MouseCursor = ImGuiMouseCursor_Arrow;
5480 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
5481
5482 // Platform IME data: reset for the frame
5483 g.PlatformImeDataPrev = g.PlatformImeData;
5484 g.PlatformImeData.WantVisible = false;
5485
5486 // Mouse wheel scrolling, scale
5487 UpdateMouseWheel();
5488
5489 // Garbage collect transient buffers of recently unused tables
5490 for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
5491 if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
5492 TableGcCompactTransientBuffers(table: g.Tables.GetByIndex(n: i));
5493 for (ImGuiTableTempData& table_temp_data : g.TablesTempData)
5494 if (table_temp_data.LastTimeActive >= 0.0f && table_temp_data.LastTimeActive < memory_compact_start_time)
5495 TableGcCompactTransientBuffers(table: &table_temp_data);
5496 if (g.GcCompactAll)
5497 GcCompactTransientMiscBuffers();
5498 g.GcCompactAll = false;
5499
5500 // Closing the focused window restore focus to the first active root window in descending z-order
5501 if (g.NavWindow && !g.NavWindow->WasActive)
5502 FocusTopMostWindowUnderOne(NULL, NULL, NULL, flags: ImGuiFocusRequestFlags_RestoreFocusedChild);
5503
5504 // No window should be open at the beginning of the frame.
5505 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
5506 g.CurrentWindowStack.resize(new_size: 0);
5507 g.BeginPopupStack.resize(new_size: 0);
5508 g.ItemFlagsStack.resize(new_size: 0);
5509 g.ItemFlagsStack.push_back(v: ImGuiItemFlags_AutoClosePopups); // Default flags
5510 g.CurrentItemFlags = g.ItemFlagsStack.back();
5511 g.GroupStack.resize(new_size: 0);
5512
5513 // Docking
5514 DockContextNewFrameUpdateDocking(ctx: &g);
5515
5516 // [DEBUG] Update debug features
5517#ifndef IMGUI_DISABLE_DEBUG_TOOLS
5518 UpdateDebugToolItemPicker();
5519 UpdateDebugToolStackQueries();
5520 UpdateDebugToolFlashStyleColor();
5521 if (g.DebugLocateFrames > 0 && --g.DebugLocateFrames == 0)
5522 {
5523 g.DebugLocateId = 0;
5524 g.DebugBreakInLocateId = false;
5525 }
5526 if (g.DebugLogAutoDisableFrames > 0 && --g.DebugLogAutoDisableFrames == 0)
5527 {
5528 DebugLog(fmt: "(Debug Log: Auto-disabled some ImGuiDebugLogFlags after 2 frames)\n");
5529 g.DebugLogFlags &= ~g.DebugLogAutoDisableFlags;
5530 g.DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
5531 }
5532#endif
5533
5534 // Create implicit/fallback window - which we will only render it if the user has added something to it.
5535 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
5536 // This fallback is particularly important as it prevents ImGui:: calls from crashing.
5537 g.WithinFrameScopeWithImplicitWindow = true;
5538 SetNextWindowSize(size: ImVec2(400, 400), cond: ImGuiCond_FirstUseEver);
5539 Begin(name: "Debug##Default");
5540 IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
5541
5542 // Store stack sizes
5543 g.ErrorCountCurrentFrame = 0;
5544 ErrorRecoveryStoreState(state_out: &g.StackSizesInNewFrame);
5545
5546 // [DEBUG] When io.ConfigDebugBeginReturnValue is set, we make Begin()/BeginChild() return false at different level of the window-stack,
5547 // allowing to validate correct Begin/End behavior in user code.
5548#ifndef IMGUI_DISABLE_DEBUG_TOOLS
5549 if (g.IO.ConfigDebugBeginReturnValueLoop)
5550 g.DebugBeginReturnValueCullDepth = (g.DebugBeginReturnValueCullDepth == -1) ? 0 : ((g.DebugBeginReturnValueCullDepth + ((g.FrameCount % 4) == 0 ? 1 : 0)) % 10);
5551 else
5552 g.DebugBeginReturnValueCullDepth = -1;
5553#endif
5554
5555 CallContextHooks(ctx: &g, hook_type: ImGuiContextHookType_NewFramePost);
5556}
5557
5558// FIXME: Add a more explicit sort order in the window structure.
5559static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
5560{
5561 const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
5562 const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
5563 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
5564 return d;
5565 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
5566 return d;
5567 return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
5568}
5569
5570static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
5571{
5572 out_sorted_windows->push_back(v: window);
5573 if (window->Active)
5574 {
5575 int count = window->DC.ChildWindows.Size;
5576 ImQsort(base: window->DC.ChildWindows.Data, count: (size_t)count, size_of_element: sizeof(ImGuiWindow*), compare_func: ChildWindowComparer);
5577 for (int i = 0; i < count; i++)
5578 {
5579 ImGuiWindow* child = window->DC.ChildWindows[i];
5580 if (child->Active)
5581 AddWindowToSortBuffer(out_sorted_windows, window: child);
5582 }
5583 }
5584}
5585
5586static void AddWindowToDrawData(ImGuiWindow* window, int layer)
5587{
5588 ImGuiContext& g = *GImGui;
5589 ImGuiViewportP* viewport = window->Viewport;
5590 IM_ASSERT(viewport != NULL);
5591 g.IO.MetricsRenderWindows++;
5592 if (window->DrawList->_Splitter._Count > 1)
5593 window->DrawList->ChannelsMerge(); // Merge if user forgot to merge back. Also required in Docking branch for ImGuiWindowFlags_DockNodeHost windows.
5594 ImGui::AddDrawListToDrawDataEx(draw_data: &viewport->DrawDataP, out_list: viewport->DrawDataBuilder.Layers[layer], draw_list: window->DrawList);
5595 for (ImGuiWindow* child : window->DC.ChildWindows)
5596 if (IsWindowActiveAndVisible(window: child)) // Clipped children may have been marked not active
5597 AddWindowToDrawData(window: child, layer);
5598}
5599
5600static inline int GetWindowDisplayLayer(ImGuiWindow* window)
5601{
5602 return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
5603}
5604
5605// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
5606static inline void AddRootWindowToDrawData(ImGuiWindow* window)
5607{
5608 AddWindowToDrawData(window, layer: GetWindowDisplayLayer(window));
5609}
5610
5611static void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder)
5612{
5613 int n = builder->Layers[0]->Size;
5614 int full_size = n;
5615 for (int i = 1; i < IM_ARRAYSIZE(builder->Layers); i++)
5616 full_size += builder->Layers[i]->Size;
5617 builder->Layers[0]->resize(new_size: full_size);
5618 for (int layer_n = 1; layer_n < IM_ARRAYSIZE(builder->Layers); layer_n++)
5619 {
5620 ImVector<ImDrawList*>* layer = builder->Layers[layer_n];
5621 if (layer->empty())
5622 continue;
5623 memcpy(dest: builder->Layers[0]->Data + n, src: layer->Data, n: layer->Size * sizeof(ImDrawList*));
5624 n += layer->Size;
5625 layer->resize(new_size: 0);
5626 }
5627}
5628
5629static void InitViewportDrawData(ImGuiViewportP* viewport)
5630{
5631 ImGuiIO& io = ImGui::GetIO();
5632 ImDrawData* draw_data = &viewport->DrawDataP;
5633
5634 viewport->DrawData = draw_data; // Make publicly accessible
5635 viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists;
5636 viewport->DrawDataBuilder.Layers[1] = &viewport->DrawDataBuilder.LayerData1;
5637 viewport->DrawDataBuilder.Layers[0]->resize(new_size: 0);
5638 viewport->DrawDataBuilder.Layers[1]->resize(new_size: 0);
5639
5640 // When minimized, we report draw_data->DisplaySize as zero to be consistent with non-viewport mode,
5641 // and to allow applications/backends to easily skip rendering.
5642 // FIXME: Note that we however do NOT attempt to report "zero drawlist / vertices" into the ImDrawData structure.
5643 // This is because the work has been done already, and its wasted! We should fix that and add optimizations for
5644 // it earlier in the pipeline, rather than pretend to hide the data at the end of the pipeline.
5645 const bool is_minimized = (viewport->Flags & ImGuiViewportFlags_IsMinimized) != 0;
5646
5647 draw_data->Valid = true;
5648 draw_data->CmdListsCount = 0;
5649 draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
5650 draw_data->DisplayPos = viewport->Pos;
5651 draw_data->DisplaySize = is_minimized ? ImVec2(0.0f, 0.0f) : viewport->Size;
5652 draw_data->FramebufferScale = io.DisplayFramebufferScale; // FIXME-VIEWPORT: This may vary on a per-monitor/viewport basis?
5653 draw_data->OwnerViewport = viewport;
5654}
5655
5656// Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
5657// - When using this function it is sane to ensure that float are perfectly rounded to integer values,
5658// so that e.g. (int)(max.x-min.x) in user's render produce correct result.
5659// - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
5660// some frequently called functions which to modify both channels and clipping simultaneously tend to use the
5661// more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.
5662// - This is analoguous to PushFont()/PopFont() in the sense that are a mixing a global stack and a window stack,
5663// which in the case of ClipRect is not so problematic but tends to be more restrictive for fonts.
5664void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
5665{
5666 ImGuiWindow* window = GetCurrentWindow();
5667 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
5668 window->ClipRect = window->DrawList->_ClipRectStack.back();
5669}
5670
5671void ImGui::PopClipRect()
5672{
5673 ImGuiWindow* window = GetCurrentWindow();
5674 window->DrawList->PopClipRect();
5675 window->ClipRect = window->DrawList->_ClipRectStack.back();
5676}
5677
5678static ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window)
5679{
5680 for (int n = window->DC.ChildWindows.Size - 1; n >= 0; n--)
5681 if (IsWindowActiveAndVisible(window: window->DC.ChildWindows[n]))
5682 return FindFrontMostVisibleChildWindow(window: window->DC.ChildWindows[n]);
5683 return window;
5684}
5685
5686static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col)
5687{
5688 if ((col & IM_COL32_A_MASK) == 0)
5689 return;
5690
5691 ImGuiViewportP* viewport = window->Viewport;
5692 ImRect viewport_rect = viewport->GetMainRect();
5693
5694 // Draw behind window by moving the draw command at the FRONT of the draw list
5695 {
5696 // Draw list have been trimmed already, hence the explicit recreation of a draw command if missing.
5697 // FIXME: This is creating complication, might be simpler if we could inject a drawlist in drawdata at a given position and not attempt to manipulate ImDrawCmd order.
5698 ImDrawList* draw_list = window->RootWindowDockTree->DrawList;
5699 draw_list->ChannelsMerge();
5700 if (draw_list->CmdBuffer.Size == 0)
5701 draw_list->AddDrawCmd();
5702 draw_list->PushClipRect(clip_rect_min: viewport_rect.Min - ImVec2(1, 1), clip_rect_max: viewport_rect.Max + ImVec2(1, 1), intersect_with_current_clip_rect: false); // FIXME: Need to stricty ensure ImDrawCmd are not merged (ElemCount==6 checks below will verify that)
5703 draw_list->AddRectFilled(p_min: viewport_rect.Min, p_max: viewport_rect.Max, col);
5704 ImDrawCmd cmd = draw_list->CmdBuffer.back();
5705 IM_ASSERT(cmd.ElemCount == 6);
5706 draw_list->CmdBuffer.pop_back();
5707 draw_list->CmdBuffer.push_front(v: cmd);
5708 draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command.
5709 draw_list->PopClipRect();
5710 }
5711
5712 // Draw over sibling docking nodes in a same docking tree
5713 if (window->RootWindow->DockIsActive)
5714 {
5715 ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window: window->RootWindowDockTree)->DrawList;
5716 draw_list->ChannelsMerge();
5717 if (draw_list->CmdBuffer.Size == 0)
5718 draw_list->AddDrawCmd();
5719 draw_list->PushClipRect(clip_rect_min: viewport_rect.Min, clip_rect_max: viewport_rect.Max, intersect_with_current_clip_rect: false);
5720 RenderRectFilledWithHole(draw_list, outer: window->RootWindowDockTree->Rect(), inner: window->RootWindow->Rect(), col, rounding: 0.0f);// window->RootWindowDockTree->WindowRounding);
5721 draw_list->PopClipRect();
5722 }
5723}
5724
5725ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window)
5726{
5727 ImGuiContext& g = *GImGui;
5728 ImGuiWindow* bottom_most_visible_window = parent_window;
5729 for (int i = FindWindowDisplayIndex(window: parent_window); i >= 0; i--)
5730 {
5731 ImGuiWindow* window = g.Windows[i];
5732 if (window->Flags & ImGuiWindowFlags_ChildWindow)
5733 continue;
5734 if (!IsWindowWithinBeginStackOf(window, potential_parent: parent_window))
5735 break;
5736 if (IsWindowActiveAndVisible(window) && GetWindowDisplayLayer(window) <= GetWindowDisplayLayer(window: parent_window))
5737 bottom_most_visible_window = window;
5738 }
5739 return bottom_most_visible_window;
5740}
5741
5742// Important: AddWindowToDrawData() has not been called yet, meaning DockNodeHost windows needs a DrawList->ChannelsMerge() before usage.
5743// We call ChannelsMerge() lazily here at it is faster that doing a full iteration of g.Windows[] prior to calling RenderDimmedBackgrounds().
5744static void ImGui::RenderDimmedBackgrounds()
5745{
5746 ImGuiContext& g = *GImGui;
5747 ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal();
5748 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
5749 return;
5750 const bool dim_bg_for_modal = (modal_window != NULL);
5751 const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active);
5752 if (!dim_bg_for_modal && !dim_bg_for_window_list)
5753 return;
5754
5755 ImGuiViewport* viewports_already_dimmed[2] = { NULL, NULL };
5756 if (dim_bg_for_modal)
5757 {
5758 // Draw dimming behind modal or a begin stack child, whichever comes first in draw order.
5759 ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(parent_window: modal_window);
5760 RenderDimmedBackgroundBehindWindow(window: dim_behind_window, col: GetColorU32(col: modal_window->DC.ModalDimBgColor, alpha_mul: g.DimBgRatio));
5761 viewports_already_dimmed[0] = modal_window->Viewport;
5762 }
5763 else if (dim_bg_for_window_list)
5764 {
5765 // Draw dimming behind CTRL+Tab target window and behind CTRL+Tab UI window
5766 RenderDimmedBackgroundBehindWindow(window: g.NavWindowingTargetAnim, col: GetColorU32(idx: ImGuiCol_NavWindowingDimBg, alpha_mul: g.DimBgRatio));
5767 if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->Viewport && g.NavWindowingListWindow->Viewport != g.NavWindowingTargetAnim->Viewport)
5768 RenderDimmedBackgroundBehindWindow(window: g.NavWindowingListWindow, col: GetColorU32(idx: ImGuiCol_NavWindowingDimBg, alpha_mul: g.DimBgRatio));
5769 viewports_already_dimmed[0] = g.NavWindowingTargetAnim->Viewport;
5770 viewports_already_dimmed[1] = g.NavWindowingListWindow ? g.NavWindowingListWindow->Viewport : NULL;
5771
5772 // Draw border around CTRL+Tab target window
5773 ImGuiWindow* window = g.NavWindowingTargetAnim;
5774 ImGuiViewport* viewport = window->Viewport;
5775 float distance = g.FontSize;
5776 ImRect bb = window->Rect();
5777 bb.Expand(amount: distance);
5778 if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y)
5779 bb.Expand(amount: -distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward
5780 window->DrawList->ChannelsMerge();
5781 if (window->DrawList->CmdBuffer.Size == 0)
5782 window->DrawList->AddDrawCmd();
5783 window->DrawList->PushClipRect(clip_rect_min: viewport->Pos, clip_rect_max: viewport->Pos + viewport->Size);
5784 window->DrawList->AddRect(p_min: bb.Min, p_max: bb.Max, col: GetColorU32(idx: ImGuiCol_NavWindowingHighlight, alpha_mul: g.NavWindowingHighlightAlpha), rounding: window->WindowRounding, flags: 0, thickness: 3.0f);
5785 window->DrawList->PopClipRect();
5786 }
5787
5788 // Draw dimming background on _other_ viewports than the ones our windows are in
5789 for (ImGuiViewportP* viewport : g.Viewports)
5790 {
5791 if (viewport == viewports_already_dimmed[0] || viewport == viewports_already_dimmed[1])
5792 continue;
5793 if (modal_window && viewport->Window && IsWindowAbove(potential_above: viewport->Window, potential_below: modal_window))
5794 continue;
5795 ImDrawList* draw_list = GetForegroundDrawList(viewport);
5796 const ImU32 dim_bg_col = GetColorU32(idx: dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, alpha_mul: g.DimBgRatio);
5797 draw_list->AddRectFilled(p_min: viewport->Pos, p_max: viewport->Pos + viewport->Size, col: dim_bg_col);
5798 }
5799}
5800
5801// 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.
5802void ImGui::EndFrame()
5803{
5804 ImGuiContext& g = *GImGui;
5805 IM_ASSERT(g.Initialized);
5806
5807 // Don't process EndFrame() multiple times.
5808 if (g.FrameCountEnded == g.FrameCount)
5809 return;
5810 IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
5811
5812 CallContextHooks(ctx: &g, hook_type: ImGuiContextHookType_EndFramePre);
5813
5814 // [EXPERIMENTAL] Recover from errors
5815 if (g.IO.ConfigErrorRecovery)
5816 ErrorRecoveryTryToRecoverState(state_in: &g.StackSizesInNewFrame);
5817 ErrorCheckEndFrameSanityChecks();
5818 ErrorCheckEndFrameFinalizeErrorTooltip();
5819
5820 // Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
5821 ImGuiPlatformImeData* ime_data = &g.PlatformImeData;
5822 if (g.PlatformIO.Platform_SetImeDataFn != NULL && memcmp(s1: ime_data, s2: &g.PlatformImeDataPrev, n: sizeof(ImGuiPlatformImeData)) != 0)
5823 {
5824 ImGuiViewport* viewport = FindViewportByID(id: g.PlatformImeViewport);
5825 IMGUI_DEBUG_LOG_IO("[io] Calling Platform_SetImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f)\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y);
5826 if (viewport == NULL)
5827 viewport = GetMainViewport();
5828 g.PlatformIO.Platform_SetImeDataFn(&g, viewport, ime_data);
5829 }
5830
5831 // Hide implicit/fallback "Debug" window if it hasn't been used
5832 g.WithinFrameScopeWithImplicitWindow = false;
5833 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
5834 g.CurrentWindow->Active = false;
5835 End();
5836
5837 // Update navigation: CTRL+Tab, wrap-around requests
5838 NavEndFrame();
5839
5840 // Update docking
5841 DockContextEndFrame(ctx: &g);
5842
5843 SetCurrentViewport(NULL, NULL);
5844
5845 // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
5846 if (g.DragDropActive)
5847 {
5848 bool is_delivered = g.DragDropPayload.Delivery;
5849 bool is_elapsed = (g.DragDropSourceFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_PayloadAutoExpire) || g.DragDropMouseButton == -1 || !IsMouseDown(button: g.DragDropMouseButton));
5850 if (is_delivered || is_elapsed)
5851 ClearDragDrop();
5852 }
5853
5854 // Drag and Drop: Fallback for missing source tooltip. This is not ideal but better than nothing.
5855 // If you want to handle source item disappearing: instead of submitting your description tooltip
5856 // in the BeginDragDropSource() block of the dragged item, you can submit them from a safe single spot
5857 // (e.g. end of your item loop, or before EndFrame) by reading payload data.
5858 // In the typical case, the contents of drag tooltip should be possible to infer solely from payload data.
5859 if (g.DragDropActive && g.DragDropSourceFrameCount + 1 < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
5860 {
5861 g.DragDropWithinSource = true;
5862 SetTooltip("...");
5863 g.DragDropWithinSource = false;
5864 }
5865
5866 // End frame
5867 g.WithinFrameScope = false;
5868 g.FrameCountEnded = g.FrameCount;
5869
5870 // Initiate moving window + handle left-click and right-click focus
5871 UpdateMouseMovingWindowEndFrame();
5872
5873 // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)
5874 UpdateViewportsEndFrame();
5875
5876 // Sort the window list so that all child windows are after their parent
5877 // We cannot do that on FocusWindow() because children may not exist yet
5878 g.WindowsTempSortBuffer.resize(new_size: 0);
5879 g.WindowsTempSortBuffer.reserve(new_capacity: g.Windows.Size);
5880 for (ImGuiWindow* window : g.Windows)
5881 {
5882 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
5883 continue;
5884 AddWindowToSortBuffer(out_sorted_windows: &g.WindowsTempSortBuffer, window);
5885 }
5886
5887 // 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.
5888 IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
5889 g.Windows.swap(rhs&: g.WindowsTempSortBuffer);
5890 g.IO.MetricsActiveWindows = g.WindowsActiveCount;
5891
5892 // Unlock font atlas
5893 g.IO.Fonts->Locked = false;
5894
5895 // Clear Input data for next frame
5896 g.IO.MousePosPrev = g.IO.MousePos;
5897 g.IO.AppFocusLost = false;
5898 g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
5899 g.IO.InputQueueCharacters.resize(new_size: 0);
5900
5901 CallContextHooks(ctx: &g, hook_type: ImGuiContextHookType_EndFramePost);
5902}
5903
5904// Prepare the data for rendering so you can call GetDrawData()
5905// (As with anything within the ImGui:: namspace this doesn't touch your GPU or graphics API at all:
5906// it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend)
5907void ImGui::Render()
5908{
5909 ImGuiContext& g = *GImGui;
5910 IM_ASSERT(g.Initialized);
5911
5912 if (g.FrameCountEnded != g.FrameCount)
5913 EndFrame();
5914 if (g.FrameCountRendered == g.FrameCount)
5915 return;
5916 g.FrameCountRendered = g.FrameCount;
5917
5918 g.IO.MetricsRenderWindows = 0;
5919 CallContextHooks(ctx: &g, hook_type: ImGuiContextHookType_RenderPre);
5920
5921 // Add background ImDrawList (for each active viewport)
5922 for (ImGuiViewportP* viewport : g.Viewports)
5923 {
5924 InitViewportDrawData(viewport);
5925 if (viewport->BgFgDrawLists[0] != NULL)
5926 AddDrawListToDrawDataEx(draw_data: &viewport->DrawDataP, out_list: viewport->DrawDataBuilder.Layers[0], draw_list: GetBackgroundDrawList(viewport));
5927 }
5928
5929 // Draw modal/window whitening backgrounds
5930 RenderDimmedBackgrounds();
5931
5932 // Add ImDrawList to render
5933 ImGuiWindow* windows_to_render_top_most[2];
5934 windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindowDockTree : NULL;
5935 windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
5936 for (ImGuiWindow* window : g.Windows)
5937 {
5938 IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'"
5939 if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
5940 AddRootWindowToDrawData(window);
5941 }
5942 for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
5943 if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(window: windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
5944 AddRootWindowToDrawData(window: windows_to_render_top_most[n]);
5945
5946 // Draw software mouse cursor if requested by io.MouseDrawCursor flag
5947 if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None)
5948 RenderMouseCursor(base_pos: g.IO.MousePos, base_scale: g.Style.MouseCursorScale, mouse_cursor: g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
5949
5950 // Setup ImDrawData structures for end-user
5951 g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;
5952 for (ImGuiViewportP* viewport : g.Viewports)
5953 {
5954 FlattenDrawDataIntoSingleLayer(builder: &viewport->DrawDataBuilder);
5955
5956 // Add foreground ImDrawList (for each active viewport)
5957 if (viewport->BgFgDrawLists[1] != NULL)
5958 AddDrawListToDrawDataEx(draw_data: &viewport->DrawDataP, out_list: viewport->DrawDataBuilder.Layers[0], draw_list: GetForegroundDrawList(viewport));
5959
5960 // We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch).
5961 ImDrawData* draw_data = &viewport->DrawDataP;
5962 IM_ASSERT(draw_data->CmdLists.Size == draw_data->CmdListsCount);
5963 for (ImDrawList* draw_list : draw_data->CmdLists)
5964 draw_list->_PopUnusedDrawCmd();
5965
5966 g.IO.MetricsRenderVertices += draw_data->TotalVtxCount;
5967 g.IO.MetricsRenderIndices += draw_data->TotalIdxCount;
5968 }
5969
5970 CallContextHooks(ctx: &g, hook_type: ImGuiContextHookType_RenderPost);
5971}
5972
5973// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
5974// CalcTextSize("") should return ImVec2(0.0f, g.FontSize)
5975ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
5976{
5977 ImGuiContext& g = *GImGui;
5978
5979 const char* text_display_end;
5980 if (hide_text_after_double_hash)
5981 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
5982 else
5983 text_display_end = text_end;
5984
5985 ImFont* font = g.Font;
5986 const float font_size = g.FontSize;
5987 if (text == text_display_end)
5988 return ImVec2(0.0f, font_size);
5989 ImVec2 text_size = font->CalcTextSizeA(size: font_size, FLT_MAX, wrap_width, text_begin: text, text_end: text_display_end, NULL);
5990
5991 // Round
5992 // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
5993 // FIXME: Investigate using ceilf or e.g.
5994 // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
5995 // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html
5996 text_size.x = IM_TRUNC(text_size.x + 0.99999f);
5997
5998 return text_size;
5999}
6000
6001// Find window given position, search front-to-back
6002// - Typically write output back to g.HoveredWindow and g.HoveredWindowUnderMovingWindow.
6003// - FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
6004// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
6005// called, aka before the next Begin(). Moving window isn't affected.
6006// - The 'find_first_and_in_any_viewport = true' mode is only used by TestEngine. It is simpler to maintain here.
6007void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window)
6008{
6009 ImGuiContext& g = *GImGui;
6010 ImGuiWindow* hovered_window = NULL;
6011 ImGuiWindow* hovered_window_under_moving_window = NULL;
6012
6013 // Special handling for the window being moved: Ignore the mouse viewport check (because it may reset/lose its viewport during the undocking frame)
6014 ImGuiViewportP* backup_moving_window_viewport = NULL;
6015 if (find_first_and_in_any_viewport == false && g.MovingWindow)
6016 {
6017 backup_moving_window_viewport = g.MovingWindow->Viewport;
6018 g.MovingWindow->Viewport = g.MouseViewport;
6019 if (!(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
6020 hovered_window = g.MovingWindow;
6021 }
6022
6023 ImVec2 padding_regular = g.Style.TouchExtraPadding;
6024 ImVec2 padding_for_resize = g.IO.ConfigWindowsResizeFromEdges ? g.WindowsHoverPadding : padding_regular;
6025 for (int i = g.Windows.Size - 1; i >= 0; i--)
6026 {
6027 ImGuiWindow* window = g.Windows[i];
6028 IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
6029 if (!window->WasActive || window->Hidden)
6030 continue;
6031 if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
6032 continue;
6033 IM_ASSERT(window->Viewport);
6034 if (window->Viewport != g.MouseViewport)
6035 continue;
6036
6037 // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
6038 ImVec2 hit_padding = (window->Flags & (ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) ? padding_regular : padding_for_resize;
6039 if (!window->OuterRectClipped.ContainsWithPad(p: pos, pad: hit_padding))
6040 continue;
6041
6042 // Support for one rectangular hole in any given window
6043 // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
6044 if (window->HitTestHoleSize.x != 0)
6045 {
6046 ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
6047 ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
6048 if (ImRect(hole_pos, hole_pos + hole_size).Contains(p: pos))
6049 continue;
6050 }
6051
6052 if (find_first_and_in_any_viewport)
6053 {
6054 hovered_window = window;
6055 break;
6056 }
6057 else
6058 {
6059 if (hovered_window == NULL)
6060 hovered_window = window;
6061 IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
6062 if (hovered_window_under_moving_window == NULL && (!g.MovingWindow || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree))
6063 hovered_window_under_moving_window = window;
6064 if (hovered_window && hovered_window_under_moving_window)
6065 break;
6066 }
6067 }
6068
6069 *out_hovered_window = hovered_window;
6070 if (out_hovered_window_under_moving_window != NULL)
6071 *out_hovered_window_under_moving_window = hovered_window_under_moving_window;
6072 if (find_first_and_in_any_viewport == false && g.MovingWindow)
6073 g.MovingWindow->Viewport = backup_moving_window_viewport;
6074}
6075
6076bool ImGui::IsItemActive()
6077{
6078 ImGuiContext& g = *GImGui;
6079 if (g.ActiveId)
6080 return g.ActiveId == g.LastItemData.ID;
6081 return false;
6082}
6083
6084bool ImGui::IsItemActivated()
6085{
6086 ImGuiContext& g = *GImGui;
6087 if (g.ActiveId)
6088 if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID)
6089 return true;
6090 return false;
6091}
6092
6093bool ImGui::IsItemDeactivated()
6094{
6095 ImGuiContext& g = *GImGui;
6096 if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated)
6097 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
6098 return (g.ActiveIdPreviousFrame == g.LastItemData.ID && g.ActiveIdPreviousFrame != 0 && g.ActiveId != g.LastItemData.ID);
6099}
6100
6101bool ImGui::IsItemDeactivatedAfterEdit()
6102{
6103 ImGuiContext& g = *GImGui;
6104 return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
6105}
6106
6107// == (GetItemID() == GetFocusID() && GetFocusID() != 0)
6108bool ImGui::IsItemFocused()
6109{
6110 ImGuiContext& g = *GImGui;
6111 if (g.NavId != g.LastItemData.ID || g.NavId == 0)
6112 return false;
6113
6114 // Special handling for the dummy item after Begin() which represent the title bar or tab.
6115 // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
6116 ImGuiWindow* window = g.CurrentWindow;
6117 if (g.LastItemData.ID == window->ID && window->WriteAccessed)
6118 return false;
6119
6120 return true;
6121}
6122
6123// Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()!
6124// Most widgets have specific reactions based on mouse-up/down state, mouse position etc.
6125bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
6126{
6127 return IsMouseClicked(button: mouse_button) && IsItemHovered(flags: ImGuiHoveredFlags_None);
6128}
6129
6130bool ImGui::IsItemToggledOpen()
6131{
6132 ImGuiContext& g = *GImGui;
6133 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
6134}
6135
6136// Call after a Selectable() or TreeNode() involved in multi-selection.
6137// Useful if you need the per-item information before reaching EndMultiSelect(), e.g. for rendering purpose.
6138// This is only meant to be called inside a BeginMultiSelect()/EndMultiSelect() block.
6139// (Outside of multi-select, it would be misleading/ambiguous to report this signal, as widgets
6140// return e.g. a pressed event and user code is in charge of altering selection in ways we cannot predict.)
6141bool ImGui::IsItemToggledSelection()
6142{
6143 ImGuiContext& g = *GImGui;
6144 IM_ASSERT(g.CurrentMultiSelect != NULL); // Can only be used inside a BeginMultiSelect()/EndMultiSelect()
6145 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
6146}
6147
6148// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
6149// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
6150// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
6151bool ImGui::IsAnyItemHovered()
6152{
6153 ImGuiContext& g = *GImGui;
6154 return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
6155}
6156
6157bool ImGui::IsAnyItemActive()
6158{
6159 ImGuiContext& g = *GImGui;
6160 return g.ActiveId != 0;
6161}
6162
6163bool ImGui::IsAnyItemFocused()
6164{
6165 ImGuiContext& g = *GImGui;
6166 return g.NavId != 0 && g.NavCursorVisible;
6167}
6168
6169bool ImGui::IsItemVisible()
6170{
6171 ImGuiContext& g = *GImGui;
6172 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) != 0;
6173}
6174
6175bool ImGui::IsItemEdited()
6176{
6177 ImGuiContext& g = *GImGui;
6178 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0;
6179}
6180
6181// Allow next item to be overlapped by subsequent items.
6182// This works by requiring HoveredId to match for two subsequent frames,
6183// so if a following items overwrite it our interactions will naturally be disabled.
6184void ImGui::SetNextItemAllowOverlap()
6185{
6186 ImGuiContext& g = *GImGui;
6187 g.NextItemData.ItemFlags |= ImGuiItemFlags_AllowOverlap;
6188}
6189
6190#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
6191// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
6192// FIXME-LEGACY: Use SetNextItemAllowOverlap() *before* your item instead.
6193void ImGui::SetItemAllowOverlap()
6194{
6195 ImGuiContext& g = *GImGui;
6196 ImGuiID id = g.LastItemData.ID;
6197 if (g.HoveredId == id)
6198 g.HoveredIdAllowOverlap = true;
6199 if (g.ActiveId == id) // Before we made this obsolete, most calls to SetItemAllowOverlap() used to avoid this path by testing g.ActiveId != id.
6200 g.ActiveIdAllowOverlap = true;
6201}
6202#endif
6203
6204// This is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations.
6205// FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version if needed?
6206void ImGui::SetActiveIdUsingAllKeyboardKeys()
6207{
6208 ImGuiContext& g = *GImGui;
6209 IM_ASSERT(g.ActiveId != 0);
6210 g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_COUNT) - 1;
6211 g.ActiveIdUsingAllKeyboardKeys = true;
6212 NavMoveRequestCancel();
6213}
6214
6215ImGuiID ImGui::GetItemID()
6216{
6217 ImGuiContext& g = *GImGui;
6218 return g.LastItemData.ID;
6219}
6220
6221ImVec2 ImGui::GetItemRectMin()
6222{
6223 ImGuiContext& g = *GImGui;
6224 return g.LastItemData.Rect.Min;
6225}
6226
6227ImVec2 ImGui::GetItemRectMax()
6228{
6229 ImGuiContext& g = *GImGui;
6230 return g.LastItemData.Rect.Max;
6231}
6232
6233ImVec2 ImGui::GetItemRectSize()
6234{
6235 ImGuiContext& g = *GImGui;
6236 return g.LastItemData.Rect.GetSize();
6237}
6238
6239// Prior to v1.90 2023/10/16, the BeginChild() function took a 'bool border = false' parameter instead of 'ImGuiChildFlags child_flags = 0'.
6240// ImGuiChildFlags_Borders is defined as always == 1 in order to allow old code passing 'true'. Read comments in imgui.h for details!
6241bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
6242{
6243 ImGuiID id = GetCurrentWindow()->GetID(str: str_id);
6244 return BeginChildEx(name: str_id, id, size_arg, child_flags, window_flags);
6245}
6246
6247bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
6248{
6249 return BeginChildEx(NULL, id, size_arg, child_flags, window_flags);
6250}
6251
6252bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
6253{
6254 ImGuiContext& g = *GImGui;
6255 ImGuiWindow* parent_window = g.CurrentWindow;
6256 IM_ASSERT(id != 0);
6257
6258 // Sanity check as it is likely that some user will accidentally pass ImGuiWindowFlags into the ImGuiChildFlags argument.
6259 const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_FrameStyle | ImGuiChildFlags_NavFlattened;
6260 IM_UNUSED(ImGuiChildFlags_SupportedMask_);
6261 IM_ASSERT((child_flags & ~ImGuiChildFlags_SupportedMask_) == 0 && "Illegal ImGuiChildFlags value. Did you pass ImGuiWindowFlags values instead of ImGuiChildFlags?");
6262 IM_ASSERT((window_flags & ImGuiWindowFlags_AlwaysAutoResize) == 0 && "Cannot specify ImGuiWindowFlags_AlwaysAutoResize for BeginChild(). Use ImGuiChildFlags_AlwaysAutoResize!");
6263 if (child_flags & ImGuiChildFlags_AlwaysAutoResize)
6264 {
6265 IM_ASSERT((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0 && "Cannot use ImGuiChildFlags_ResizeX or ImGuiChildFlags_ResizeY with ImGuiChildFlags_AlwaysAutoResize!");
6266 IM_ASSERT((child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) != 0 && "Must use ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY with ImGuiChildFlags_AlwaysAutoResize!");
6267 }
6268#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
6269 if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding)
6270 child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding;
6271 if (window_flags & ImGuiWindowFlags_NavFlattened)
6272 child_flags |= ImGuiChildFlags_NavFlattened;
6273#endif
6274 if (child_flags & ImGuiChildFlags_AutoResizeX)
6275 child_flags &= ~ImGuiChildFlags_ResizeX;
6276 if (child_flags & ImGuiChildFlags_AutoResizeY)
6277 child_flags &= ~ImGuiChildFlags_ResizeY;
6278
6279 // Set window flags
6280 window_flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking;
6281 window_flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
6282 if (child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize))
6283 window_flags |= ImGuiWindowFlags_AlwaysAutoResize;
6284 if ((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0)
6285 window_flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
6286
6287 // Special framed style
6288 if (child_flags & ImGuiChildFlags_FrameStyle)
6289 {
6290 PushStyleColor(idx: ImGuiCol_ChildBg, col: g.Style.Colors[ImGuiCol_FrameBg]);
6291 PushStyleVar(idx: ImGuiStyleVar_ChildRounding, val: g.Style.FrameRounding);
6292 PushStyleVar(idx: ImGuiStyleVar_ChildBorderSize, val: g.Style.FrameBorderSize);
6293 PushStyleVar(idx: ImGuiStyleVar_WindowPadding, val: g.Style.FramePadding);
6294 child_flags |= ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding;
6295 window_flags |= ImGuiWindowFlags_NoMove;
6296 }
6297
6298 // Forward size
6299 // Important: Begin() has special processing to switch condition to ImGuiCond_FirstUseEver for a given axis when ImGuiChildFlags_ResizeXXX is set.
6300 // (the alternative would to store conditional flags per axis, which is possible but more code)
6301 const ImVec2 size_avail = GetContentRegionAvail();
6302 const ImVec2 size_default((child_flags & ImGuiChildFlags_AutoResizeX) ? 0.0f : size_avail.x, (child_flags & ImGuiChildFlags_AutoResizeY) ? 0.0f : size_avail.y);
6303 ImVec2 size = CalcItemSize(size: size_arg, default_w: size_default.x, default_h: size_default.y);
6304
6305 // A SetNextWindowSize() call always has priority (#8020)
6306 // (since the code in Begin() never supported SizeVal==0.0f aka auto-resize via SetNextWindowSize() call, we don't support it here for now)
6307 // FIXME: We only support ImGuiCond_Always in this path. Supporting other paths would requires to obtain window pointer.
6308 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) != 0 && (g.NextWindowData.SizeCond & ImGuiCond_Always) != 0)
6309 {
6310 if (g.NextWindowData.SizeVal.x > 0.0f)
6311 {
6312 size.x = g.NextWindowData.SizeVal.x;
6313 child_flags &= ~ImGuiChildFlags_ResizeX;
6314 }
6315 if (g.NextWindowData.SizeVal.y > 0.0f)
6316 {
6317 size.y = g.NextWindowData.SizeVal.y;
6318 child_flags &= ~ImGuiChildFlags_ResizeY;
6319 }
6320 }
6321 SetNextWindowSize(size);
6322
6323 // Forward child flags
6324 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasChildFlags;
6325 g.NextWindowData.ChildFlags = child_flags;
6326
6327 // 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.
6328 // FIXME: 2023/11/14: commented out shorted version. We had an issue with multiple ### in child window path names, which the trailing hash helped workaround.
6329 // e.g. "ParentName###ParentIdentifier/ChildName###ChildIdentifier" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it.
6330 const char* temp_window_name;
6331 /*if (name && parent_window->IDStack.back() == parent_window->ID)
6332 ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s", parent_window->Name, name); // May omit ID if in root of ID stack
6333 else*/
6334 if (name)
6335 ImFormatStringToTempBuffer(out_buf: &temp_window_name, NULL, fmt: "%s/%s_%08X", parent_window->Name, name, id);
6336 else
6337 ImFormatStringToTempBuffer(out_buf: &temp_window_name, NULL, fmt: "%s/%08X", parent_window->Name, id);
6338
6339 // Set style
6340 const float backup_border_size = g.Style.ChildBorderSize;
6341 if ((child_flags & ImGuiChildFlags_Borders) == 0)
6342 g.Style.ChildBorderSize = 0.0f;
6343
6344 // Begin into window
6345 const bool ret = Begin(name: temp_window_name, NULL, flags: window_flags);
6346
6347 // Restore style
6348 g.Style.ChildBorderSize = backup_border_size;
6349 if (child_flags & ImGuiChildFlags_FrameStyle)
6350 {
6351 PopStyleVar(count: 3);
6352 PopStyleColor();
6353 }
6354
6355 ImGuiWindow* child_window = g.CurrentWindow;
6356 child_window->ChildId = id;
6357
6358 // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
6359 // While this is not really documented/defined, it seems that the expected thing to do.
6360 if (child_window->BeginCount == 1)
6361 parent_window->DC.CursorPos = child_window->Pos;
6362
6363 // Process navigation-in immediately so NavInit can run on first frame
6364 // Can enter a child if (A) it has navigable items or (B) it can be scrolled.
6365 const ImGuiID temp_id_for_activation = ImHashStr(data_p: "##Child", data_size: 0, seed: id);
6366 if (g.ActiveId == temp_id_for_activation)
6367 ClearActiveID();
6368 if (g.NavActivateId == id && !(child_flags & ImGuiChildFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY))
6369 {
6370 FocusWindow(window: child_window);
6371 NavInitWindow(window: child_window, force_reinit: false);
6372 SetActiveID(id: temp_id_for_activation, window: child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
6373 g.ActiveIdSource = g.NavInputSource;
6374 }
6375 return ret;
6376}
6377
6378void ImGui::EndChild()
6379{
6380 ImGuiContext& g = *GImGui;
6381 ImGuiWindow* child_window = g.CurrentWindow;
6382
6383 IM_ASSERT(g.WithinEndChild == false);
6384 IM_ASSERT(child_window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls
6385
6386 g.WithinEndChild = true;
6387 ImVec2 child_size = child_window->Size;
6388 End();
6389 if (child_window->BeginCount == 1)
6390 {
6391 ImGuiWindow* parent_window = g.CurrentWindow;
6392 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + child_size);
6393 ItemSize(size: child_size);
6394 const bool nav_flattened = (child_window->ChildFlags & ImGuiChildFlags_NavFlattened) != 0;
6395 if ((child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY) && !nav_flattened)
6396 {
6397 ItemAdd(bb, id: child_window->ChildId);
6398 RenderNavCursor(bb, id: child_window->ChildId);
6399
6400 // When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying)
6401 if (child_window->DC.NavLayersActiveMask == 0 && child_window == g.NavWindow)
6402 RenderNavCursor(bb: ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), id: g.NavId, flags: ImGuiNavRenderCursorFlags_Compact);
6403 }
6404 else
6405 {
6406 // Not navigable into
6407 // - This is a bit of a fringe use case, mostly useful for undecorated, non-scrolling contents childs, or empty childs.
6408 // - We could later decide to not apply this path if ImGuiChildFlags_FrameStyle or ImGuiChildFlags_Borders is set.
6409 ItemAdd(bb, id: child_window->ChildId, NULL, extra_flags: ImGuiItemFlags_NoNav);
6410
6411 // But when flattened we directly reach items, adjust active layer mask accordingly
6412 if (nav_flattened)
6413 parent_window->DC.NavLayersActiveMaskNext |= child_window->DC.NavLayersActiveMaskNext;
6414 }
6415 if (g.HoveredWindow == child_window)
6416 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
6417 }
6418 g.WithinEndChild = false;
6419 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
6420}
6421
6422static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
6423{
6424 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
6425 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
6426 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
6427 window->SetWindowDockAllowFlags = enabled ? (window->SetWindowDockAllowFlags | flags) : (window->SetWindowDockAllowFlags & ~flags);
6428}
6429
6430ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
6431{
6432 ImGuiContext& g = *GImGui;
6433 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(key: id);
6434}
6435
6436ImGuiWindow* ImGui::FindWindowByName(const char* name)
6437{
6438 ImGuiID id = ImHashStr(data_p: name);
6439 return FindWindowByID(id);
6440}
6441
6442static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
6443{
6444 const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
6445 window->ViewportPos = main_viewport->Pos;
6446 if (settings->ViewportId)
6447 {
6448 window->ViewportId = settings->ViewportId;
6449 window->ViewportPos = ImVec2(settings->ViewportPos.x, settings->ViewportPos.y);
6450 }
6451 window->Pos = ImTrunc(v: ImVec2(settings->Pos.x + window->ViewportPos.x, settings->Pos.y + window->ViewportPos.y));
6452 if (settings->Size.x > 0 && settings->Size.y > 0)
6453 window->Size = window->SizeFull = ImTrunc(v: ImVec2(settings->Size.x, settings->Size.y));
6454 window->Collapsed = settings->Collapsed;
6455 window->DockId = settings->DockId;
6456 window->DockOrder = settings->DockOrder;
6457}
6458
6459static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags)
6460{
6461 ImGuiContext& g = *GImGui;
6462
6463 const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0 && ((new_flags & ImGuiWindowFlags_Popup) == 0 || (new_flags & ImGuiWindowFlags_ChildMenu) != 0);
6464 const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild;
6465 if ((just_created || child_flag_changed) && !new_is_explicit_child)
6466 {
6467 IM_ASSERT(!g.WindowsFocusOrder.contains(window));
6468 g.WindowsFocusOrder.push_back(v: window);
6469 window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1);
6470 }
6471 else if (!just_created && child_flag_changed && new_is_explicit_child)
6472 {
6473 IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window);
6474 for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++)
6475 g.WindowsFocusOrder[n]->FocusOrder--;
6476 g.WindowsFocusOrder.erase(it: g.WindowsFocusOrder.Data + window->FocusOrder);
6477 window->FocusOrder = -1;
6478 }
6479 window->IsExplicitChild = new_is_explicit_child;
6480}
6481
6482static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
6483{
6484 // Initial window state with e.g. default/arbitrary window position
6485 // Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
6486 const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
6487 window->Pos = main_viewport->Pos + ImVec2(60, 60);
6488 window->Size = window->SizeFull = ImVec2(0, 0);
6489 window->ViewportPos = main_viewport->Pos;
6490 window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = window->SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
6491
6492 if (settings != NULL)
6493 {
6494 SetWindowConditionAllowFlags(window, flags: ImGuiCond_FirstUseEver, enabled: false);
6495 ApplyWindowSettings(window, settings);
6496 }
6497 window->DC.CursorStartPos = window->DC.CursorMaxPos = window->DC.IdealMaxPos = window->Pos; // So first call to CalcWindowContentSizes() doesn't return crazy values
6498
6499 if ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
6500 {
6501 window->AutoFitFramesX = window->AutoFitFramesY = 2;
6502 window->AutoFitOnlyGrows = false;
6503 }
6504 else
6505 {
6506 if (window->Size.x <= 0.0f)
6507 window->AutoFitFramesX = 2;
6508 if (window->Size.y <= 0.0f)
6509 window->AutoFitFramesY = 2;
6510 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
6511 }
6512}
6513
6514static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
6515{
6516 // Create window the first time
6517 //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
6518 ImGuiContext& g = *GImGui;
6519 ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
6520 window->Flags = flags;
6521 g.WindowsById.SetVoidPtr(key: window->ID, val: window);
6522
6523 ImGuiWindowSettings* settings = NULL;
6524 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
6525 if ((settings = ImGui::FindWindowSettingsByWindow(window)) != 0)
6526 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(p: settings);
6527
6528 InitOrLoadWindowSettings(window, settings);
6529
6530 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
6531 g.Windows.push_front(v: window); // Quite slow but rare and only once
6532 else
6533 g.Windows.push_back(v: window);
6534
6535 return window;
6536}
6537
6538static ImGuiWindow* GetWindowForTitleDisplay(ImGuiWindow* window)
6539{
6540 return window->DockNodeAsHost ? window->DockNodeAsHost->VisibleWindow : window;
6541}
6542
6543static ImGuiWindow* GetWindowForTitleAndMenuHeight(ImGuiWindow* window)
6544{
6545 return (window->DockNodeAsHost && window->DockNodeAsHost->VisibleWindow) ? window->DockNodeAsHost->VisibleWindow : window;
6546}
6547
6548static inline ImVec2 CalcWindowMinSize(ImGuiWindow* window)
6549{
6550 // We give windows non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)
6551 // FIXME: Essentially we want to restrict manual resizing to WindowMinSize+Decoration, and allow api resizing to be smaller.
6552 // Perhaps should tend further a neater test for this.
6553 ImGuiContext& g = *GImGui;
6554 ImVec2 size_min;
6555 if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup))
6556 {
6557 size_min.x = (window->ChildFlags & ImGuiChildFlags_ResizeX) ? g.Style.WindowMinSize.x : 4.0f;
6558 size_min.y = (window->ChildFlags & ImGuiChildFlags_ResizeY) ? g.Style.WindowMinSize.y : 4.0f;
6559 }
6560 else
6561 {
6562 size_min.x = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : 4.0f;
6563 size_min.y = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.y : 4.0f;
6564 }
6565
6566 // Reduce artifacts with very small windows
6567 ImGuiWindow* window_for_height = GetWindowForTitleAndMenuHeight(window);
6568 size_min.y = ImMax(lhs: size_min.y, rhs: window_for_height->TitleBarHeight + window_for_height->MenuBarHeight + ImMax(lhs: 0.0f, rhs: g.Style.WindowRounding - 1.0f));
6569 return size_min;
6570}
6571
6572static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired)
6573{
6574 ImGuiContext& g = *GImGui;
6575 ImVec2 new_size = size_desired;
6576 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
6577 {
6578 // See comments in SetNextWindowSizeConstraints() for details about setting size_min an size_max.
6579 ImRect cr = g.NextWindowData.SizeConstraintRect;
6580 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(v: new_size.x, mn: cr.Min.x, mx: cr.Max.x) : window->SizeFull.x;
6581 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(v: new_size.y, mn: cr.Min.y, mx: cr.Max.y) : window->SizeFull.y;
6582 if (g.NextWindowData.SizeCallback)
6583 {
6584 ImGuiSizeCallbackData data;
6585 data.UserData = g.NextWindowData.SizeCallbackUserData;
6586 data.Pos = window->Pos;
6587 data.CurrentSize = window->SizeFull;
6588 data.DesiredSize = new_size;
6589 g.NextWindowData.SizeCallback(&data);
6590 new_size = data.DesiredSize;
6591 }
6592 new_size.x = IM_TRUNC(new_size.x);
6593 new_size.y = IM_TRUNC(new_size.y);
6594 }
6595
6596 // Minimum size
6597 ImVec2 size_min = CalcWindowMinSize(window);
6598 return ImMax(lhs: new_size, rhs: size_min);
6599}
6600
6601static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)
6602{
6603 bool preserve_old_content_sizes = false;
6604 if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
6605 preserve_old_content_sizes = true;
6606 else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
6607 preserve_old_content_sizes = true;
6608 if (preserve_old_content_sizes)
6609 {
6610 *content_size_current = window->ContentSize;
6611 *content_size_ideal = window->ContentSizeIdeal;
6612 return;
6613 }
6614
6615 content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_TRUNC(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
6616 content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_TRUNC(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
6617 content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_TRUNC(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x);
6618 content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_TRUNC(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y);
6619}
6620
6621static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
6622{
6623 ImGuiContext& g = *GImGui;
6624 ImGuiStyle& style = g.Style;
6625 const float decoration_w_without_scrollbars = window->DecoOuterSizeX1 + window->DecoOuterSizeX2 - window->ScrollbarSizes.x;
6626 const float decoration_h_without_scrollbars = window->DecoOuterSizeY1 + window->DecoOuterSizeY2 - window->ScrollbarSizes.y;
6627 ImVec2 size_pad = window->WindowPadding * 2.0f;
6628 ImVec2 size_desired = size_contents + size_pad + ImVec2(decoration_w_without_scrollbars, decoration_h_without_scrollbars);
6629 if (window->Flags & ImGuiWindowFlags_Tooltip)
6630 {
6631 // Tooltip always resize
6632 return size_desired;
6633 }
6634 else
6635 {
6636 // Maximum window size is determined by the viewport size or monitor size
6637 ImVec2 size_min = CalcWindowMinSize(window);
6638 ImVec2 size_max = ImVec2(FLT_MAX, FLT_MAX);
6639
6640 // Child windows are layed within their parent (unless they are also popups/menus) and thus have no restriction
6641 if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || (window->Flags & ImGuiWindowFlags_Popup) != 0)
6642 {
6643 if (!window->ViewportOwned)
6644 size_max = ImGui::GetMainViewport()->WorkSize - style.DisplaySafeAreaPadding * 2.0f;
6645 const int monitor_idx = window->ViewportAllowPlatformMonitorExtend;
6646 if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size)
6647 size_max = g.PlatformIO.Monitors[monitor_idx].WorkSize - style.DisplaySafeAreaPadding * 2.0f;
6648 }
6649
6650 ImVec2 size_auto_fit = ImClamp(v: size_desired, mn: size_min, mx: ImMax(lhs: size_min, rhs: size_max));
6651
6652 // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one axis may be auto-fit when calculating scrollbars,
6653 // we may need to compute/store three variants of size_auto_fit, for x/y/xy.
6654 // Here we implement a workaround for child windows only, but a full solution would apply to normal windows as well:
6655 if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && !(window->ChildFlags & ImGuiChildFlags_ResizeY))
6656 size_auto_fit.y = window->SizeFull.y;
6657 else if (!(window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->ChildFlags & ImGuiChildFlags_ResizeY))
6658 size_auto_fit.x = window->SizeFull.x;
6659
6660 // When the window cannot fit all contents (either because of constraints, either because screen is too small),
6661 // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
6662 ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_desired: size_auto_fit);
6663 bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - decoration_w_without_scrollbars < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);
6664 bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_h_without_scrollbars < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar);
6665 if (will_have_scrollbar_x)
6666 size_auto_fit.y += style.ScrollbarSize;
6667 if (will_have_scrollbar_y)
6668 size_auto_fit.x += style.ScrollbarSize;
6669 return size_auto_fit;
6670 }
6671}
6672
6673ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window)
6674{
6675 ImVec2 size_contents_current;
6676 ImVec2 size_contents_ideal;
6677 CalcWindowContentSizes(window, content_size_current: &size_contents_current, content_size_ideal: &size_contents_ideal);
6678 ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents: size_contents_ideal);
6679 ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_desired: size_auto_fit);
6680 return size_final;
6681}
6682
6683static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window)
6684{
6685 if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
6686 return ImGuiCol_PopupBg;
6687 if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !window->DockIsActive)
6688 return ImGuiCol_ChildBg;
6689 return ImGuiCol_WindowBg;
6690}
6691
6692static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
6693{
6694 ImVec2 pos_min = ImLerp(a: corner_target, b: window->Pos, t: corner_norm); // Expected window upper-left
6695 ImVec2 pos_max = ImLerp(a: window->Pos + window->Size, b: corner_target, t: corner_norm); // Expected window lower-right
6696 ImVec2 size_expected = pos_max - pos_min;
6697 ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_desired: size_expected);
6698 *out_pos = pos_min;
6699 if (corner_norm.x == 0.0f)
6700 out_pos->x -= (size_constrained.x - size_expected.x);
6701 if (corner_norm.y == 0.0f)
6702 out_pos->y -= (size_constrained.y - size_expected.y);
6703 *out_size = size_constrained;
6704}
6705
6706// Data for resizing from resize grip / corner
6707struct ImGuiResizeGripDef
6708{
6709 ImVec2 CornerPosN;
6710 ImVec2 InnerDir;
6711 int AngleMin12, AngleMax12;
6712};
6713static const ImGuiResizeGripDef resize_grip_def[4] =
6714{
6715 { .CornerPosN: ImVec2(1, 1), .InnerDir: ImVec2(-1, -1), .AngleMin12: 0, .AngleMax12: 3 }, // Lower-right
6716 { .CornerPosN: ImVec2(0, 1), .InnerDir: ImVec2(+1, -1), .AngleMin12: 3, .AngleMax12: 6 }, // Lower-left
6717 { .CornerPosN: ImVec2(0, 0), .InnerDir: ImVec2(+1, +1), .AngleMin12: 6, .AngleMax12: 9 }, // Upper-left (Unused)
6718 { .CornerPosN: ImVec2(1, 0), .InnerDir: ImVec2(-1, +1), .AngleMin12: 9, .AngleMax12: 12 } // Upper-right (Unused)
6719};
6720
6721// Data for resizing from borders
6722struct ImGuiResizeBorderDef
6723{
6724 ImVec2 InnerDir; // Normal toward inside
6725 ImVec2 SegmentN1, SegmentN2; // End positions, normalized (0,0: upper left)
6726 float OuterAngle; // Angle toward outside
6727};
6728static const ImGuiResizeBorderDef resize_border_def[4] =
6729{
6730 { .InnerDir: ImVec2(+1, 0), .SegmentN1: ImVec2(0, 1), .SegmentN2: ImVec2(0, 0), IM_PI * 1.00f }, // Left
6731 { .InnerDir: ImVec2(-1, 0), .SegmentN1: ImVec2(1, 0), .SegmentN2: ImVec2(1, 1), IM_PI * 0.00f }, // Right
6732 { .InnerDir: ImVec2(0, +1), .SegmentN1: ImVec2(0, 0), .SegmentN2: ImVec2(1, 0), IM_PI * 1.50f }, // Up
6733 { .InnerDir: ImVec2(0, -1), .SegmentN1: ImVec2(1, 1), .SegmentN2: ImVec2(0, 1), IM_PI * 0.50f } // Down
6734};
6735
6736static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
6737{
6738 ImRect rect = window->Rect();
6739 if (thickness == 0.0f)
6740 rect.Max -= ImVec2(1, 1);
6741 if (border_n == ImGuiDir_Left) { return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); }
6742 if (border_n == ImGuiDir_Right) { return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); }
6743 if (border_n == ImGuiDir_Up) { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); }
6744 if (border_n == ImGuiDir_Down) { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); }
6745 IM_ASSERT(0);
6746 return ImRect();
6747}
6748
6749// 0..3: corners (Lower-right, Lower-left, Unused, Unused)
6750ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n)
6751{
6752 IM_ASSERT(n >= 0 && n < 4);
6753 ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID;
6754 id = ImHashStr(data_p: "#RESIZE", data_size: 0, seed: id);
6755 id = ImHashData(data_p: &n, data_size: sizeof(int), seed: id);
6756 return id;
6757}
6758
6759// Borders (Left, Right, Up, Down)
6760ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir)
6761{
6762 IM_ASSERT(dir >= 0 && dir < 4);
6763 int n = (int)dir + 4;
6764 ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID;
6765 id = ImHashStr(data_p: "#RESIZE", data_size: 0, seed: id);
6766 id = ImHashData(data_p: &n, data_size: sizeof(int), seed: id);
6767 return id;
6768}
6769
6770// Handle resize for: Resize Grips, Borders, Gamepad
6771// Return true when using auto-fit (double-click on resize grip)
6772static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect)
6773{
6774 ImGuiContext& g = *GImGui;
6775 ImGuiWindowFlags flags = window->Flags;
6776
6777 if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
6778 return false;
6779 if (window->WasActive == false) // Early out to avoid running this code for e.g. a hidden implicit/fallback Debug window.
6780 return false;
6781
6782 int ret_auto_fit_mask = 0x00;
6783 const float grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
6784 const float grip_hover_inner_size = (resize_grip_count > 0) ? IM_TRUNC(grip_draw_size * 0.75f) : 0.0f;
6785 const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_HOVER_PADDING : 0.0f;
6786
6787 ImRect clamp_rect = visibility_rect;
6788 const bool window_move_from_title_bar = g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar);
6789 if (window_move_from_title_bar)
6790 clamp_rect.Min.y -= window->TitleBarHeight;
6791
6792 ImVec2 pos_target(FLT_MAX, FLT_MAX);
6793 ImVec2 size_target(FLT_MAX, FLT_MAX);
6794
6795 // Clip mouse interaction rectangles within the viewport rectangle (in practice the narrowing is going to happen most of the time).
6796 // - Not narrowing would mostly benefit the situation where OS windows _without_ decoration have a threshold for hovering when outside their limits.
6797 // This is however not the case with current backends under Win32, but a custom borderless window implementation would benefit from it.
6798 // - When decoration are enabled we typically benefit from that distance, but then our resize elements would be conflicting with OS resize elements, so we also narrow.
6799 // - Note that we are unable to tell if the platform setup allows hovering with a distance threshold (on Win32, decorated window have such threshold).
6800 // We only clip interaction so we overwrite window->ClipRect, cannot call PushClipRect() yet as DrawList is not yet setup.
6801 const bool clip_with_viewport_rect = !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) || (g.IO.MouseHoveredViewport != window->ViewportId) || !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration);
6802 if (clip_with_viewport_rect)
6803 window->ClipRect = window->Viewport->GetMainRect();
6804
6805 // Resize grips and borders are on layer 1
6806 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
6807
6808 // Manual resize grips
6809 PushID(str_id: "#RESIZE");
6810 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
6811 {
6812 const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n];
6813 const ImVec2 corner = ImLerp(a: window->Pos, b: window->Pos + window->Size, t: def.CornerPosN);
6814
6815 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
6816 bool hovered, held;
6817 ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size);
6818 if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(a&: resize_rect.Min.x, b&: resize_rect.Max.x);
6819 if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(a&: resize_rect.Min.y, b&: resize_rect.Max.y);
6820 ImGuiID resize_grip_id = window->GetID(n: resize_grip_n); // == GetWindowResizeCornerID()
6821 ItemAdd(bb: resize_rect, id: resize_grip_id, NULL, extra_flags: ImGuiItemFlags_NoNav);
6822 ButtonBehavior(bb: resize_rect, id: resize_grip_id, out_hovered: &hovered, out_held: &held, flags: ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
6823 //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
6824 if (hovered || held)
6825 SetMouseCursor((resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE);
6826
6827 if (held && g.IO.MouseDoubleClicked[0])
6828 {
6829 // Auto-fit when double-clicking
6830 size_target = CalcWindowSizeAfterConstraint(window, size_desired: size_auto_fit);
6831 ret_auto_fit_mask = 0x03; // Both axises
6832 ClearActiveID();
6833 }
6834 else if (held)
6835 {
6836 // Resize from any of the four corners
6837 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
6838 ImVec2 clamp_min = ImVec2(def.CornerPosN.x == 1.0f ? clamp_rect.Min.x : -FLT_MAX, (def.CornerPosN.y == 1.0f || (def.CornerPosN.y == 0.0f && window_move_from_title_bar)) ? clamp_rect.Min.y : -FLT_MAX);
6839 ImVec2 clamp_max = ImVec2(def.CornerPosN.x == 0.0f ? clamp_rect.Max.x : +FLT_MAX, def.CornerPosN.y == 0.0f ? clamp_rect.Max.y : +FLT_MAX);
6840 ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(a: def.InnerDir * grip_hover_outer_size, b: def.InnerDir * -grip_hover_inner_size, t: def.CornerPosN); // Corner of the window corresponding to our corner grip
6841 corner_target = ImClamp(v: corner_target, mn: clamp_min, mx: clamp_max);
6842 CalcResizePosSizeFromAnyCorner(window, corner_target, corner_norm: def.CornerPosN, out_pos: &pos_target, out_size: &size_target);
6843 }
6844
6845 // Only lower-left grip is visible before hovering/activating
6846 if (resize_grip_n == 0 || held || hovered)
6847 resize_grip_col[resize_grip_n] = GetColorU32(idx: held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
6848 }
6849
6850 int resize_border_mask = 0x00;
6851 if (window->Flags & ImGuiWindowFlags_ChildWindow)
6852 resize_border_mask |= ((window->ChildFlags & ImGuiChildFlags_ResizeX) ? 0x02 : 0) | ((window->ChildFlags & ImGuiChildFlags_ResizeY) ? 0x08 : 0);
6853 else
6854 resize_border_mask = g.IO.ConfigWindowsResizeFromEdges ? 0x0F : 0x00;
6855 for (int border_n = 0; border_n < 4; border_n++)
6856 {
6857 if ((resize_border_mask & (1 << border_n)) == 0)
6858 continue;
6859 const ImGuiResizeBorderDef& def = resize_border_def[border_n];
6860 const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
6861
6862 bool hovered, held;
6863 ImRect border_rect = GetResizeBorderRect(window, border_n, perp_padding: grip_hover_inner_size, thickness: WINDOWS_HOVER_PADDING);
6864 ImGuiID border_id = window->GetID(n: border_n + 4); // == GetWindowResizeBorderID()
6865 ItemAdd(bb: border_rect, id: border_id, NULL, extra_flags: ImGuiItemFlags_NoNav);
6866 ButtonBehavior(bb: border_rect, id: border_id, out_hovered: &hovered, out_held: &held, flags: ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
6867 //GetForegroundDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
6868 if (hovered && g.HoveredIdTimer <= WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER)
6869 hovered = false;
6870 if (hovered || held)
6871 SetMouseCursor((axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS);
6872 if (held && g.IO.MouseDoubleClicked[0])
6873 {
6874 // Double-clicking bottom or right border auto-fit on this axis
6875 // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one side may be auto-fit when calculating scrollbars.
6876 // FIXME: Support top and right borders: rework CalcResizePosSizeFromAnyCorner() to be reusable in both cases.
6877 if (border_n == 1 || border_n == 3) // Right and bottom border
6878 {
6879 size_target[axis] = CalcWindowSizeAfterConstraint(window, size_desired: size_auto_fit)[axis];
6880 ret_auto_fit_mask |= (1 << axis);
6881 hovered = held = false; // So border doesn't show highlighted at new position
6882 }
6883 ClearActiveID();
6884 }
6885 else if (held)
6886 {
6887 // Switch to relative resizing mode when border geometry moved (e.g. resizing a child altering parent scroll), in order to avoid resizing feedback loop.
6888 // Currently only using relative mode on resizable child windows, as the problem to solve is more likely noticeable for them, but could apply for all windows eventually.
6889 // FIXME: May want to generalize this idiom at lower-level, so more widgets can use it!
6890 const bool just_scrolled_manually_while_resizing = (g.WheelingWindow != NULL && g.WheelingWindowScrolledFrame == g.FrameCount && IsWindowChildOf(window, potential_parent: g.WheelingWindow, popup_hierarchy: false, dock_hierarchy: true));
6891 if (g.ActiveIdIsJustActivated || just_scrolled_manually_while_resizing)
6892 {
6893 g.WindowResizeBorderExpectedRect = border_rect;
6894 g.WindowResizeRelativeMode = false;
6895 }
6896 if ((window->Flags & ImGuiWindowFlags_ChildWindow) && memcmp(s1: &g.WindowResizeBorderExpectedRect, s2: &border_rect, n: sizeof(ImRect)) != 0)
6897 g.WindowResizeRelativeMode = true;
6898
6899 const ImVec2 border_curr = (window->Pos + ImMin(lhs: def.SegmentN1, rhs: def.SegmentN2) * window->Size);
6900 const float border_target_rel_mode_for_axis = border_curr[axis] + g.IO.MouseDelta[axis];
6901 const float border_target_abs_mode_for_axis = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING; // Match ButtonBehavior() padding above.
6902
6903 // Use absolute mode position
6904 ImVec2 border_target = window->Pos;
6905 border_target[axis] = border_target_abs_mode_for_axis;
6906
6907 // Use relative mode target for child window, ignore resize when moving back toward the ideal absolute position.
6908 bool ignore_resize = false;
6909 if (g.WindowResizeRelativeMode)
6910 {
6911 //GetForegroundDrawList()->AddText(GetMainViewport()->WorkPos, IM_COL32_WHITE, "Relative Mode");
6912 border_target[axis] = border_target_rel_mode_for_axis;
6913 if (g.IO.MouseDelta[axis] == 0.0f || (g.IO.MouseDelta[axis] > 0.0f) == (border_target_rel_mode_for_axis > border_target_abs_mode_for_axis))
6914 ignore_resize = true;
6915 }
6916
6917 // Clamp, apply
6918 ImVec2 clamp_min(border_n == ImGuiDir_Right ? clamp_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down || (border_n == ImGuiDir_Up && window_move_from_title_bar) ? clamp_rect.Min.y : -FLT_MAX);
6919 ImVec2 clamp_max(border_n == ImGuiDir_Left ? clamp_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? clamp_rect.Max.y : +FLT_MAX);
6920 border_target = ImClamp(v: border_target, mn: clamp_min, mx: clamp_max);
6921 if (flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent
6922 {
6923 ImGuiWindow* parent_window = window->ParentWindow;
6924 ImGuiWindowFlags parent_flags = parent_window->Flags;
6925 ImRect border_limit_rect = parent_window->InnerRect;
6926 border_limit_rect.Expand(amount: ImVec2(-ImMax(lhs: parent_window->WindowPadding.x, rhs: parent_window->WindowBorderSize), -ImMax(lhs: parent_window->WindowPadding.y, rhs: parent_window->WindowBorderSize)));
6927 if ((axis == ImGuiAxis_X) && ((parent_flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (parent_flags & ImGuiWindowFlags_NoScrollbar)))
6928 border_target.x = ImClamp(v: border_target.x, mn: border_limit_rect.Min.x, mx: border_limit_rect.Max.x);
6929 if ((axis == ImGuiAxis_Y) && (parent_flags & ImGuiWindowFlags_NoScrollbar))
6930 border_target.y = ImClamp(v: border_target.y, mn: border_limit_rect.Min.y, mx: border_limit_rect.Max.y);
6931 }
6932 if (!ignore_resize)
6933 CalcResizePosSizeFromAnyCorner(window, corner_target: border_target, corner_norm: ImMin(lhs: def.SegmentN1, rhs: def.SegmentN2), out_pos: &pos_target, out_size: &size_target);
6934 }
6935 if (hovered)
6936 *border_hovered = border_n;
6937 if (held)
6938 *border_held = border_n;
6939 }
6940 PopID();
6941
6942 // Restore nav layer
6943 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
6944
6945 // Navigation resize (keyboard/gamepad)
6946 // FIXME: This cannot be moved to NavUpdateWindowing() because CalcWindowSizeAfterConstraint() need to callback into user.
6947 // Not even sure the callback works here.
6948 if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindowDockTree == window)
6949 {
6950 ImVec2 nav_resize_dir;
6951 if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift)
6952 nav_resize_dir = GetKeyMagnitude2d(key_left: ImGuiKey_LeftArrow, key_right: ImGuiKey_RightArrow, key_up: ImGuiKey_UpArrow, key_down: ImGuiKey_DownArrow);
6953 if (g.NavInputSource == ImGuiInputSource_Gamepad)
6954 nav_resize_dir = GetKeyMagnitude2d(key_left: ImGuiKey_GamepadDpadLeft, key_right: ImGuiKey_GamepadDpadRight, key_up: ImGuiKey_GamepadDpadUp, key_down: ImGuiKey_GamepadDpadDown);
6955 if (nav_resize_dir.x != 0.0f || nav_resize_dir.y != 0.0f)
6956 {
6957 const float NAV_RESIZE_SPEED = 600.0f;
6958 const float resize_step = NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(lhs: g.IO.DisplayFramebufferScale.x, rhs: g.IO.DisplayFramebufferScale.y);
6959 g.NavWindowingAccumDeltaSize += nav_resize_dir * resize_step;
6960 g.NavWindowingAccumDeltaSize = ImMax(lhs: g.NavWindowingAccumDeltaSize, rhs: clamp_rect.Min - window->Pos - window->Size); // We need Pos+Size >= clmap_rect.Min, so Size >= clmap_rect.Min - Pos, so size_delta >= clmap_rect.Min - window->Pos - window->Size
6961 g.NavWindowingToggleLayer = false;
6962 g.NavHighlightItemUnderNav = true;
6963 resize_grip_col[0] = GetColorU32(idx: ImGuiCol_ResizeGripActive);
6964 ImVec2 accum_floored = ImTrunc(v: g.NavWindowingAccumDeltaSize);
6965 if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
6966 {
6967 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
6968 size_target = CalcWindowSizeAfterConstraint(window, size_desired: window->SizeFull + accum_floored);
6969 g.NavWindowingAccumDeltaSize -= accum_floored;
6970 }
6971 }
6972 }
6973
6974 // Apply back modified position/size to window
6975 const ImVec2 curr_pos = window->Pos;
6976 const ImVec2 curr_size = window->SizeFull;
6977 if (size_target.x != FLT_MAX && (window->Size.x != size_target.x || window->SizeFull.x != size_target.x))
6978 window->Size.x = window->SizeFull.x = size_target.x;
6979 if (size_target.y != FLT_MAX && (window->Size.y != size_target.y || window->SizeFull.y != size_target.y))
6980 window->Size.y = window->SizeFull.y = size_target.y;
6981 if (pos_target.x != FLT_MAX && window->Pos.x != ImTrunc(f: pos_target.x))
6982 window->Pos.x = ImTrunc(f: pos_target.x);
6983 if (pos_target.y != FLT_MAX && window->Pos.y != ImTrunc(f: pos_target.y))
6984 window->Pos.y = ImTrunc(f: pos_target.y);
6985 if (curr_pos.x != window->Pos.x || curr_pos.y != window->Pos.y || curr_size.x != window->SizeFull.x || curr_size.y != window->SizeFull.y)
6986 MarkIniSettingsDirty(window);
6987
6988 // Recalculate next expected border expected coordinates
6989 if (*border_held != -1)
6990 g.WindowResizeBorderExpectedRect = GetResizeBorderRect(window, border_n: *border_held, perp_padding: grip_hover_inner_size, thickness: WINDOWS_HOVER_PADDING);
6991
6992 return ret_auto_fit_mask;
6993}
6994
6995static inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_rect)
6996{
6997 ImGuiContext& g = *GImGui;
6998 ImVec2 size_for_clamping = window->Size;
6999 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && window->DockNodeAsHost)
7000 size_for_clamping.y = ImGui::GetFrameHeight(); // Not using window->TitleBarHeight() as DockNodeAsHost will report 0.0f here.
7001 else if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
7002 size_for_clamping.y = window->TitleBarHeight;
7003 window->Pos = ImClamp(v: window->Pos, mn: visibility_rect.Min - size_for_clamping, mx: visibility_rect.Max);
7004}
7005
7006static void RenderWindowOuterSingleBorder(ImGuiWindow* window, int border_n, ImU32 border_col, float border_size)
7007{
7008 const ImGuiResizeBorderDef& def = resize_border_def[border_n];
7009 const float rounding = window->WindowRounding;
7010 const ImRect border_r = GetResizeBorderRect(window, border_n, perp_padding: rounding, thickness: 0.0f);
7011 window->DrawList->PathArcTo(center: ImLerp(a: border_r.Min, b: border_r.Max, t: def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, radius: rounding, a_min: def.OuterAngle - IM_PI * 0.25f, a_max: def.OuterAngle);
7012 window->DrawList->PathArcTo(center: ImLerp(a: border_r.Min, b: border_r.Max, t: def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, radius: rounding, a_min: def.OuterAngle, a_max: def.OuterAngle + IM_PI * 0.25f);
7013 window->DrawList->PathStroke(col: border_col, flags: ImDrawFlags_None, thickness: border_size);
7014}
7015
7016static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
7017{
7018 ImGuiContext& g = *GImGui;
7019 const float border_size = window->WindowBorderSize;
7020 const ImU32 border_col = GetColorU32(idx: ImGuiCol_Border);
7021 if (border_size > 0.0f && (window->Flags & ImGuiWindowFlags_NoBackground) == 0)
7022 window->DrawList->AddRect(p_min: window->Pos, p_max: window->Pos + window->Size, col: border_col, rounding: window->WindowRounding, flags: 0, thickness: window->WindowBorderSize);
7023 else if (border_size > 0.0f)
7024 {
7025 if (window->ChildFlags & ImGuiChildFlags_ResizeX) // Similar code as 'resize_border_mask' computation in UpdateWindowManualResize() but we specifically only always draw explicit child resize border.
7026 RenderWindowOuterSingleBorder(window, border_n: 1, border_col, border_size);
7027 if (window->ChildFlags & ImGuiChildFlags_ResizeY)
7028 RenderWindowOuterSingleBorder(window, border_n: 3, border_col, border_size);
7029 }
7030 if (window->ResizeBorderHovered != -1 || window->ResizeBorderHeld != -1)
7031 {
7032 const int border_n = (window->ResizeBorderHeld != -1) ? window->ResizeBorderHeld : window->ResizeBorderHovered;
7033 const ImU32 border_col_resizing = GetColorU32(idx: (window->ResizeBorderHeld != -1) ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered);
7034 RenderWindowOuterSingleBorder(window, border_n, border_col: border_col_resizing, border_size: ImMax(lhs: 2.0f, rhs: window->WindowBorderSize)); // Thicker than usual
7035 }
7036 if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
7037 {
7038 float y = window->Pos.y + window->TitleBarHeight - 1;
7039 window->DrawList->AddLine(p1: ImVec2(window->Pos.x + border_size, y), p2: ImVec2(window->Pos.x + window->Size.x - border_size, y), col: border_col, thickness: g.Style.FrameBorderSize);
7040 }
7041}
7042
7043// Draw background and borders
7044// Draw and handle scrollbars
7045void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size)
7046{
7047 ImGuiContext& g = *GImGui;
7048 ImGuiStyle& style = g.Style;
7049 ImGuiWindowFlags flags = window->Flags;
7050
7051 // Ensure that ScrollBar doesn't read last frame's SkipItems
7052 IM_ASSERT(window->BeginCount == 0);
7053 window->SkipItems = false;
7054
7055 // Draw window + handle manual resize
7056 // As we highlight the title bar when want_focus is set, multiple reappearing windows will have their title bar highlighted on their reappearing frame.
7057 const float window_rounding = window->WindowRounding;
7058 const float window_border_size = window->WindowBorderSize;
7059 if (window->Collapsed)
7060 {
7061 // Title bar only
7062 const float backup_border_size = style.FrameBorderSize;
7063 g.Style.FrameBorderSize = window->WindowBorderSize;
7064 ImU32 title_bar_col = GetColorU32(idx: (title_bar_is_highlight && g.NavCursorVisible) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
7065 if (window->ViewportOwned)
7066 title_bar_col |= IM_COL32_A_MASK; // No alpha (we don't support is_docking_transparent_payload here because simpler and less meaningful, but could with a bit of code shuffle/reuse)
7067 RenderFrame(p_min: title_bar_rect.Min, p_max: title_bar_rect.Max, fill_col: title_bar_col, borders: true, rounding: window_rounding);
7068 g.Style.FrameBorderSize = backup_border_size;
7069 }
7070 else
7071 {
7072 // Window background
7073 if (!(flags & ImGuiWindowFlags_NoBackground))
7074 {
7075 bool is_docking_transparent_payload = false;
7076 if (g.DragDropActive && (g.FrameCount - g.DragDropAcceptFrameCount) <= 1 && g.IO.ConfigDockingTransparentPayload)
7077 if (g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && *(ImGuiWindow**)g.DragDropPayload.Data == window)
7078 is_docking_transparent_payload = true;
7079
7080 ImU32 bg_col = GetColorU32(idx: GetWindowBgColorIdx(window));
7081 if (window->ViewportOwned)
7082 {
7083 bg_col |= IM_COL32_A_MASK; // No alpha
7084 if (is_docking_transparent_payload)
7085 window->Viewport->Alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA;
7086 }
7087 else
7088 {
7089 // Adjust alpha. For docking
7090 bool override_alpha = false;
7091 float alpha = 1.0f;
7092 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
7093 {
7094 alpha = g.NextWindowData.BgAlphaVal;
7095 override_alpha = true;
7096 }
7097 if (is_docking_transparent_payload)
7098 {
7099 alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA; // FIXME-DOCK: Should that be an override?
7100 override_alpha = true;
7101 }
7102 if (override_alpha)
7103 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
7104 }
7105
7106 // Render, for docked windows and host windows we ensure bg goes before decorations
7107 if (window->DockIsActive)
7108 window->DockNode->LastBgColor = bg_col;
7109 ImDrawList* bg_draw_list = window->DockIsActive ? window->DockNode->HostWindow->DrawList : window->DrawList;
7110 if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost))
7111 bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG);
7112 bg_draw_list->AddRectFilled(p_min: window->Pos + ImVec2(0, window->TitleBarHeight), p_max: window->Pos + window->Size, col: bg_col, rounding: window_rounding, flags: (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom);
7113 if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost))
7114 bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG);
7115 }
7116 if (window->DockIsActive)
7117 window->DockNode->IsBgDrawnThisFrame = true;
7118
7119 // Title bar
7120 // (when docked, DockNode are drawing their own title bar. Individual windows however do NOT set the _NoTitleBar flag,
7121 // in order for their pos/size to be matching their undocking state.)
7122 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
7123 {
7124 ImU32 title_bar_col = GetColorU32(idx: title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
7125 if (window->ViewportOwned)
7126 title_bar_col |= IM_COL32_A_MASK; // No alpha
7127 window->DrawList->AddRectFilled(p_min: title_bar_rect.Min, p_max: title_bar_rect.Max, col: title_bar_col, rounding: window_rounding, flags: ImDrawFlags_RoundCornersTop);
7128 }
7129
7130 // Menu bar
7131 if (flags & ImGuiWindowFlags_MenuBar)
7132 {
7133 ImRect menu_bar_rect = window->MenuBarRect();
7134 menu_bar_rect.ClipWith(r: window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them.
7135 window->DrawList->AddRectFilled(p_min: menu_bar_rect.Min + ImVec2(window_border_size, 0), p_max: menu_bar_rect.Max - ImVec2(window_border_size, 0), col: GetColorU32(idx: ImGuiCol_MenuBarBg), rounding: (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, flags: ImDrawFlags_RoundCornersTop);
7136 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
7137 window->DrawList->AddLine(p1: menu_bar_rect.GetBL(), p2: menu_bar_rect.GetBR(), col: GetColorU32(idx: ImGuiCol_Border), thickness: style.FrameBorderSize);
7138 }
7139
7140 // Docking: Unhide tab bar (small triangle in the corner), drag from small triangle to quickly undock
7141 ImGuiDockNode* node = window->DockNode;
7142 if (window->DockIsActive && node->IsHiddenTabBar() && !node->IsNoTabBar())
7143 {
7144 float unhide_sz_draw = ImTrunc(f: g.FontSize * 0.70f);
7145 float unhide_sz_hit = ImTrunc(f: g.FontSize * 0.55f);
7146 ImVec2 p = node->Pos;
7147 ImRect r(p, p + ImVec2(unhide_sz_hit, unhide_sz_hit));
7148 ImGuiID unhide_id = window->GetID(str: "#UNHIDE");
7149 KeepAliveID(id: unhide_id);
7150 bool hovered, held;
7151 if (ButtonBehavior(bb: r, id: unhide_id, out_hovered: &hovered, out_held: &held, flags: ImGuiButtonFlags_FlattenChildren))
7152 node->WantHiddenTabBarToggle = true;
7153 else if (held && IsMouseDragging(button: 0))
7154 StartMouseMovingWindowOrNode(window, node, undock: true); // Undock from tab-bar triangle = same as window/collapse menu button
7155
7156 // FIXME-DOCK: Ideally we'd use ImGuiCol_TitleBgActive/ImGuiCol_TitleBg here, but neither is guaranteed to be visible enough at this sort of size..
7157 ImU32 col = GetColorU32(idx: ((held && hovered) || (node->IsFocused && !hovered)) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
7158 window->DrawList->AddTriangleFilled(p1: p, p2: p + ImVec2(unhide_sz_draw, 0.0f), p3: p + ImVec2(0.0f, unhide_sz_draw), col);
7159 }
7160
7161 // Scrollbars
7162 if (window->ScrollbarX)
7163 Scrollbar(axis: ImGuiAxis_X);
7164 if (window->ScrollbarY)
7165 Scrollbar(axis: ImGuiAxis_Y);
7166
7167 // Render resize grips (after their input handling so we don't have a frame of latency)
7168 if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize))
7169 {
7170 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
7171 {
7172 const ImU32 col = resize_grip_col[resize_grip_n];
7173 if ((col & IM_COL32_A_MASK) == 0)
7174 continue;
7175 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
7176 const ImVec2 corner = ImLerp(a: window->Pos, b: window->Pos + window->Size, t: grip.CornerPosN);
7177 window->DrawList->PathLineTo(pos: corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size)));
7178 window->DrawList->PathLineTo(pos: corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size)));
7179 window->DrawList->PathArcToFast(center: ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), radius: window_rounding, a_min_of_12: grip.AngleMin12, a_max_of_12: grip.AngleMax12);
7180 window->DrawList->PathFillConvex(col);
7181 }
7182 }
7183
7184 // Borders (for dock node host they will be rendered over after the tab bar)
7185 if (handle_borders_and_resize_grips && !window->DockNodeAsHost)
7186 RenderWindowOuterBorders(window);
7187 }
7188}
7189
7190// When inside a dock node, this is handled in DockNodeCalcTabBarLayout() instead.
7191// Render title text, collapse button, close button
7192void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
7193{
7194 ImGuiContext& g = *GImGui;
7195 ImGuiStyle& style = g.Style;
7196 ImGuiWindowFlags flags = window->Flags;
7197
7198 const bool has_close_button = (p_open != NULL);
7199 const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
7200
7201 // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
7202 // FIXME-NAV: Might want (or not?) to set the equivalent of ImGuiButtonFlags_NoNavFocus so that mouse clicks on standard title bar items don't necessarily set nav/keyboard ref?
7203 const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags;
7204 g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
7205 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
7206
7207 // Layout buttons
7208 // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
7209 float pad_l = style.FramePadding.x;
7210 float pad_r = style.FramePadding.x;
7211 float button_sz = g.FontSize;
7212 ImVec2 close_button_pos;
7213 ImVec2 collapse_button_pos;
7214 if (has_close_button)
7215 {
7216 close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
7217 pad_r += button_sz + style.ItemInnerSpacing.x;
7218 }
7219 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
7220 {
7221 collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
7222 pad_r += button_sz + style.ItemInnerSpacing.x;
7223 }
7224 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
7225 {
7226 collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y + style.FramePadding.y);
7227 pad_l += button_sz + style.ItemInnerSpacing.x;
7228 }
7229
7230 // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
7231 if (has_collapse_button)
7232 if (CollapseButton(id: window->GetID(str: "#COLLAPSE"), pos: collapse_button_pos, NULL))
7233 window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
7234
7235 // Close button
7236 if (has_close_button)
7237 if (CloseButton(id: window->GetID(str: "#CLOSE"), pos: close_button_pos))
7238 *p_open = false;
7239
7240 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7241 g.CurrentItemFlags = item_flags_backup;
7242
7243 // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
7244 // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
7245 const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f;
7246 const ImVec2 text_size = CalcTextSize(text: name, NULL, hide_text_after_double_hash: true) + ImVec2(marker_size_x, 0.0f);
7247
7248 // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
7249 // while uncentered title text will still reach edges correctly.
7250 if (pad_l > style.FramePadding.x)
7251 pad_l += g.Style.ItemInnerSpacing.x;
7252 if (pad_r > style.FramePadding.x)
7253 pad_r += g.Style.ItemInnerSpacing.x;
7254 if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
7255 {
7256 float centerness = ImSaturate(f: 1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
7257 float pad_extend = ImMin(lhs: ImMax(lhs: pad_l, rhs: pad_r), rhs: title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
7258 pad_l = ImMax(lhs: pad_l, rhs: pad_extend * centerness);
7259 pad_r = ImMax(lhs: pad_r, rhs: pad_extend * centerness);
7260 }
7261
7262 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);
7263 ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(lhs: layout_r.Max.x + g.Style.ItemInnerSpacing.x, rhs: title_bar_rect.Max.x), layout_r.Max.y);
7264 if (flags & ImGuiWindowFlags_UnsavedDocument)
7265 {
7266 ImVec2 marker_pos;
7267 marker_pos.x = ImClamp(v: layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x + text_size.x, mn: layout_r.Min.x, mx: layout_r.Max.x);
7268 marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f;
7269 if (marker_pos.x > layout_r.Min.x)
7270 {
7271 RenderBullet(draw_list: window->DrawList, pos: marker_pos, col: GetColorU32(idx: ImGuiCol_Text));
7272 clip_r.Max.x = ImMin(lhs: clip_r.Max.x, rhs: marker_pos.x - (int)(marker_size_x * 0.5f));
7273 }
7274 }
7275 //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
7276 //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
7277 RenderTextClipped(pos_min: layout_r.Min, pos_max: layout_r.Max, text: name, NULL, text_size_if_known: &text_size, align: style.WindowTitleAlign, clip_rect: &clip_r);
7278}
7279
7280void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
7281{
7282 window->ParentWindow = parent_window;
7283 window->RootWindow = window->RootWindowPopupTree = window->RootWindowDockTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
7284 if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
7285 {
7286 window->RootWindowDockTree = parent_window->RootWindowDockTree;
7287 if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost))
7288 window->RootWindow = parent_window->RootWindow;
7289 }
7290 if (parent_window && (flags & ImGuiWindowFlags_Popup))
7291 window->RootWindowPopupTree = parent_window->RootWindowPopupTree;
7292 if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) // FIXME: simply use _NoTitleBar ?
7293 window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
7294 while (window->RootWindowForNav->ChildFlags & ImGuiChildFlags_NavFlattened)
7295 {
7296 IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
7297 window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
7298 }
7299}
7300
7301// [EXPERIMENTAL] Called by Begin(). NextWindowData is valid at this point.
7302// This is designed as a toy/test-bed for
7303void ImGui::UpdateWindowSkipRefresh(ImGuiWindow* window)
7304{
7305 ImGuiContext& g = *GImGui;
7306 window->SkipRefresh = false;
7307 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasRefreshPolicy) == 0)
7308 return;
7309 if (g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_TryToAvoidRefresh)
7310 {
7311 // FIXME-IDLE: Tests for e.g. mouse clicks or keyboard while focused.
7312 if (window->Appearing) // If currently appearing
7313 return;
7314 if (window->Hidden) // If was hidden (previous frame)
7315 return;
7316 if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnHover) && g.HoveredWindow)
7317 if (window->RootWindow == g.HoveredWindow->RootWindow || IsWindowWithinBeginStackOf(window: g.HoveredWindow->RootWindow, potential_parent: window))
7318 return;
7319 if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnFocus) && g.NavWindow)
7320 if (window->RootWindow == g.NavWindow->RootWindow || IsWindowWithinBeginStackOf(window: g.NavWindow->RootWindow, potential_parent: window))
7321 return;
7322 window->DrawList = NULL;
7323 window->SkipRefresh = true;
7324 }
7325}
7326
7327static void SetWindowActiveForSkipRefresh(ImGuiWindow* window)
7328{
7329 window->Active = true;
7330 for (ImGuiWindow* child : window->DC.ChildWindows)
7331 if (!child->Hidden)
7332 {
7333 child->Active = child->SkipRefresh = true;
7334 SetWindowActiveForSkipRefresh(child);
7335 }
7336}
7337
7338// When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing)
7339// should be positioned behind that modal window, unless the window was created inside the modal begin-stack.
7340// In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent.
7341// - WindowA // FindBlockingModal() returns Modal1
7342// - WindowB // .. returns Modal1
7343// - Modal1 // .. returns Modal2
7344// - WindowC // .. returns Modal2
7345// - WindowD // .. returns Modal2
7346// - Modal2 // .. returns Modal2
7347// - WindowE // .. returns NULL
7348// Notes:
7349// - FindBlockingModal(NULL) == NULL is generally equivalent to GetTopMostPopupModal() == NULL.
7350// Only difference is here we check for ->Active/WasActive but it may be unnecessary.
7351ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window)
7352{
7353 ImGuiContext& g = *GImGui;
7354 if (g.OpenPopupStack.Size <= 0)
7355 return NULL;
7356
7357 // Find a modal that has common parent with specified window. Specified window should be positioned behind that modal.
7358 for (ImGuiPopupData& popup_data : g.OpenPopupStack)
7359 {
7360 ImGuiWindow* popup_window = popup_data.Window;
7361 if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal))
7362 continue;
7363 if (!popup_window->Active && !popup_window->WasActive) // Check WasActive, because this code may run before popup renders on current frame, also check Active to handle newly created windows.
7364 continue;
7365 if (window == NULL) // FindBlockingModal(NULL) test for if FocusWindow(NULL) is naturally possible via a mouse click.
7366 return popup_window;
7367 if (IsWindowWithinBeginStackOf(window, potential_parent: popup_window)) // Window may be over modal
7368 continue;
7369 return popup_window; // Place window right below first block modal
7370 }
7371 return NULL;
7372}
7373
7374// Push a new Dear ImGui window to add widgets to.
7375// - 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.
7376// - Begin/End can be called multiple times during the frame with the same window name to append content.
7377// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
7378// 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.
7379// - 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.
7380// - 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.
7381bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
7382{
7383 ImGuiContext& g = *GImGui;
7384 const ImGuiStyle& style = g.Style;
7385 IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required
7386 IM_ASSERT(g.WithinFrameScope); // Forgot to call ImGui::NewFrame()
7387 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
7388
7389 // Find or create
7390 ImGuiWindow* window = FindWindowByName(name);
7391 const bool window_just_created = (window == NULL);
7392 if (window_just_created)
7393 window = CreateNewWindow(name, flags);
7394
7395 // [DEBUG] Debug break requested by user
7396 if (g.DebugBreakInWindow == window->ID)
7397 IM_DEBUG_BREAK();
7398
7399 // Automatically disable manual moving/resizing when NoInputs is set
7400 if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
7401 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
7402
7403 const int current_frame = g.FrameCount;
7404 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
7405 window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
7406
7407 // Update the Appearing flag (note: the BeginDocked() path may also set this to true later)
7408 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
7409 if (flags & ImGuiWindowFlags_Popup)
7410 {
7411 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
7412 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
7413 window_just_activated_by_user |= (window != popup_ref.Window);
7414 }
7415
7416 // Update Flags, LastFrameActive, BeginOrderXXX fields
7417 const bool window_was_appearing = window->Appearing;
7418 if (first_begin_of_the_frame)
7419 {
7420 UpdateWindowInFocusOrderList(window, just_created: window_just_created, new_flags: flags);
7421 window->Appearing = window_just_activated_by_user;
7422 if (window->Appearing)
7423 SetWindowConditionAllowFlags(window, flags: ImGuiCond_Appearing, enabled: true);
7424 window->FlagsPreviousFrame = window->Flags;
7425 window->Flags = (ImGuiWindowFlags)flags;
7426 window->ChildFlags = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : 0;
7427 window->LastFrameActive = current_frame;
7428 window->LastTimeActive = (float)g.Time;
7429 window->BeginOrderWithinParent = 0;
7430 window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
7431 }
7432 else
7433 {
7434 flags = window->Flags;
7435 }
7436
7437 // Docking
7438 // (NB: during the frame dock nodes are created, it is possible that (window->DockIsActive == false) even though (window->DockNode->Windows.Size > 1)
7439 IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); // Cannot be both
7440 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasDock)
7441 SetWindowDock(window, dock_id: g.NextWindowData.DockId, cond: g.NextWindowData.DockCond);
7442 if (first_begin_of_the_frame)
7443 {
7444 bool has_dock_node = (window->DockId != 0 || window->DockNode != NULL);
7445 bool new_auto_dock_node = !has_dock_node && GetWindowAlwaysWantOwnTabBar(window);
7446 bool dock_node_was_visible = window->DockNodeIsVisible;
7447 bool dock_tab_was_visible = window->DockTabIsVisible;
7448 if (has_dock_node || new_auto_dock_node)
7449 {
7450 BeginDocked(window, p_open);
7451 flags = window->Flags;
7452 if (window->DockIsActive)
7453 {
7454 IM_ASSERT(window->DockNode != NULL);
7455 g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; // Docking currently override constraints
7456 }
7457
7458 // Amend the Appearing flag
7459 if (window->DockTabIsVisible && !dock_tab_was_visible && dock_node_was_visible && !window->Appearing && !window_was_appearing)
7460 {
7461 window->Appearing = true;
7462 SetWindowConditionAllowFlags(window, flags: ImGuiCond_Appearing, enabled: true);
7463 }
7464 }
7465 else
7466 {
7467 window->DockIsActive = window->DockNodeIsVisible = window->DockTabIsVisible = false;
7468 }
7469 }
7470
7471 // 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
7472 ImGuiWindow* parent_window_in_stack = (window->DockIsActive && window->DockNode->HostWindow) ? window->DockNode->HostWindow : g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window;
7473 ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
7474 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
7475
7476 // We allow window memory to be compacted so recreate the base stack when needed.
7477 if (window->IDStack.Size == 0)
7478 window->IDStack.push_back(v: window->ID);
7479
7480 // Add to stack
7481 g.CurrentWindow = window;
7482 g.CurrentWindowStack.resize(new_size: g.CurrentWindowStack.Size + 1);
7483 ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();
7484 window_stack_data.Window = window;
7485 window_stack_data.ParentLastItemDataBackup = g.LastItemData;
7486 window_stack_data.DisabledOverrideReenable = (flags & ImGuiWindowFlags_Tooltip) && (g.CurrentItemFlags & ImGuiItemFlags_Disabled);
7487 ErrorRecoveryStoreState(state_out: &window_stack_data.StackSizesInBegin);
7488 g.StackSizesInBeginForCurrentWindow = &window_stack_data.StackSizesInBegin;
7489 if (flags & ImGuiWindowFlags_ChildMenu)
7490 g.BeginMenuDepth++;
7491
7492 // Update ->RootWindow and others pointers (before any possible call to FocusWindow)
7493 if (first_begin_of_the_frame)
7494 {
7495 UpdateWindowParentAndRootLinks(window, flags, parent_window);
7496 window->ParentWindowInBeginStack = parent_window_in_stack;
7497
7498 // Focus route
7499 // There's little point to expose a flag to set this: because the interesting cases won't be using parent_window_in_stack,
7500 // Use for e.g. linking a tool window in a standalone viewport to a document window, regardless of their Begin() stack parenting. (#6798)
7501 window->ParentWindowForFocusRoute = (window->RootWindow != window) ? parent_window_in_stack : NULL;
7502 if (window->ParentWindowForFocusRoute == NULL && window->DockNode != NULL)
7503 if (window->DockNode->MergedFlags & ImGuiDockNodeFlags_DockedWindowsInFocusRoute)
7504 window->ParentWindowForFocusRoute = window->DockNode->HostWindow;
7505
7506 // Override with SetNextWindowClass() field or direct call to SetWindowParentWindowForFocusRoute()
7507 if (window->WindowClass.FocusRouteParentWindowId != 0)
7508 {
7509 window->ParentWindowForFocusRoute = FindWindowByID(id: window->WindowClass.FocusRouteParentWindowId);
7510 IM_ASSERT(window->ParentWindowForFocusRoute != 0); // Invalid value for FocusRouteParentWindowId.
7511 }
7512 }
7513
7514 // Add to focus scope stack
7515 PushFocusScope(id: (window->ChildFlags & ImGuiChildFlags_NavFlattened) ? g.CurrentFocusScopeId : window->ID);
7516 window->NavRootFocusScopeId = g.CurrentFocusScopeId;
7517
7518 // Add to popup stacks: update OpenPopupStack[] data, push to BeginPopupStack[]
7519 if (flags & ImGuiWindowFlags_Popup)
7520 {
7521 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
7522 popup_ref.Window = window;
7523 popup_ref.ParentNavLayer = parent_window_in_stack->DC.NavLayerCurrent;
7524 g.BeginPopupStack.push_back(v: popup_ref);
7525 window->PopupId = popup_ref.PopupId;
7526 }
7527
7528 // Process SetNextWindow***() calls
7529 // (FIXME: Consider splitting the HasXXX flags into X/Y components
7530 bool window_pos_set_by_api = false;
7531 bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
7532 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
7533 {
7534 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
7535 if (window_pos_set_by_api && ImLengthSqr(lhs: g.NextWindowData.PosPivotVal) > 0.00001f)
7536 {
7537 // May be processed on the next frame if this is our first frame and we are measuring size
7538 // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
7539 window->SetWindowPosVal = g.NextWindowData.PosVal;
7540 window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
7541 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7542 }
7543 else
7544 {
7545 SetWindowPos(window, pos: g.NextWindowData.PosVal, cond: g.NextWindowData.PosCond);
7546 }
7547 }
7548 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
7549 {
7550 window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
7551 window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
7552 if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0) // Axis-specific conditions for BeginChild()
7553 g.NextWindowData.SizeVal.x = window->SizeFull.x;
7554 if ((window->ChildFlags & ImGuiChildFlags_ResizeY) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0)
7555 g.NextWindowData.SizeVal.y = window->SizeFull.y;
7556 SetWindowSize(window, size: g.NextWindowData.SizeVal, cond: g.NextWindowData.SizeCond);
7557 }
7558 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll)
7559 {
7560 if (g.NextWindowData.ScrollVal.x >= 0.0f)
7561 {
7562 window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
7563 window->ScrollTargetCenterRatio.x = 0.0f;
7564 }
7565 if (g.NextWindowData.ScrollVal.y >= 0.0f)
7566 {
7567 window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
7568 window->ScrollTargetCenterRatio.y = 0.0f;
7569 }
7570 }
7571 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
7572 window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
7573 else if (first_begin_of_the_frame)
7574 window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
7575 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasWindowClass)
7576 window->WindowClass = g.NextWindowData.WindowClass;
7577 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
7578 SetWindowCollapsed(window, collapsed: g.NextWindowData.CollapsedVal, cond: g.NextWindowData.CollapsedCond);
7579 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
7580 FocusWindow(window);
7581 if (window->Appearing)
7582 SetWindowConditionAllowFlags(window, flags: ImGuiCond_Appearing, enabled: false);
7583
7584 // [EXPERIMENTAL] Skip Refresh mode
7585 UpdateWindowSkipRefresh(window);
7586
7587 // Nested root windows (typically tooltips) override disabled state
7588 if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window)
7589 BeginDisabledOverrideReenable();
7590
7591 // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
7592 g.CurrentWindow = NULL;
7593
7594 // When reusing window again multiple times a frame, just append content (don't need to setup again)
7595 if (first_begin_of_the_frame && !window->SkipRefresh)
7596 {
7597 // Initialize
7598 const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
7599 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
7600 window->Active = true;
7601 window->HasCloseButton = (p_open != NULL);
7602 window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
7603 window->IDStack.resize(new_size: 1);
7604 window->DrawList->_ResetForNewFrame();
7605 window->DC.CurrentTableIdx = -1;
7606 if (flags & ImGuiWindowFlags_DockNodeHost)
7607 {
7608 window->DrawList->ChannelsSplit(count: 2);
7609 window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); // Render decorations on channel 1 as we will render the backgrounds manually later
7610 }
7611
7612 // Restore buffer capacity when woken from a compacted state, to avoid
7613 if (window->MemoryCompacted)
7614 GcAwakeTransientWindowBuffers(window);
7615
7616 // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
7617 // 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.
7618 bool window_title_visible_elsewhere = false;
7619 if ((window->Viewport && window->Viewport->Window == window) || (window->DockIsActive))
7620 window_title_visible_elsewhere = true;
7621 else if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
7622 window_title_visible_elsewhere = true;
7623 if (window_title_visible_elsewhere && !window_just_created && strcmp(s1: name, s2: window->Name) != 0)
7624 {
7625 size_t buf_len = (size_t)window->NameBufLen;
7626 window->Name = ImStrdupcpy(dst: window->Name, p_dst_size: &buf_len, src: name);
7627 window->NameBufLen = (int)buf_len;
7628 }
7629
7630 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
7631
7632 // Update contents size from last frame for auto-fitting (or use explicit size)
7633 CalcWindowContentSizes(window, content_size_current: &window->ContentSize, content_size_ideal: &window->ContentSizeIdeal);
7634
7635 // FIXME: These flags are decremented before they are used. This means that in order to have these fields produce their intended behaviors
7636 // for one frame we must set them to at least 2, which is counter-intuitive. HiddenFramesCannotSkipItems is a more complicated case because
7637 // it has a single usage before this code block and may be set below before it is finally checked.
7638 if (window->HiddenFramesCanSkipItems > 0)
7639 window->HiddenFramesCanSkipItems--;
7640 if (window->HiddenFramesCannotSkipItems > 0)
7641 window->HiddenFramesCannotSkipItems--;
7642 if (window->HiddenFramesForRenderOnly > 0)
7643 window->HiddenFramesForRenderOnly--;
7644
7645 // Hide new windows for one frame until they calculate their size
7646 if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
7647 window->HiddenFramesCannotSkipItems = 1;
7648
7649 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
7650 // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
7651 if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
7652 {
7653 window->HiddenFramesCannotSkipItems = 1;
7654 if (flags & ImGuiWindowFlags_AlwaysAutoResize)
7655 {
7656 if (!window_size_x_set_by_api)
7657 window->Size.x = window->SizeFull.x = 0.f;
7658 if (!window_size_y_set_by_api)
7659 window->Size.y = window->SizeFull.y = 0.f;
7660 window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f);
7661 }
7662 }
7663
7664 // SELECT VIEWPORT
7665 // We need to do this before using any style/font sizes, as viewport with a different DPI may affect font sizes.
7666
7667 WindowSelectViewport(window);
7668 SetCurrentViewport(window, viewport: window->Viewport);
7669 window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f;
7670 SetCurrentWindow(window);
7671 flags = window->Flags;
7672
7673 // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
7674 // We read Style data after the call to UpdateSelectWindowViewport() which might be swapping the style.
7675
7676 if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow))
7677 window->WindowBorderSize = style.ChildBorderSize;
7678 else
7679 window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
7680 window->WindowPadding = style.WindowPadding;
7681 if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !(window->ChildFlags & ImGuiChildFlags_AlwaysUseWindowPadding) && window->WindowBorderSize == 0.0f)
7682 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
7683
7684 // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
7685 window->DC.MenuBarOffset.x = ImMax(lhs: ImMax(lhs: window->WindowPadding.x, rhs: style.ItemSpacing.x), rhs: g.NextWindowData.MenuBarOffsetMinVal.x);
7686 window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
7687 window->TitleBarHeight = (flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : g.FontSize + g.Style.FramePadding.y * 2.0f;
7688 window->MenuBarHeight = (flags & ImGuiWindowFlags_MenuBar) ? window->DC.MenuBarOffset.y + g.FontSize + g.Style.FramePadding.y * 2.0f : 0.0f;
7689
7690 // Depending on condition we use previous or current window size to compare against contents size to decide if a scrollbar should be visible.
7691 // Those flags will be altered further down in the function depending on more conditions.
7692 bool use_current_size_for_scrollbar_x = window_just_created;
7693 bool use_current_size_for_scrollbar_y = window_just_created;
7694 if (window_size_x_set_by_api && window->ContentSizeExplicit.x != 0.0f)
7695 use_current_size_for_scrollbar_x = true;
7696 if (window_size_y_set_by_api && window->ContentSizeExplicit.y != 0.0f) // #7252
7697 use_current_size_for_scrollbar_y = true;
7698
7699 // Collapse window by double-clicking on title bar
7700 // 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
7701 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse) && !window->DockIsActive)
7702 {
7703 // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed),
7704 // so verify that we don't have items over the title bar.
7705 ImRect title_bar_rect = window->TitleBarRect();
7706 if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && g.ActiveId == 0 && IsMouseHoveringRect(r_min: title_bar_rect.Min, r_max: title_bar_rect.Max))
7707 if (g.IO.MouseClickedCount[0] == 2 && GetKeyOwner(key: ImGuiKey_MouseLeft) == ImGuiKeyOwner_NoOwner)
7708 window->WantCollapseToggle = true;
7709 if (window->WantCollapseToggle)
7710 {
7711 window->Collapsed = !window->Collapsed;
7712 if (!window->Collapsed)
7713 use_current_size_for_scrollbar_y = true;
7714 MarkIniSettingsDirty(window);
7715 }
7716 }
7717 else
7718 {
7719 window->Collapsed = false;
7720 }
7721 window->WantCollapseToggle = false;
7722
7723 // SIZE
7724
7725 // Outer Decoration Sizes
7726 // (we need to clear ScrollbarSize immediately as CalcWindowAutoFitSize() needs it and can be called from other locations).
7727 const ImVec2 scrollbar_sizes_from_last_frame = window->ScrollbarSizes;
7728 window->DecoOuterSizeX1 = 0.0f;
7729 window->DecoOuterSizeX2 = 0.0f;
7730 window->DecoOuterSizeY1 = window->TitleBarHeight + window->MenuBarHeight;
7731 window->DecoOuterSizeY2 = 0.0f;
7732 window->ScrollbarSizes = ImVec2(0.0f, 0.0f);
7733
7734 // Calculate auto-fit size, handle automatic resize
7735 const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents: window->ContentSizeIdeal);
7736 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
7737 {
7738 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
7739 if (!window_size_x_set_by_api)
7740 {
7741 window->SizeFull.x = size_auto_fit.x;
7742 use_current_size_for_scrollbar_x = true;
7743 }
7744 if (!window_size_y_set_by_api)
7745 {
7746 window->SizeFull.y = size_auto_fit.y;
7747 use_current_size_for_scrollbar_y = true;
7748 }
7749 }
7750 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
7751 {
7752 // Auto-fit may only grow window during the first few frames
7753 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
7754 if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
7755 {
7756 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(lhs: window->SizeFull.x, rhs: size_auto_fit.x) : size_auto_fit.x;
7757 use_current_size_for_scrollbar_x = true;
7758 }
7759 if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
7760 {
7761 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(lhs: window->SizeFull.y, rhs: size_auto_fit.y) : size_auto_fit.y;
7762 use_current_size_for_scrollbar_y = true;
7763 }
7764 if (!window->Collapsed)
7765 MarkIniSettingsDirty(window);
7766 }
7767
7768 // Apply minimum/maximum window size constraints and final size
7769 window->SizeFull = CalcWindowSizeAfterConstraint(window, size_desired: window->SizeFull);
7770 window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
7771
7772 // POSITION
7773
7774 // Popup latch its initial position, will position itself when it appears next frame
7775 if (window_just_activated_by_user)
7776 {
7777 window->AutoPosLastDirection = ImGuiDir_None;
7778 if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
7779 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
7780 }
7781
7782 // Position child window
7783 if (flags & ImGuiWindowFlags_ChildWindow)
7784 {
7785 IM_ASSERT(parent_window && parent_window->Active);
7786 window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
7787 parent_window->DC.ChildWindows.push_back(v: window);
7788 if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
7789 window->Pos = parent_window->DC.CursorPos;
7790 }
7791
7792 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
7793 if (window_pos_with_pivot)
7794 SetWindowPos(window, pos: window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, cond: 0); // Position given a pivot (e.g. for centering)
7795 else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
7796 window->Pos = FindBestWindowPosForPopup(window);
7797 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
7798 window->Pos = FindBestWindowPosForPopup(window);
7799 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
7800 window->Pos = FindBestWindowPosForPopup(window);
7801
7802 // Late create viewport if we don't fit within our current host viewport.
7803 if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_IsMinimized))
7804 if (!window->Viewport->GetMainRect().Contains(r: window->Rect()))
7805 {
7806 // This is based on the assumption that the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport)
7807 //ImGuiViewport* old_viewport = window->Viewport;
7808 window->Viewport = AddUpdateViewport(window, id: window->ID, platform_pos: window->Pos, size: window->Size, flags: ImGuiViewportFlags_NoFocusOnAppearing);
7809
7810 // FIXME-DPI
7811 //IM_ASSERT(old_viewport->DpiScale == window->Viewport->DpiScale); // FIXME-DPI: Something went wrong
7812 SetCurrentViewport(window, viewport: window->Viewport);
7813 window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f;
7814 SetCurrentWindow(window);
7815 }
7816
7817 if (window->ViewportOwned)
7818 WindowSyncOwnedViewport(window, parent_window_in_stack);
7819
7820 // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
7821 // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
7822 ImRect viewport_rect(window->Viewport->GetMainRect());
7823 ImRect viewport_work_rect(window->Viewport->GetWorkRect());
7824 ImVec2 visibility_padding = ImMax(lhs: style.DisplayWindowPadding, rhs: style.DisplaySafeAreaPadding);
7825 ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding);
7826
7827 // Clamp position/size so window stays visible within its viewport or monitor
7828 // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
7829 // FIXME: Similar to code in GetWindowAllowedExtentRect()
7830 if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow))
7831 {
7832 if (!window->ViewportOwned && viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f)
7833 {
7834 ClampWindowPos(window, visibility_rect);
7835 }
7836 else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0)
7837 {
7838 if (g.MovingWindow != NULL && window->RootWindowDockTree == g.MovingWindow->RootWindowDockTree)
7839 {
7840 // While moving windows we allow them to straddle monitors (#7299, #3071)
7841 visibility_rect = g.PlatformMonitorsFullWorkRect;
7842 }
7843 else
7844 {
7845 // When not moving ensure visible in its monitor
7846 // Lost windows (e.g. a monitor disconnected) will naturally moved to the fallback/dummy monitor aka the main viewport.
7847 const ImGuiPlatformMonitor* monitor = GetViewportPlatformMonitor(viewport: window->Viewport);
7848 visibility_rect = ImRect(monitor->WorkPos, monitor->WorkPos + monitor->WorkSize);
7849 }
7850 visibility_rect.Expand(amount: -visibility_padding);
7851 ClampWindowPos(window, visibility_rect);
7852 }
7853 }
7854 window->Pos = ImTrunc(v: window->Pos);
7855
7856 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
7857 // Large values tend to lead to variety of artifacts and are not recommended.
7858 if (window->ViewportOwned || window->DockIsActive)
7859 window->WindowRounding = 0.0f;
7860 else
7861 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
7862
7863 // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
7864 //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
7865 // window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
7866
7867 // Apply window focus (new and reactivated windows are moved to front)
7868 bool want_focus = false;
7869 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
7870 {
7871 if (flags & ImGuiWindowFlags_Popup)
7872 want_focus = true;
7873 else if ((window->DockIsActive || (flags & ImGuiWindowFlags_ChildWindow) == 0) && !(flags & ImGuiWindowFlags_Tooltip))
7874 want_focus = true;
7875 }
7876
7877 // [Test Engine] Register whole window in the item system (before submitting further decorations)
7878#ifdef IMGUI_ENABLE_TEST_ENGINE
7879 if (g.TestEngineHookItems)
7880 {
7881 IM_ASSERT(window->IDStack.Size == 1);
7882 window->IDStack.Size = 0; // As window->IDStack[0] == window->ID here, make sure TestEngine doesn't erroneously see window as parent of itself.
7883 IMGUI_TEST_ENGINE_ITEM_ADD(window->ID, window->Rect(), NULL);
7884 IMGUI_TEST_ENGINE_ITEM_INFO(window->ID, window->Name, (g.HoveredWindow == window) ? ImGuiItemStatusFlags_HoveredRect : 0);
7885 window->IDStack.Size = 1;
7886 }
7887#endif
7888
7889 // Decide if we are going to handle borders and resize grips
7890 const bool handle_borders_and_resize_grips = (window->DockNodeAsHost || !window->DockIsActive);
7891
7892 // Handle manual resize: Resize Grips, Borders, Gamepad
7893 int border_hovered = -1, border_held = -1;
7894 ImU32 resize_grip_col[4] = {};
7895 const int resize_grip_count = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup)) ? 0 : g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
7896 const float resize_grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
7897 if (handle_borders_and_resize_grips && !window->Collapsed)
7898 if (int auto_fit_mask = UpdateWindowManualResize(window, size_auto_fit, border_hovered: &border_hovered, border_held: &border_held, resize_grip_count, resize_grip_col: &resize_grip_col[0], visibility_rect))
7899 {
7900 if (auto_fit_mask & (1 << ImGuiAxis_X))
7901 use_current_size_for_scrollbar_x = true;
7902 if (auto_fit_mask & (1 << ImGuiAxis_Y))
7903 use_current_size_for_scrollbar_y = true;
7904 }
7905 window->ResizeBorderHovered = (signed char)border_hovered;
7906 window->ResizeBorderHeld = (signed char)border_held;
7907
7908 // Synchronize window --> viewport again and one last time (clamping and manual resize may have affected either)
7909 if (window->ViewportOwned)
7910 {
7911 if (!window->Viewport->PlatformRequestMove)
7912 window->Viewport->Pos = window->Pos;
7913 if (!window->Viewport->PlatformRequestResize)
7914 window->Viewport->Size = window->Size;
7915 window->Viewport->UpdateWorkRect();
7916 viewport_rect = window->Viewport->GetMainRect();
7917 }
7918
7919 // Save last known viewport position within the window itself (so it can be saved in .ini file and restored)
7920 window->ViewportPos = window->Viewport->Pos;
7921
7922 // SCROLLBAR VISIBILITY
7923
7924 // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
7925 if (!window->Collapsed)
7926 {
7927 // When reading the current size we need to read it after size constraints have been applied.
7928 // Intentionally use previous frame values for InnerRect and ScrollbarSizes.
7929 // And when we use window->DecorationUp here it doesn't have ScrollbarSizes.y applied yet.
7930 ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2));
7931 ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + scrollbar_sizes_from_last_frame;
7932 ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
7933 float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
7934 float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
7935 //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
7936 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
7937 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));
7938 if (window->ScrollbarX && !window->ScrollbarY)
7939 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
7940 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
7941
7942 // Amend the partially filled window->DecorationXXX values.
7943 window->DecoOuterSizeX2 += window->ScrollbarSizes.x;
7944 window->DecoOuterSizeY2 += window->ScrollbarSizes.y;
7945 }
7946
7947 // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
7948 // Update various regions. Variables they depend on should be set above in this function.
7949 // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
7950
7951 // Outer rectangle
7952 // Not affected by window border size. Used by:
7953 // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
7954 // - Begin() initial clipping rect for drawing window background and borders.
7955 // - Begin() clipping whole child
7956 const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
7957 const ImRect outer_rect = window->Rect();
7958 const ImRect title_bar_rect = window->TitleBarRect();
7959 window->OuterRectClipped = outer_rect;
7960 if (window->DockIsActive)
7961 window->OuterRectClipped.Min.y += window->TitleBarHeight;
7962 window->OuterRectClipped.ClipWith(r: host_rect);
7963
7964 // Inner rectangle
7965 // Not affected by window border size. Used by:
7966 // - InnerClipRect
7967 // - ScrollToRectEx()
7968 // - NavUpdatePageUpPageDown()
7969 // - Scrollbar()
7970 window->InnerRect.Min.x = window->Pos.x + window->DecoOuterSizeX1;
7971 window->InnerRect.Min.y = window->Pos.y + window->DecoOuterSizeY1;
7972 window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->DecoOuterSizeX2;
7973 window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->DecoOuterSizeY2;
7974
7975 // Inner clipping rectangle.
7976 // - Extend a outside of normal work region up to borders.
7977 // - This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
7978 // - It also makes clipped items be more noticeable.
7979 // - And is consistent on both axis (prior to 2024/05/03 ClipRect used WindowPadding.x * 0.5f on left and right edge), see #3312
7980 // - Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
7981 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
7982 // Affected by window/frame border size. Used by:
7983 // - Begin() initial clip rect
7984 float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
7985
7986 // Try to match the fact that our border is drawn centered over the window rectangle, rather than inner.
7987 // This is why we do a *0.5f here. We don't currently even technically support large values for WindowBorderSize,
7988 // see e.g #7887 #7888, but may do after we move the window border to become an inner border (and then we can remove the 0.5f here).
7989 window->InnerClipRect.Min.x = ImFloor(f: 0.5f + window->InnerRect.Min.x + window->WindowBorderSize * 0.5f);
7990 window->InnerClipRect.Min.y = ImFloor(f: 0.5f + window->InnerRect.Min.y + top_border_size * 0.5f);
7991 window->InnerClipRect.Max.x = ImFloor(f: window->InnerRect.Max.x - window->WindowBorderSize * 0.5f);
7992 window->InnerClipRect.Max.y = ImFloor(f: window->InnerRect.Max.y - window->WindowBorderSize * 0.5f);
7993 window->InnerClipRect.ClipWithFull(r: host_rect);
7994
7995 // Default item width. Make it proportional to window size if window manually resizes
7996 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
7997 window->ItemWidthDefault = ImTrunc(f: window->Size.x * 0.65f);
7998 else
7999 window->ItemWidthDefault = ImTrunc(f: g.FontSize * 16.0f);
8000
8001 // SCROLLING
8002
8003 // Lock down maximum scrolling
8004 // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
8005 // for right/bottom aligned items without creating a scrollbar.
8006 window->ScrollMax.x = ImMax(lhs: 0.0f, rhs: window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
8007 window->ScrollMax.y = ImMax(lhs: 0.0f, rhs: window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
8008
8009 // Apply scrolling
8010 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
8011 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
8012 window->DecoInnerSizeX1 = window->DecoInnerSizeY1 = 0.0f;
8013
8014 // DRAWING
8015
8016 // Setup draw list and outer clipping rectangle
8017 IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
8018 window->DrawList->PushTextureID(texture_id: g.Font->ContainerAtlas->TexID);
8019 PushClipRect(clip_rect_min: host_rect.Min, clip_rect_max: host_rect.Max, intersect_with_current_clip_rect: false);
8020
8021 // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)
8022 // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
8023 // FIXME: User 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 (github #4493)
8024 const bool is_undocked_or_docked_visible = !window->DockIsActive || window->DockTabIsVisible;
8025 if (is_undocked_or_docked_visible)
8026 {
8027 bool render_decorations_in_parent = false;
8028 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
8029 {
8030 // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here)
8031 // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs
8032 ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL;
8033 bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(r: window->Rect()) : false;
8034 bool parent_is_empty = (parent_window->DrawList->VtxBuffer.Size == 0);
8035 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && !parent_is_empty && !previous_child_overlapping)
8036 render_decorations_in_parent = true;
8037 }
8038 if (render_decorations_in_parent)
8039 window->DrawList = parent_window->DrawList;
8040
8041 // Handle title bar, scrollbar, resize grips and resize borders
8042 const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
8043 const bool title_bar_is_highlight = want_focus || (window_to_highlight && (window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight || (window->DockNode && window->DockNode == window_to_highlight->DockNode)));
8044 RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size);
8045
8046 if (render_decorations_in_parent)
8047 window->DrawList = &window->DrawListInst;
8048 }
8049
8050 // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
8051
8052 // Work rectangle.
8053 // Affected by window padding and border size. Used by:
8054 // - Columns() for right-most edge
8055 // - TreeNode(), CollapsingHeader() for right-most edge
8056 // - BeginTabBar() for right-most edge
8057 const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
8058 const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
8059 const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(lhs: allow_scrollbar_x ? window->ContentSize.x : 0.0f, rhs: window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2)));
8060 const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(lhs: allow_scrollbar_y ? window->ContentSize.y : 0.0f, rhs: window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)));
8061 window->WorkRect.Min.x = ImTrunc(f: window->InnerRect.Min.x - window->Scroll.x + ImMax(lhs: window->WindowPadding.x, rhs: window->WindowBorderSize));
8062 window->WorkRect.Min.y = ImTrunc(f: window->InnerRect.Min.y - window->Scroll.y + ImMax(lhs: window->WindowPadding.y, rhs: window->WindowBorderSize));
8063 window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
8064 window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
8065 window->ParentWorkRect = window->WorkRect;
8066
8067 // [LEGACY] Content Region
8068 // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
8069 // Unless explicit content size is specified by user, this currently represent the region leading to no scrolling.
8070 // Used by:
8071 // - Mouse wheel scrolling + many other things
8072 window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x + window->DecoOuterSizeX1;
8073 window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->DecoOuterSizeY1;
8074 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->DecoOuterSizeX1 + window->DecoOuterSizeX2)));
8075 window->ContentRegionRect.Max.y = window->ContentRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)));
8076
8077 // Setup drawing context
8078 // (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.)
8079 window->DC.Indent.x = window->DecoOuterSizeX1 + window->WindowPadding.x - window->Scroll.x;
8080 window->DC.GroupOffset.x = 0.0f;
8081 window->DC.ColumnsOffset.x = 0.0f;
8082
8083 // Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount.
8084 // This is used by clipper to compensate and fix the most common use case of large scroll area. Easy and cheap, next best thing compared to switching everything to double or ImU64.
8085 double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DecoOuterSizeX1 + window->DC.ColumnsOffset.x;
8086 double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + window->DecoOuterSizeY1;
8087 window->DC.CursorStartPos = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y);
8088 window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y));
8089 window->DC.CursorPos = window->DC.CursorStartPos;
8090 window->DC.CursorPosPrevLine = window->DC.CursorPos;
8091 window->DC.CursorMaxPos = window->DC.CursorStartPos;
8092 window->DC.IdealMaxPos = window->DC.CursorStartPos;
8093 window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
8094 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
8095 window->DC.IsSameLine = window->DC.IsSetPos = false;
8096
8097 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
8098 window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext;
8099 window->DC.NavLayersActiveMaskNext = 0x00;
8100 window->DC.NavIsScrollPushableX = true;
8101 window->DC.NavHideHighlightOneFrame = false;
8102 window->DC.NavWindowHasScrollY = (window->ScrollMax.y > 0.0f);
8103
8104 window->DC.MenuBarAppending = false;
8105 window->DC.MenuColumns.Update(spacing: style.ItemSpacing.x, window_reappearing: window_just_activated_by_user);
8106 window->DC.TreeDepth = 0;
8107 window->DC.TreeHasStackDataDepthMask = 0x00;
8108 window->DC.ChildWindows.resize(new_size: 0);
8109 window->DC.StateStorage = &window->StateStorage;
8110 window->DC.CurrentColumns = NULL;
8111 window->DC.LayoutType = ImGuiLayoutType_Vertical;
8112 window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
8113
8114 window->DC.ItemWidth = window->ItemWidthDefault;
8115 window->DC.TextWrapPos = -1.0f; // disabled
8116 window->DC.ItemWidthStack.resize(new_size: 0);
8117 window->DC.TextWrapPosStack.resize(new_size: 0);
8118 if (flags & ImGuiWindowFlags_Modal)
8119 window->DC.ModalDimBgColor = ColorConvertFloat4ToU32(in: GetStyleColorVec4(idx: ImGuiCol_ModalWindowDimBg));
8120
8121 if (window->AutoFitFramesX > 0)
8122 window->AutoFitFramesX--;
8123 if (window->AutoFitFramesY > 0)
8124 window->AutoFitFramesY--;
8125
8126 // Clear SetNextWindowXXX data (can aim to move this higher in the function)
8127 g.NextWindowData.ClearFlags();
8128
8129 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
8130 // We ImGuiFocusRequestFlags_UnlessBelowModal to:
8131 // - Avoid focusing a window that is created outside of a modal. This will prevent active modal from being closed.
8132 // - Position window behind the modal that is not a begin-parent of this window.
8133 if (want_focus)
8134 FocusWindow(window, flags: ImGuiFocusRequestFlags_UnlessBelowModal);
8135 if (want_focus && window == g.NavWindow)
8136 NavInitWindow(window, force_reinit: false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls
8137
8138 // Close requested by platform window (apply to all windows in this viewport)
8139 if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport())
8140 {
8141 IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Window '%s' closed by PlatformRequestClose\n", window->Name);
8142 *p_open = false;
8143 g.NavWindowingToggleLayer = false; // Assume user mapped PlatformRequestClose on ALT-F4 so we disable ALT for menu toggle. False positive not an issue. // FIXME-NAV: Try removing.
8144 }
8145
8146 // Pressing CTRL+C copy window content into the clipboard
8147 // [EXPERIMENTAL] Breaks on nested Begin/End pairs. We need to work that out and add better logging scope.
8148 // [EXPERIMENTAL] Text outputs has many issues.
8149 if (g.IO.ConfigWindowsCopyContentsWithCtrlC)
8150 if (g.NavWindow && g.NavWindow->RootWindow == window && g.ActiveId == 0 && Shortcut(key_chord: ImGuiMod_Ctrl | ImGuiKey_C))
8151 LogToClipboard(auto_open_depth: 0);
8152
8153 // Title bar
8154 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
8155 RenderWindowTitleBarContents(window, title_bar_rect: ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open);
8156 else if (!(flags & ImGuiWindowFlags_NoTitleBar) && window->DockIsActive)
8157 LogText(fmt: "%.*s\n", (int)(FindRenderedTextEnd(text: window->Name) - window->Name), window->Name);
8158
8159 // Clear hit test shape every frame
8160 window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
8161
8162 if (flags & ImGuiWindowFlags_Tooltip)
8163 g.TooltipPreviousWindow = window;
8164
8165 if (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)
8166 {
8167 // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source.
8168 // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginDockableDragDropSource() also overwrites it.
8169 if (g.MovingWindow == window && (window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoDocking) == 0)
8170 BeginDockableDragDropSource(window);
8171
8172 // Docking: Any dockable window can act as a target. For dock node hosts we call BeginDockableDragDropTarget() in DockNodeUpdate() instead.
8173 if (g.DragDropActive && !(flags & ImGuiWindowFlags_NoDocking))
8174 if (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != window)
8175 if ((window == window->RootWindowDockTree) && !(window->Flags & ImGuiWindowFlags_DockNodeHost))
8176 BeginDockableDragDropTarget(window);
8177 }
8178
8179 // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
8180 // This is useful to allow creating context menus on title bar only, etc.
8181 SetLastItemDataForWindow(window, rect: title_bar_rect);
8182
8183 // [DEBUG]
8184#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8185 if (g.DebugLocateId != 0 && (window->ID == g.DebugLocateId || window->MoveId == g.DebugLocateId))
8186 DebugLocateItemResolveWithLastItem();
8187#endif
8188
8189 // [Test Engine] Register title bar / tab with MoveId.
8190#ifdef IMGUI_ENABLE_TEST_ENGINE
8191 if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
8192 IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.ID, g.LastItemData.Rect, &g.LastItemData);
8193#endif
8194 }
8195 else
8196 {
8197 // Skip refresh always mark active
8198 if (window->SkipRefresh)
8199 SetWindowActiveForSkipRefresh(window);
8200
8201 // Append
8202 SetCurrentViewport(window, viewport: window->Viewport);
8203 SetCurrentWindow(window);
8204 g.NextWindowData.ClearFlags();
8205 SetLastItemDataForWindow(window, rect: window->TitleBarRect());
8206 }
8207
8208 if (!(flags & ImGuiWindowFlags_DockNodeHost) && !window->SkipRefresh)
8209 PushClipRect(clip_rect_min: window->InnerClipRect.Min, clip_rect_max: window->InnerClipRect.Max, intersect_with_current_clip_rect: true);
8210
8211 // 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)
8212 window->WriteAccessed = false;
8213 window->BeginCount++;
8214
8215 // Update visibility
8216 if (first_begin_of_the_frame && !window->SkipRefresh)
8217 {
8218 // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesCannotSkipItems.
8219 // This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents.
8220 // This is analogous to regular windows being hidden from one frame.
8221 // It is especially important as e.g. nested TabBars would otherwise generate flicker in the form of one empty frame, or focus requests won't be processed.
8222 if (window->DockIsActive && !window->DockTabIsVisible)
8223 {
8224 if (window->LastFrameJustFocused == g.FrameCount)
8225 window->HiddenFramesCannotSkipItems = 1;
8226 else
8227 window->HiddenFramesCanSkipItems = 1;
8228 }
8229
8230 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu))
8231 {
8232 // Child window can be out of sight and have "negative" clip windows.
8233 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
8234 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0 || window->DockIsActive);
8235 const bool nav_request = (window->ChildFlags & ImGuiChildFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
8236 if (!g.LogEnabled && !nav_request)
8237 if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
8238 {
8239 if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
8240 window->HiddenFramesCannotSkipItems = 1;
8241 else
8242 window->HiddenFramesCanSkipItems = 1;
8243 }
8244
8245 // Hide along with parent or if parent is collapsed
8246 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
8247 window->HiddenFramesCanSkipItems = 1;
8248 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
8249 window->HiddenFramesCannotSkipItems = 1;
8250 }
8251
8252 // 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)
8253 if (style.Alpha <= 0.0f)
8254 window->HiddenFramesCanSkipItems = 1;
8255
8256 // Update the Hidden flag
8257 bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);
8258 window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0);
8259
8260 // Disable inputs for requested number of frames
8261 if (window->DisableInputsFrames > 0)
8262 {
8263 window->DisableInputsFrames--;
8264 window->Flags |= ImGuiWindowFlags_NoInputs;
8265 }
8266
8267 // Update the SkipItems flag, used to early out of all items functions (no layout required)
8268 bool skip_items = false;
8269 if (window->Collapsed || !window->Active || hidden_regular)
8270 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
8271 skip_items = true;
8272 window->SkipItems = skip_items;
8273
8274 // Restore NavLayersActiveMaskNext to previous value when not visible, so a CTRL+Tab back can use a safe value.
8275 if (window->SkipItems)
8276 window->DC.NavLayersActiveMaskNext = window->DC.NavLayersActiveMask;
8277
8278 // Sanity check: there are two spots which can set Appearing = true
8279 // - when 'window_just_activated_by_user' is set -> HiddenFramesCannotSkipItems is set -> SkipItems always false
8280 // - in BeginDocked() path when DockNodeIsVisible == DockTabIsVisible == true -> hidden _should_ be all zero // FIXME: Not formally proven, hence the assert.
8281 if (window->SkipItems && !window->Appearing)
8282 IM_ASSERT(window->Appearing == false); // Please report on GitHub if this triggers: https://github.com/ocornut/imgui/issues/4177
8283 }
8284 else if (first_begin_of_the_frame)
8285 {
8286 // Skip refresh mode
8287 window->SkipItems = true;
8288 }
8289
8290 // [DEBUG] io.ConfigDebugBeginReturnValue override return value to test Begin/End and BeginChild/EndChild behaviors.
8291 // (The implicit fallback window is NOT automatically ended allowing it to always be able to receive commands without crashing)
8292#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8293 if (!window->IsFallbackWindow)
8294 if ((g.IO.ConfigDebugBeginReturnValueOnce && window_just_created) || (g.IO.ConfigDebugBeginReturnValueLoop && g.DebugBeginReturnValueCullDepth == g.CurrentWindowStack.Size))
8295 {
8296 if (window->AutoFitFramesX > 0) { window->AutoFitFramesX++; }
8297 if (window->AutoFitFramesY > 0) { window->AutoFitFramesY++; }
8298 return false;
8299 }
8300#endif
8301
8302 return !window->SkipItems;
8303}
8304
8305static void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect)
8306{
8307 ImGuiContext& g = *GImGui;
8308 if (window->DockIsActive)
8309 SetLastItemData(item_id: window->MoveId, in_flags: g.CurrentItemFlags, item_flags: window->DockTabItemStatusFlags, item_rect: window->DockTabItemRect);
8310 else
8311 SetLastItemData(item_id: window->MoveId, in_flags: g.CurrentItemFlags, item_flags: IsMouseHoveringRect(r_min: rect.Min, r_max: rect.Max, clip: false) ? ImGuiItemStatusFlags_HoveredRect : 0, item_rect: rect);
8312}
8313
8314void ImGui::End()
8315{
8316 ImGuiContext& g = *GImGui;
8317 ImGuiWindow* window = g.CurrentWindow;
8318
8319 // Error checking: verify that user hasn't called End() too many times!
8320 if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
8321 {
8322 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
8323 return;
8324 }
8325 ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();
8326
8327 // Error checking: verify that user doesn't directly call End() on a child window.
8328 if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_DockNodeHost) && !window->DockIsActive)
8329 IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
8330
8331 // Close anything that is open
8332 if (window->DC.CurrentColumns)
8333 EndColumns();
8334 if (!(window->Flags & ImGuiWindowFlags_DockNodeHost) && !window->SkipRefresh) // Pop inner window clip rectangle
8335 PopClipRect();
8336 PopFocusScope();
8337 if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window)
8338 EndDisabledOverrideReenable();
8339
8340 if (window->SkipRefresh)
8341 {
8342 IM_ASSERT(window->DrawList == NULL);
8343 window->DrawList = &window->DrawListInst;
8344 }
8345
8346 // Stop logging
8347 if (g.LogWindow == window) // FIXME: add more options for scope of logging
8348 LogFinish();
8349
8350 if (window->DC.IsSetPos)
8351 ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
8352
8353 // Docking: report contents sizes to parent to allow for auto-resize
8354 if (window->DockNode && window->DockTabIsVisible)
8355 if (ImGuiWindow* host_window = window->DockNode->HostWindow) // FIXME-DOCK
8356 host_window->DC.CursorMaxPos = window->DC.CursorMaxPos + window->WindowPadding - host_window->WindowPadding;
8357
8358 // Pop from window stack
8359 g.LastItemData = window_stack_data.ParentLastItemDataBackup;
8360 if (window->Flags & ImGuiWindowFlags_ChildMenu)
8361 g.BeginMenuDepth--;
8362 if (window->Flags & ImGuiWindowFlags_Popup)
8363 g.BeginPopupStack.pop_back();
8364
8365 // Error handling, state recovery
8366 if (g.IO.ConfigErrorRecovery)
8367 ErrorRecoveryTryToRecoverWindowState(state_in: &window_stack_data.StackSizesInBegin);
8368
8369 g.CurrentWindowStack.pop_back();
8370 SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
8371 if (g.CurrentWindow)
8372 SetCurrentViewport(window: g.CurrentWindow, viewport: g.CurrentWindow->Viewport);
8373}
8374
8375void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
8376{
8377 ImGuiContext& g = *GImGui;
8378 IM_ASSERT(window == window->RootWindow);
8379
8380 const int cur_order = window->FocusOrder;
8381 IM_ASSERT(g.WindowsFocusOrder[cur_order] == window);
8382 if (g.WindowsFocusOrder.back() == window)
8383 return;
8384
8385 const int new_order = g.WindowsFocusOrder.Size - 1;
8386 for (int n = cur_order; n < new_order; n++)
8387 {
8388 g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1];
8389 g.WindowsFocusOrder[n]->FocusOrder--;
8390 IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n);
8391 }
8392 g.WindowsFocusOrder[new_order] = window;
8393 window->FocusOrder = (short)new_order;
8394}
8395
8396void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
8397{
8398 ImGuiContext& g = *GImGui;
8399 ImGuiWindow* current_front_window = g.Windows.back();
8400 if (current_front_window == window || current_front_window->RootWindowDockTree == window) // Cheap early out (could be better)
8401 return;
8402 for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
8403 if (g.Windows[i] == window)
8404 {
8405 memmove(dest: &g.Windows[i], src: &g.Windows[i + 1], n: (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
8406 g.Windows[g.Windows.Size - 1] = window;
8407 break;
8408 }
8409}
8410
8411void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
8412{
8413 ImGuiContext& g = *GImGui;
8414 if (g.Windows[0] == window)
8415 return;
8416 for (int i = 0; i < g.Windows.Size; i++)
8417 if (g.Windows[i] == window)
8418 {
8419 memmove(dest: &g.Windows[1], src: &g.Windows[0], n: (size_t)i * sizeof(ImGuiWindow*));
8420 g.Windows[0] = window;
8421 break;
8422 }
8423}
8424
8425void ImGui::BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* behind_window)
8426{
8427 IM_ASSERT(window != NULL && behind_window != NULL);
8428 ImGuiContext& g = *GImGui;
8429 window = window->RootWindow;
8430 behind_window = behind_window->RootWindow;
8431 int pos_wnd = FindWindowDisplayIndex(window);
8432 int pos_beh = FindWindowDisplayIndex(window: behind_window);
8433 if (pos_wnd < pos_beh)
8434 {
8435 size_t copy_bytes = (pos_beh - pos_wnd - 1) * sizeof(ImGuiWindow*);
8436 memmove(dest: &g.Windows.Data[pos_wnd], src: &g.Windows.Data[pos_wnd + 1], n: copy_bytes);
8437 g.Windows[pos_beh - 1] = window;
8438 }
8439 else
8440 {
8441 size_t copy_bytes = (pos_wnd - pos_beh) * sizeof(ImGuiWindow*);
8442 memmove(dest: &g.Windows.Data[pos_beh + 1], src: &g.Windows.Data[pos_beh], n: copy_bytes);
8443 g.Windows[pos_beh] = window;
8444 }
8445}
8446
8447int ImGui::FindWindowDisplayIndex(ImGuiWindow* window)
8448{
8449 ImGuiContext& g = *GImGui;
8450 return g.Windows.index_from_ptr(it: g.Windows.find(v: window));
8451}
8452
8453// Moving window to front of display and set focus (which happens to be back of our sorted list)
8454void ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags)
8455{
8456 ImGuiContext& g = *GImGui;
8457
8458 // Modal check?
8459 if ((flags & ImGuiFocusRequestFlags_UnlessBelowModal) && (g.NavWindow != window)) // Early out in common case.
8460 if (ImGuiWindow* blocking_modal = FindBlockingModal(window))
8461 {
8462 // This block would typically be reached in two situations:
8463 // - API call to FocusWindow() with a window under a modal and ImGuiFocusRequestFlags_UnlessBelowModal flag.
8464 // - User clicking on void or anything behind a modal while a modal is open (window == NULL)
8465 IMGUI_DEBUG_LOG_FOCUS("[focus] FocusWindow(\"%s\", UnlessBelowModal): prevented by \"%s\".\n", window ? window->Name : "<NULL>", blocking_modal->Name);
8466 if (window && window == window->RootWindow && (window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
8467 BringWindowToDisplayBehind(window, behind_window: blocking_modal); // Still bring right under modal. (FIXME: Could move in focus list too?)
8468 ClosePopupsOverWindow(ref_window: GetTopMostPopupModal(), restore_focus_to_window_under_popup: false); // Note how we need to use GetTopMostPopupModal() aad NOT blocking_modal, to handle nested modals
8469 return;
8470 }
8471
8472 // Find last focused child (if any) and focus it instead.
8473 if ((flags & ImGuiFocusRequestFlags_RestoreFocusedChild) && window != NULL)
8474 window = NavRestoreLastChildNavWindow(window);
8475
8476 // Apply focus
8477 if (g.NavWindow != window)
8478 {
8479 SetNavWindow(window);
8480 if (window && g.NavHighlightItemUnderNav)
8481 g.NavMousePosDirty = true;
8482 g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
8483 g.NavLayer = ImGuiNavLayer_Main;
8484 SetNavFocusScope(window ? window->NavRootFocusScopeId : 0);
8485 g.NavIdIsAlive = false;
8486 g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
8487
8488 // Close popups if any
8489 ClosePopupsOverWindow(ref_window: window, restore_focus_to_window_under_popup: false);
8490 }
8491
8492 // Move the root window to the top of the pile
8493 IM_ASSERT(window == NULL || window->RootWindowDockTree != NULL);
8494 ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL;
8495 ImGuiWindow* display_front_window = window ? window->RootWindowDockTree : NULL;
8496 ImGuiDockNode* dock_node = window ? window->DockNode : NULL;
8497 bool active_id_window_is_dock_node_host = (g.ActiveIdWindow && dock_node && dock_node->HostWindow == g.ActiveIdWindow);
8498
8499 // Steal active widgets. Some of the cases it triggers includes:
8500 // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
8501 // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
8502 // - Using dock host items (tab, collapse button) can trigger this before we redirect the ActiveIdWindow toward the child window.
8503 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
8504 if (!g.ActiveIdNoClearOnFocusLoss && !active_id_window_is_dock_node_host)
8505 ClearActiveID();
8506
8507 // Passing NULL allow to disable keyboard focus
8508 if (!window)
8509 return;
8510 window->LastFrameJustFocused = g.FrameCount;
8511
8512 // Select in dock node
8513 // For #2304 we avoid applying focus immediately before the tabbar is visible.
8514 //if (dock_node && dock_node->TabBar)
8515 // dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->TabId;
8516
8517 // Bring to front
8518 BringWindowToFocusFront(window: focus_front_window);
8519 if (((window->Flags | focus_front_window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
8520 BringWindowToDisplayFront(window: display_front_window);
8521}
8522
8523void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags)
8524{
8525 ImGuiContext& g = *GImGui;
8526 int start_idx = g.WindowsFocusOrder.Size - 1;
8527 if (under_this_window != NULL)
8528 {
8529 // Aim at root window behind us, if we are in a child window that's our own root (see #4640)
8530 int offset = -1;
8531 while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow)
8532 {
8533 under_this_window = under_this_window->ParentWindow;
8534 offset = 0;
8535 }
8536 start_idx = FindWindowFocusIndex(window: under_this_window) + offset;
8537 }
8538 for (int i = start_idx; i >= 0; i--)
8539 {
8540 // 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.
8541 ImGuiWindow* window = g.WindowsFocusOrder[i];
8542 if (window == ignore_window || !window->WasActive)
8543 continue;
8544 if (filter_viewport != NULL && window->Viewport != filter_viewport)
8545 continue;
8546 if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
8547 {
8548 // FIXME-DOCK: When ImGuiFocusRequestFlags_RestoreFocusedChild is set...
8549 // This is failing (lagging by one frame) for docked windows.
8550 // If A and B are docked into window and B disappear, at the NewFrame() call site window->NavLastChildNavWindow will still point to B.
8551 // We might leverage the tab order implicitly stored in window->DockNodeAsHost->TabBar (essentially the 'most_recently_selected_tab' code in tab bar will do that but on next update)
8552 // to tell which is the "previous" window. Or we may leverage 'LastFrameFocused/LastFrameJustFocused' and have this function handle child window itself?
8553 FocusWindow(window, flags);
8554 return;
8555 }
8556 }
8557 FocusWindow(NULL, flags);
8558}
8559
8560// Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only.
8561void ImGui::SetCurrentFont(ImFont* font)
8562{
8563 ImGuiContext& g = *GImGui;
8564 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
8565 IM_ASSERT(font->Scale > 0.0f);
8566 g.Font = font;
8567 g.FontBaseSize = ImMax(lhs: 1.0f, rhs: g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
8568 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
8569 g.FontScale = g.FontSize / g.Font->FontSize;
8570
8571 ImFontAtlas* atlas = g.Font->ContainerAtlas;
8572 g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
8573 g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
8574 g.DrawListSharedData.Font = g.Font;
8575 g.DrawListSharedData.FontSize = g.FontSize;
8576 g.DrawListSharedData.FontScale = g.FontScale;
8577}
8578
8579// Use ImDrawList::_SetTextureID(), making our shared g.FontStack[] authorative against window-local ImDrawList.
8580// - Whereas ImDrawList::PushTextureID()/PopTextureID() is not to be used across Begin() calls.
8581// - Note that we don't propagate current texture id when e.g. Begin()-ing into a new window, we never really did...
8582// - Some code paths never really fully worked with multiple atlas textures.
8583// - The right-ish solution may be to remove _SetTextureID() and make AddText/RenderText lazily call PushTextureID()/PopTextureID()
8584// the same way AddImage() does, but then all other primitives would also need to? I don't think we should tackle this problem
8585// because we have a concrete need and a test bed for multiple atlas textures.
8586void ImGui::PushFont(ImFont* font)
8587{
8588 ImGuiContext& g = *GImGui;
8589 if (font == NULL)
8590 font = GetDefaultFont();
8591 g.FontStack.push_back(v: font);
8592 SetCurrentFont(font);
8593 g.CurrentWindow->DrawList->_SetTextureID(texture_id: font->ContainerAtlas->TexID);
8594}
8595
8596void ImGui::PopFont()
8597{
8598 ImGuiContext& g = *GImGui;
8599 if (g.FontStack.Size <= 0)
8600 {
8601 IM_ASSERT_USER_ERROR(0, "Calling PopFont() too many times!");
8602 return;
8603 }
8604 g.FontStack.pop_back();
8605 ImFont* font = g.FontStack.Size == 0 ? GetDefaultFont() : g.FontStack.back();
8606 SetCurrentFont(font);
8607 g.CurrentWindow->DrawList->_SetTextureID(texture_id: font->ContainerAtlas->TexID);
8608}
8609
8610void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
8611{
8612 ImGuiContext& g = *GImGui;
8613 ImGuiItemFlags item_flags = g.CurrentItemFlags;
8614 IM_ASSERT(item_flags == g.ItemFlagsStack.back());
8615 if (enabled)
8616 item_flags |= option;
8617 else
8618 item_flags &= ~option;
8619 g.CurrentItemFlags = item_flags;
8620 g.ItemFlagsStack.push_back(v: item_flags);
8621}
8622
8623void ImGui::PopItemFlag()
8624{
8625 ImGuiContext& g = *GImGui;
8626 if (g.ItemFlagsStack.Size <= 1)
8627 {
8628 IM_ASSERT_USER_ERROR(0, "Calling PopItemFlag() too many times!");
8629 return;
8630 }
8631 g.ItemFlagsStack.pop_back();
8632 g.CurrentItemFlags = g.ItemFlagsStack.back();
8633}
8634
8635// BeginDisabled()/EndDisabled()
8636// - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)
8637// - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.
8638// - Feedback welcome at https://github.com/ocornut/imgui/issues/211
8639// - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions.
8640// (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls)
8641// - Note: mixing up BeginDisabled() and PushItemFlag(ImGuiItemFlags_Disabled) is currently NOT SUPPORTED.
8642void ImGui::BeginDisabled(bool disabled)
8643{
8644 ImGuiContext& g = *GImGui;
8645 bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
8646 if (!was_disabled && disabled)
8647 {
8648 g.DisabledAlphaBackup = g.Style.Alpha;
8649 g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha);
8650 }
8651 if (was_disabled || disabled)
8652 g.CurrentItemFlags |= ImGuiItemFlags_Disabled;
8653 g.ItemFlagsStack.push_back(v: g.CurrentItemFlags); // FIXME-OPT: can we simply skip this and use DisabledStackSize?
8654 g.DisabledStackSize++;
8655}
8656
8657void ImGui::EndDisabled()
8658{
8659 ImGuiContext& g = *GImGui;
8660 if (g.DisabledStackSize <= 0)
8661 {
8662 IM_ASSERT_USER_ERROR(0, "Calling EndDisabled() too many times!");
8663 return;
8664 }
8665 g.DisabledStackSize--;
8666 bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
8667 //PopItemFlag();
8668 g.ItemFlagsStack.pop_back();
8669 g.CurrentItemFlags = g.ItemFlagsStack.back();
8670 if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)
8671 g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar();
8672}
8673
8674// Could have been called BeginDisabledDisable() but it didn't want to be award nominated for most awkward function name.
8675// Ideally we would use a shared e.g. BeginDisabled()->BeginDisabledEx() but earlier needs to be optimal.
8676// The whole code for this is awkward, will reevaluate if we find a way to implement SetNextItemDisabled().
8677void ImGui::BeginDisabledOverrideReenable()
8678{
8679 ImGuiContext& g = *GImGui;
8680 IM_ASSERT(g.CurrentItemFlags & ImGuiItemFlags_Disabled);
8681 g.Style.Alpha = g.DisabledAlphaBackup;
8682 g.CurrentItemFlags &= ~ImGuiItemFlags_Disabled;
8683 g.ItemFlagsStack.push_back(v: g.CurrentItemFlags);
8684 g.DisabledStackSize++;
8685}
8686
8687void ImGui::EndDisabledOverrideReenable()
8688{
8689 ImGuiContext& g = *GImGui;
8690 g.DisabledStackSize--;
8691 IM_ASSERT(g.DisabledStackSize > 0);
8692 g.ItemFlagsStack.pop_back();
8693 g.CurrentItemFlags = g.ItemFlagsStack.back();
8694 g.Style.Alpha = g.DisabledAlphaBackup * g.Style.DisabledAlpha;
8695}
8696
8697void ImGui::PushTextWrapPos(float wrap_pos_x)
8698{
8699 ImGuiContext& g = *GImGui;
8700 ImGuiWindow* window = g.CurrentWindow;
8701 window->DC.TextWrapPosStack.push_back(v: window->DC.TextWrapPos);
8702 window->DC.TextWrapPos = wrap_pos_x;
8703}
8704
8705void ImGui::PopTextWrapPos()
8706{
8707 ImGuiContext& g = *GImGui;
8708 ImGuiWindow* window = g.CurrentWindow;
8709 if (window->DC.TextWrapPosStack.Size <= 0)
8710 {
8711 IM_ASSERT_USER_ERROR(0, "Calling PopTextWrapPos() too many times!");
8712 return;
8713 }
8714 window->DC.TextWrapPos = window->DC.TextWrapPosStack.back();
8715 window->DC.TextWrapPosStack.pop_back();
8716}
8717
8718static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy, bool dock_hierarchy)
8719{
8720 ImGuiWindow* last_window = NULL;
8721 while (last_window != window)
8722 {
8723 last_window = window;
8724 window = window->RootWindow;
8725 if (popup_hierarchy)
8726 window = window->RootWindowPopupTree;
8727 if (dock_hierarchy)
8728 window = window->RootWindowDockTree;
8729 }
8730 return window;
8731}
8732
8733bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy)
8734{
8735 ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy, dock_hierarchy);
8736 if (window_root == potential_parent)
8737 return true;
8738 while (window != NULL)
8739 {
8740 if (window == potential_parent)
8741 return true;
8742 if (window == window_root) // end of chain
8743 return false;
8744 window = window->ParentWindow;
8745 }
8746 return false;
8747}
8748
8749bool ImGui::IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
8750{
8751 if (window->RootWindow == potential_parent)
8752 return true;
8753 while (window != NULL)
8754 {
8755 if (window == potential_parent)
8756 return true;
8757 window = window->ParentWindowInBeginStack;
8758 }
8759 return false;
8760}
8761
8762bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)
8763{
8764 ImGuiContext& g = *GImGui;
8765
8766 // It would be saner to ensure that display layer is always reflected in the g.Windows[] order, which would likely requires altering all manipulations of that array
8767 const int display_layer_delta = GetWindowDisplayLayer(window: potential_above) - GetWindowDisplayLayer(window: potential_below);
8768 if (display_layer_delta != 0)
8769 return display_layer_delta > 0;
8770
8771 for (int i = g.Windows.Size - 1; i >= 0; i--)
8772 {
8773 ImGuiWindow* candidate_window = g.Windows[i];
8774 if (candidate_window == potential_above)
8775 return true;
8776 if (candidate_window == potential_below)
8777 return false;
8778 }
8779 return false;
8780}
8781
8782// Is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options.
8783// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
8784// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
8785// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
8786bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
8787{
8788 ImGuiContext& g = *GImGui;
8789 IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0, "Invalid flags for IsWindowHovered()!");
8790
8791 ImGuiWindow* ref_window = g.HoveredWindow;
8792 ImGuiWindow* cur_window = g.CurrentWindow;
8793 if (ref_window == NULL)
8794 return false;
8795
8796 if ((flags & ImGuiHoveredFlags_AnyWindow) == 0)
8797 {
8798 IM_ASSERT(cur_window); // Not inside a Begin()/End()
8799 const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0;
8800 const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0;
8801 if (flags & ImGuiHoveredFlags_RootWindow)
8802 cur_window = GetCombinedRootWindow(window: cur_window, popup_hierarchy, dock_hierarchy);
8803
8804 bool result;
8805 if (flags & ImGuiHoveredFlags_ChildWindows)
8806 result = IsWindowChildOf(window: ref_window, potential_parent: cur_window, popup_hierarchy, dock_hierarchy);
8807 else
8808 result = (ref_window == cur_window);
8809 if (!result)
8810 return false;
8811 }
8812
8813 if (!IsWindowContentHoverable(window: ref_window, flags))
8814 return false;
8815 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
8816 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId)
8817 return false;
8818
8819 // When changing hovered window we requires a bit of stationary delay before activating hover timer.
8820 // FIXME: We don't support delay other than stationary one for now, other delay would need a way
8821 // to fulfill the possibility that multiple IsWindowHovered() with varying flag could return true
8822 // for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache.
8823 // We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow.
8824 if (flags & ImGuiHoveredFlags_ForTooltip)
8825 flags = ApplyHoverFlagsForTooltip(user_flags: flags, shared_flags: g.Style.HoverFlagsForTooltipMouse);
8826 if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverWindowUnlockedStationaryId != ref_window->ID)
8827 return false;
8828
8829 return true;
8830}
8831
8832bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
8833{
8834 ImGuiContext& g = *GImGui;
8835 ImGuiWindow* ref_window = g.NavWindow;
8836 ImGuiWindow* cur_window = g.CurrentWindow;
8837
8838 if (ref_window == NULL)
8839 return false;
8840 if (flags & ImGuiFocusedFlags_AnyWindow)
8841 return true;
8842
8843 IM_ASSERT(cur_window); // Not inside a Begin()/End()
8844 const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0;
8845 const bool dock_hierarchy = (flags & ImGuiFocusedFlags_DockHierarchy) != 0;
8846 if (flags & ImGuiHoveredFlags_RootWindow)
8847 cur_window = GetCombinedRootWindow(window: cur_window, popup_hierarchy, dock_hierarchy);
8848
8849 if (flags & ImGuiHoveredFlags_ChildWindows)
8850 return IsWindowChildOf(window: ref_window, potential_parent: cur_window, popup_hierarchy, dock_hierarchy);
8851 else
8852 return (ref_window == cur_window);
8853}
8854
8855ImGuiID ImGui::GetWindowDockID()
8856{
8857 ImGuiContext& g = *GImGui;
8858 return g.CurrentWindow->DockId;
8859}
8860
8861bool ImGui::IsWindowDocked()
8862{
8863 ImGuiContext& g = *GImGui;
8864 return g.CurrentWindow->DockIsActive;
8865}
8866
8867// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
8868// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
8869// If you want a window to never be focused, you may use the e.g. NoInputs flag.
8870bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
8871{
8872 return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
8873}
8874
8875float ImGui::GetWindowWidth()
8876{
8877 ImGuiWindow* window = GImGui->CurrentWindow;
8878 return window->Size.x;
8879}
8880
8881float ImGui::GetWindowHeight()
8882{
8883 ImGuiWindow* window = GImGui->CurrentWindow;
8884 return window->Size.y;
8885}
8886
8887ImVec2 ImGui::GetWindowPos()
8888{
8889 ImGuiContext& g = *GImGui;
8890 ImGuiWindow* window = g.CurrentWindow;
8891 return window->Pos;
8892}
8893
8894void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
8895{
8896 // Test condition (NB: bit 0 is always true) and clear flags for next time
8897 if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
8898 return;
8899
8900 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8901 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8902 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
8903
8904 // Set
8905 const ImVec2 old_pos = window->Pos;
8906 window->Pos = ImTrunc(v: pos);
8907 ImVec2 offset = window->Pos - old_pos;
8908 if (offset.x == 0.0f && offset.y == 0.0f)
8909 return;
8910 MarkIniSettingsDirty(window);
8911 // FIXME: share code with TranslateWindow(), need to confirm whether the 3 rect modified by TranslateWindow() are desirable here.
8912 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
8913 window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
8914 window->DC.IdealMaxPos += offset;
8915 window->DC.CursorStartPos += offset;
8916}
8917
8918void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
8919{
8920 ImGuiWindow* window = GetCurrentWindowRead();
8921 SetWindowPos(window, pos, cond);
8922}
8923
8924void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
8925{
8926 if (ImGuiWindow* window = FindWindowByName(name))
8927 SetWindowPos(window, pos, cond);
8928}
8929
8930ImVec2 ImGui::GetWindowSize()
8931{
8932 ImGuiWindow* window = GetCurrentWindowRead();
8933 return window->Size;
8934}
8935
8936void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
8937{
8938 // Test condition (NB: bit 0 is always true) and clear flags for next time
8939 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
8940 return;
8941
8942 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8943 window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8944
8945 // Enable auto-fit (not done in BeginChild() path unless appearing or combined with ImGuiChildFlags_AlwaysAutoResize)
8946 if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
8947 window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0;
8948 if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
8949 window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0;
8950
8951 // Set
8952 ImVec2 old_size = window->SizeFull;
8953 if (size.x <= 0.0f)
8954 window->AutoFitOnlyGrows = false;
8955 else
8956 window->SizeFull.x = IM_TRUNC(size.x);
8957 if (size.y <= 0.0f)
8958 window->AutoFitOnlyGrows = false;
8959 else
8960 window->SizeFull.y = IM_TRUNC(size.y);
8961 if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y)
8962 MarkIniSettingsDirty(window);
8963}
8964
8965void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
8966{
8967 SetWindowSize(window: GImGui->CurrentWindow, size, cond);
8968}
8969
8970void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
8971{
8972 if (ImGuiWindow* window = FindWindowByName(name))
8973 SetWindowSize(window, size, cond);
8974}
8975
8976void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
8977{
8978 // Test condition (NB: bit 0 is always true) and clear flags for next time
8979 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
8980 return;
8981 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8982
8983 // Set
8984 window->Collapsed = collapsed;
8985}
8986
8987void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
8988{
8989 IM_ASSERT(window->HitTestHoleSize.x == 0); // We don't support multiple holes/hit test filters
8990 window->HitTestHoleSize = ImVec2ih(size);
8991 window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
8992}
8993
8994void ImGui::SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window)
8995{
8996 window->Hidden = window->SkipItems = true;
8997 window->HiddenFramesCanSkipItems = 1;
8998}
8999
9000void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
9001{
9002 SetWindowCollapsed(window: GImGui->CurrentWindow, collapsed, cond);
9003}
9004
9005bool ImGui::IsWindowCollapsed()
9006{
9007 ImGuiWindow* window = GetCurrentWindowRead();
9008 return window->Collapsed;
9009}
9010
9011bool ImGui::IsWindowAppearing()
9012{
9013 ImGuiWindow* window = GetCurrentWindowRead();
9014 return window->Appearing;
9015}
9016
9017void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
9018{
9019 if (ImGuiWindow* window = FindWindowByName(name))
9020 SetWindowCollapsed(window, collapsed, cond);
9021}
9022
9023void ImGui::SetWindowFocus()
9024{
9025 FocusWindow(window: GImGui->CurrentWindow);
9026}
9027
9028void ImGui::SetWindowFocus(const char* name)
9029{
9030 if (name)
9031 {
9032 if (ImGuiWindow* window = FindWindowByName(name))
9033 FocusWindow(window);
9034 }
9035 else
9036 {
9037 FocusWindow(NULL);
9038 }
9039}
9040
9041void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
9042{
9043 ImGuiContext& g = *GImGui;
9044 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
9045 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
9046 g.NextWindowData.PosVal = pos;
9047 g.NextWindowData.PosPivotVal = pivot;
9048 g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
9049 g.NextWindowData.PosUndock = true;
9050}
9051
9052void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
9053{
9054 ImGuiContext& g = *GImGui;
9055 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
9056 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
9057 g.NextWindowData.SizeVal = size;
9058 g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
9059}
9060
9061// For each axis:
9062// - Use 0.0f as min or FLT_MAX as max if you don't want limits, e.g. size_min = (500.0f, 0.0f), size_max = (FLT_MAX, FLT_MAX) sets a minimum width.
9063// - Use -1 for both min and max of same axis to preserve current size which itself is a constraint.
9064// - See "Demo->Examples->Constrained-resizing window" for examples.
9065void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
9066{
9067 ImGuiContext& g = *GImGui;
9068 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
9069 g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
9070 g.NextWindowData.SizeCallback = custom_callback;
9071 g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
9072}
9073
9074// Content size = inner scrollable rectangle, padded with WindowPadding.
9075// SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
9076void ImGui::SetNextWindowContentSize(const ImVec2& size)
9077{
9078 ImGuiContext& g = *GImGui;
9079 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
9080 g.NextWindowData.ContentSizeVal = ImTrunc(v: size);
9081}
9082
9083void ImGui::SetNextWindowScroll(const ImVec2& scroll)
9084{
9085 ImGuiContext& g = *GImGui;
9086 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll;
9087 g.NextWindowData.ScrollVal = scroll;
9088}
9089
9090void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
9091{
9092 ImGuiContext& g = *GImGui;
9093 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
9094 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
9095 g.NextWindowData.CollapsedVal = collapsed;
9096 g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
9097}
9098
9099void ImGui::SetNextWindowFocus()
9100{
9101 ImGuiContext& g = *GImGui;
9102 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
9103}
9104
9105void ImGui::SetNextWindowBgAlpha(float alpha)
9106{
9107 ImGuiContext& g = *GImGui;
9108 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
9109 g.NextWindowData.BgAlphaVal = alpha;
9110}
9111
9112void ImGui::SetNextWindowViewport(ImGuiID id)
9113{
9114 ImGuiContext& g = *GImGui;
9115 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasViewport;
9116 g.NextWindowData.ViewportId = id;
9117}
9118
9119void ImGui::SetNextWindowDockID(ImGuiID id, ImGuiCond cond)
9120{
9121 ImGuiContext& g = *GImGui;
9122 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasDock;
9123 g.NextWindowData.DockCond = cond ? cond : ImGuiCond_Always;
9124 g.NextWindowData.DockId = id;
9125}
9126
9127void ImGui::SetNextWindowClass(const ImGuiWindowClass* window_class)
9128{
9129 ImGuiContext& g = *GImGui;
9130 IM_ASSERT((window_class->ViewportFlagsOverrideSet & window_class->ViewportFlagsOverrideClear) == 0); // Cannot set both set and clear for the same bit
9131 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasWindowClass;
9132 g.NextWindowData.WindowClass = *window_class;
9133}
9134
9135// This is experimental and meant to be a toy for exploring a future/wider range of features.
9136void ImGui::SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags)
9137{
9138 ImGuiContext& g = *GImGui;
9139 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasRefreshPolicy;
9140 g.NextWindowData.RefreshFlagsVal = flags;
9141}
9142
9143ImDrawList* ImGui::GetWindowDrawList()
9144{
9145 ImGuiWindow* window = GetCurrentWindow();
9146 return window->DrawList;
9147}
9148
9149float ImGui::GetWindowDpiScale()
9150{
9151 ImGuiContext& g = *GImGui;
9152 return g.CurrentDpiScale;
9153}
9154
9155ImGuiViewport* ImGui::GetWindowViewport()
9156{
9157 ImGuiContext& g = *GImGui;
9158 IM_ASSERT(g.CurrentViewport != NULL && g.CurrentViewport == g.CurrentWindow->Viewport);
9159 return g.CurrentViewport;
9160}
9161
9162ImFont* ImGui::GetFont()
9163{
9164 return GImGui->Font;
9165}
9166
9167float ImGui::GetFontSize()
9168{
9169 return GImGui->FontSize;
9170}
9171
9172ImVec2 ImGui::GetFontTexUvWhitePixel()
9173{
9174 return GImGui->DrawListSharedData.TexUvWhitePixel;
9175}
9176
9177void ImGui::SetWindowFontScale(float scale)
9178{
9179 IM_ASSERT(scale > 0.0f);
9180 ImGuiContext& g = *GImGui;
9181 ImGuiWindow* window = GetCurrentWindow();
9182 window->FontWindowScale = scale;
9183 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
9184 g.FontScale = g.DrawListSharedData.FontScale = g.FontSize / g.Font->FontSize;
9185}
9186
9187void ImGui::PushFocusScope(ImGuiID id)
9188{
9189 ImGuiContext& g = *GImGui;
9190 ImGuiFocusScopeData data;
9191 data.ID = id;
9192 data.WindowID = g.CurrentWindow->ID;
9193 g.FocusScopeStack.push_back(v: data);
9194 g.CurrentFocusScopeId = id;
9195}
9196
9197void ImGui::PopFocusScope()
9198{
9199 ImGuiContext& g = *GImGui;
9200 if (g.FocusScopeStack.Size <= g.StackSizesInBeginForCurrentWindow->SizeOfFocusScopeStack)
9201 {
9202 IM_ASSERT_USER_ERROR(0, "Calling PopFocusScope() too many times!");
9203 return;
9204 }
9205 g.FocusScopeStack.pop_back();
9206 g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back().ID : 0;
9207}
9208
9209void ImGui::SetNavFocusScope(ImGuiID focus_scope_id)
9210{
9211 ImGuiContext& g = *GImGui;
9212 g.NavFocusScopeId = focus_scope_id;
9213 g.NavFocusRoute.resize(new_size: 0); // Invalidate
9214 if (focus_scope_id == 0)
9215 return;
9216 IM_ASSERT(g.NavWindow != NULL);
9217
9218 // Store current path (in reverse order)
9219 if (focus_scope_id == g.CurrentFocusScopeId)
9220 {
9221 // Top of focus stack contains local focus scopes inside current window
9222 for (int n = g.FocusScopeStack.Size - 1; n >= 0 && g.FocusScopeStack.Data[n].WindowID == g.CurrentWindow->ID; n--)
9223 g.NavFocusRoute.push_back(v: g.FocusScopeStack.Data[n]);
9224 }
9225 else if (focus_scope_id == g.NavWindow->NavRootFocusScopeId)
9226 g.NavFocusRoute.push_back(v: { .ID: focus_scope_id, .WindowID: g.NavWindow->ID });
9227 else
9228 return;
9229
9230 // Then follow on manually set ParentWindowForFocusRoute field (#6798)
9231 for (ImGuiWindow* window = g.NavWindow->ParentWindowForFocusRoute; window != NULL; window = window->ParentWindowForFocusRoute)
9232 g.NavFocusRoute.push_back(v: { .ID: window->NavRootFocusScopeId, .WindowID: window->ID });
9233 IM_ASSERT(g.NavFocusRoute.Size < 100); // Maximum depth is technically 251 as per CalcRoutingScore(): 254 - 3
9234}
9235
9236// Focus = move navigation cursor, set scrolling, set focus window.
9237void ImGui::FocusItem()
9238{
9239 ImGuiContext& g = *GImGui;
9240 ImGuiWindow* window = g.CurrentWindow;
9241 IMGUI_DEBUG_LOG_FOCUS("FocusItem(0x%08x) in window \"%s\"\n", g.LastItemData.ID, window->Name);
9242 if (g.DragDropActive || g.MovingWindow != NULL) // FIXME: Opt-in flags for this?
9243 {
9244 IMGUI_DEBUG_LOG_FOCUS("FocusItem() ignored while DragDropActive!\n");
9245 return;
9246 }
9247
9248 ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible | ImGuiNavMoveFlags_NoSelect;
9249 ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
9250 SetNavWindow(window);
9251 NavMoveRequestSubmit(move_dir: ImGuiDir_None, clip_dir: ImGuiDir_Up, move_flags, scroll_flags);
9252 NavMoveRequestResolveWithLastItem(result: &g.NavMoveResultLocal);
9253}
9254
9255void ImGui::ActivateItemByID(ImGuiID id)
9256{
9257 ImGuiContext& g = *GImGui;
9258 g.NavNextActivateId = id;
9259 g.NavNextActivateFlags = ImGuiActivateFlags_None;
9260}
9261
9262// Note: this will likely be called ActivateItem() once we rework our Focus/Activation system!
9263// But ActivateItem() should function without altering scroll/focus?
9264void ImGui::SetKeyboardFocusHere(int offset)
9265{
9266 ImGuiContext& g = *GImGui;
9267 ImGuiWindow* window = g.CurrentWindow;
9268 IM_ASSERT(offset >= -1); // -1 is allowed but not below
9269 IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name);
9270
9271 // It makes sense in the vast majority of cases to never interrupt a drag and drop.
9272 // When we refactor this function into ActivateItem() we may want to make this an option.
9273 // MovingWindow is protected from most user inputs using SetActiveIdUsingNavAndKeys(), but
9274 // is also automatically dropped in the event g.ActiveId is stolen.
9275 if (g.DragDropActive || g.MovingWindow != NULL)
9276 {
9277 IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere() ignored while DragDropActive!\n");
9278 return;
9279 }
9280
9281 SetNavWindow(window);
9282
9283 ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible;
9284 ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
9285 NavMoveRequestSubmit(move_dir: ImGuiDir_None, clip_dir: offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
9286 if (offset == -1)
9287 {
9288 NavMoveRequestResolveWithLastItem(result: &g.NavMoveResultLocal);
9289 }
9290 else
9291 {
9292 g.NavTabbingDir = 1;
9293 g.NavTabbingCounter = offset + 1;
9294 }
9295}
9296
9297void ImGui::SetItemDefaultFocus()
9298{
9299 ImGuiContext& g = *GImGui;
9300 ImGuiWindow* window = g.CurrentWindow;
9301 if (!window->Appearing)
9302 return;
9303 if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResult.ID == 0) || g.NavLayer != window->DC.NavLayerCurrent)
9304 return;
9305
9306 g.NavInitRequest = false;
9307 NavApplyItemToResult(result: &g.NavInitResult);
9308 NavUpdateAnyRequestFlag();
9309
9310 // Scroll could be done in NavInitRequestApplyResult() via an opt-in flag (we however don't want regular init requests to scroll)
9311 if (!window->ClipRect.Contains(r: g.LastItemData.Rect))
9312 ScrollToRectEx(window, rect: g.LastItemData.Rect, flags: ImGuiScrollFlags_None);
9313}
9314
9315void ImGui::SetStateStorage(ImGuiStorage* tree)
9316{
9317 ImGuiWindow* window = GImGui->CurrentWindow;
9318 window->DC.StateStorage = tree ? tree : &window->StateStorage;
9319}
9320
9321ImGuiStorage* ImGui::GetStateStorage()
9322{
9323 ImGuiWindow* window = GImGui->CurrentWindow;
9324 return window->DC.StateStorage;
9325}
9326
9327bool ImGui::IsRectVisible(const ImVec2& size)
9328{
9329 ImGuiWindow* window = GImGui->CurrentWindow;
9330 return window->ClipRect.Overlaps(r: ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
9331}
9332
9333bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
9334{
9335 ImGuiWindow* window = GImGui->CurrentWindow;
9336 return window->ClipRect.Overlaps(r: ImRect(rect_min, rect_max));
9337}
9338
9339//-----------------------------------------------------------------------------
9340// [SECTION] ID STACK
9341//-----------------------------------------------------------------------------
9342
9343// This is one of the very rare legacy case where we use ImGuiWindow methods,
9344// it should ideally be flattened at some point but it's been used a lots by widgets.
9345IM_MSVC_RUNTIME_CHECKS_OFF
9346ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
9347{
9348 ImGuiID seed = IDStack.back();
9349 ImGuiID id = ImHashStr(data_p: str, data_size: str_end ? (str_end - str) : 0, seed);
9350#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9351 ImGuiContext& g = *Ctx;
9352 if (g.DebugHookIdInfo == id)
9353 ImGui::DebugHookIdInfo(id, data_type: ImGuiDataType_String, data_id: str, data_id_end: str_end);
9354#endif
9355 return id;
9356}
9357
9358ImGuiID ImGuiWindow::GetID(const void* ptr)
9359{
9360 ImGuiID seed = IDStack.back();
9361 ImGuiID id = ImHashData(data_p: &ptr, data_size: sizeof(void*), seed);
9362#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9363 ImGuiContext& g = *Ctx;
9364 if (g.DebugHookIdInfo == id)
9365 ImGui::DebugHookIdInfo(id, data_type: ImGuiDataType_Pointer, data_id: ptr, NULL);
9366#endif
9367 return id;
9368}
9369
9370ImGuiID ImGuiWindow::GetID(int n)
9371{
9372 ImGuiID seed = IDStack.back();
9373 ImGuiID id = ImHashData(data_p: &n, data_size: sizeof(n), seed);
9374#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9375 ImGuiContext& g = *Ctx;
9376 if (g.DebugHookIdInfo == id)
9377 ImGui::DebugHookIdInfo(id, data_type: ImGuiDataType_S32, data_id: (void*)(intptr_t)n, NULL);
9378#endif
9379 return id;
9380}
9381
9382// This is only used in rare/specific situations to manufacture an ID out of nowhere.
9383// FIXME: Consider instead storing last non-zero ID + count of successive zero-ID, and combine those?
9384ImGuiID ImGuiWindow::GetIDFromPos(const ImVec2& p_abs)
9385{
9386 ImGuiID seed = IDStack.back();
9387 ImVec2 p_rel = ImGui::WindowPosAbsToRel(window: this, p: p_abs);
9388 ImGuiID id = ImHashData(data_p: &p_rel, data_size: sizeof(p_rel), seed);
9389 return id;
9390}
9391
9392// "
9393ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
9394{
9395 ImGuiID seed = IDStack.back();
9396 ImRect r_rel = ImGui::WindowRectAbsToRel(window: this, r: r_abs);
9397 ImGuiID id = ImHashData(data_p: &r_rel, data_size: sizeof(r_rel), seed);
9398 return id;
9399}
9400
9401void ImGui::PushID(const char* str_id)
9402{
9403 ImGuiContext& g = *GImGui;
9404 ImGuiWindow* window = g.CurrentWindow;
9405 ImGuiID id = window->GetID(str: str_id);
9406 window->IDStack.push_back(v: id);
9407}
9408
9409void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
9410{
9411 ImGuiContext& g = *GImGui;
9412 ImGuiWindow* window = g.CurrentWindow;
9413 ImGuiID id = window->GetID(str: str_id_begin, str_end: str_id_end);
9414 window->IDStack.push_back(v: id);
9415}
9416
9417void ImGui::PushID(const void* ptr_id)
9418{
9419 ImGuiContext& g = *GImGui;
9420 ImGuiWindow* window = g.CurrentWindow;
9421 ImGuiID id = window->GetID(ptr: ptr_id);
9422 window->IDStack.push_back(v: id);
9423}
9424
9425void ImGui::PushID(int int_id)
9426{
9427 ImGuiContext& g = *GImGui;
9428 ImGuiWindow* window = g.CurrentWindow;
9429 ImGuiID id = window->GetID(n: int_id);
9430 window->IDStack.push_back(v: id);
9431}
9432
9433// Push a given id value ignoring the ID stack as a seed.
9434void ImGui::PushOverrideID(ImGuiID id)
9435{
9436 ImGuiContext& g = *GImGui;
9437 ImGuiWindow* window = g.CurrentWindow;
9438#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9439 if (g.DebugHookIdInfo == id)
9440 DebugHookIdInfo(id, data_type: ImGuiDataType_ID, NULL, NULL);
9441#endif
9442 window->IDStack.push_back(v: id);
9443}
9444
9445// Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call
9446// (note that when using this pattern, ID Stack Tool will tend to not display the intermediate stack level.
9447// for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more)
9448ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
9449{
9450 ImGuiID id = ImHashStr(data_p: str, data_size: str_end ? (str_end - str) : 0, seed);
9451#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9452 ImGuiContext& g = *GImGui;
9453 if (g.DebugHookIdInfo == id)
9454 DebugHookIdInfo(id, data_type: ImGuiDataType_String, data_id: str, data_id_end: str_end);
9455#endif
9456 return id;
9457}
9458
9459ImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed)
9460{
9461 ImGuiID id = ImHashData(data_p: &n, data_size: sizeof(n), seed);
9462#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9463 ImGuiContext& g = *GImGui;
9464 if (g.DebugHookIdInfo == id)
9465 DebugHookIdInfo(id, data_type: ImGuiDataType_S32, data_id: (void*)(intptr_t)n, NULL);
9466#endif
9467 return id;
9468}
9469
9470void ImGui::PopID()
9471{
9472 ImGuiWindow* window = GImGui->CurrentWindow;
9473 if (window->IDStack.Size <= 1)
9474 {
9475 IM_ASSERT_USER_ERROR(0, "Calling PopID() too many times!");
9476 return;
9477 }
9478 window->IDStack.pop_back();
9479}
9480
9481ImGuiID ImGui::GetID(const char* str_id)
9482{
9483 ImGuiWindow* window = GImGui->CurrentWindow;
9484 return window->GetID(str: str_id);
9485}
9486
9487ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
9488{
9489 ImGuiWindow* window = GImGui->CurrentWindow;
9490 return window->GetID(str: str_id_begin, str_end: str_id_end);
9491}
9492
9493ImGuiID ImGui::GetID(const void* ptr_id)
9494{
9495 ImGuiWindow* window = GImGui->CurrentWindow;
9496 return window->GetID(ptr: ptr_id);
9497}
9498
9499ImGuiID ImGui::GetID(int int_id)
9500{
9501 ImGuiWindow* window = GImGui->CurrentWindow;
9502 return window->GetID(n: int_id);
9503}
9504IM_MSVC_RUNTIME_CHECKS_RESTORE
9505
9506//-----------------------------------------------------------------------------
9507// [SECTION] INPUTS
9508//-----------------------------------------------------------------------------
9509// - GetModForLRModKey() [Internal]
9510// - FixupKeyChord() [Internal]
9511// - GetKeyData() [Internal]
9512// - GetKeyIndex() [Internal]
9513// - GetKeyName()
9514// - GetKeyChordName() [Internal]
9515// - CalcTypematicRepeatAmount() [Internal]
9516// - GetTypematicRepeatRate() [Internal]
9517// - GetKeyPressedAmount() [Internal]
9518// - GetKeyMagnitude2d() [Internal]
9519//-----------------------------------------------------------------------------
9520// - UpdateKeyRoutingTable() [Internal]
9521// - GetRoutingIdFromOwnerId() [Internal]
9522// - GetShortcutRoutingData() [Internal]
9523// - CalcRoutingScore() [Internal]
9524// - SetShortcutRouting() [Internal]
9525// - TestShortcutRouting() [Internal]
9526//-----------------------------------------------------------------------------
9527// - IsKeyDown()
9528// - IsKeyPressed()
9529// - IsKeyReleased()
9530//-----------------------------------------------------------------------------
9531// - IsMouseDown()
9532// - IsMouseClicked()
9533// - IsMouseReleased()
9534// - IsMouseDoubleClicked()
9535// - GetMouseClickedCount()
9536// - IsMouseHoveringRect() [Internal]
9537// - IsMouseDragPastThreshold() [Internal]
9538// - IsMouseDragging()
9539// - GetMousePos()
9540// - SetMousePos() [Internal]
9541// - GetMousePosOnOpeningCurrentPopup()
9542// - IsMousePosValid()
9543// - IsAnyMouseDown()
9544// - GetMouseDragDelta()
9545// - ResetMouseDragDelta()
9546// - GetMouseCursor()
9547// - SetMouseCursor()
9548//-----------------------------------------------------------------------------
9549// - UpdateAliasKey()
9550// - GetMergedModsFromKeys()
9551// - UpdateKeyboardInputs()
9552// - UpdateMouseInputs()
9553//-----------------------------------------------------------------------------
9554// - LockWheelingWindow [Internal]
9555// - FindBestWheelingWindow [Internal]
9556// - UpdateMouseWheel() [Internal]
9557//-----------------------------------------------------------------------------
9558// - SetNextFrameWantCaptureKeyboard()
9559// - SetNextFrameWantCaptureMouse()
9560//-----------------------------------------------------------------------------
9561// - GetInputSourceName() [Internal]
9562// - DebugPrintInputEvent() [Internal]
9563// - UpdateInputEvents() [Internal]
9564//-----------------------------------------------------------------------------
9565// - GetKeyOwner() [Internal]
9566// - TestKeyOwner() [Internal]
9567// - SetKeyOwner() [Internal]
9568// - SetItemKeyOwner() [Internal]
9569// - Shortcut() [Internal]
9570//-----------------------------------------------------------------------------
9571
9572static ImGuiKeyChord GetModForLRModKey(ImGuiKey key)
9573{
9574 if (key == ImGuiKey_LeftCtrl || key == ImGuiKey_RightCtrl)
9575 return ImGuiMod_Ctrl;
9576 if (key == ImGuiKey_LeftShift || key == ImGuiKey_RightShift)
9577 return ImGuiMod_Shift;
9578 if (key == ImGuiKey_LeftAlt || key == ImGuiKey_RightAlt)
9579 return ImGuiMod_Alt;
9580 if (key == ImGuiKey_LeftSuper || key == ImGuiKey_RightSuper)
9581 return ImGuiMod_Super;
9582 return ImGuiMod_None;
9583}
9584
9585ImGuiKeyChord ImGui::FixupKeyChord(ImGuiKeyChord key_chord)
9586{
9587 // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
9588 ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9589 if (IsLRModKey(key))
9590 key_chord |= GetModForLRModKey(key);
9591 return key_chord;
9592}
9593
9594ImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key)
9595{
9596 ImGuiContext& g = *ctx;
9597
9598 // Special storage location for mods
9599 if (key & ImGuiMod_Mask_)
9600 key = ConvertSingleModFlagToKey(key);
9601
9602 IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code.");
9603 return &g.IO.KeysData[key - ImGuiKey_NamedKey_BEGIN];
9604}
9605
9606// Those names a provided for debugging purpose and are not meant to be saved persistently not compared.
9607static const char* const GKeyNames[] =
9608{
9609 "Tab", "LeftArrow", "RightArrow", "UpArrow", "DownArrow", "PageUp", "PageDown",
9610 "Home", "End", "Insert", "Delete", "Backspace", "Space", "Enter", "Escape",
9611 "LeftCtrl", "LeftShift", "LeftAlt", "LeftSuper", "RightCtrl", "RightShift", "RightAlt", "RightSuper", "Menu",
9612 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H",
9613 "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
9614 "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
9615 "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24",
9616 "Apostrophe", "Comma", "Minus", "Period", "Slash", "Semicolon", "Equal", "LeftBracket",
9617 "Backslash", "RightBracket", "GraveAccent", "CapsLock", "ScrollLock", "NumLock", "PrintScreen",
9618 "Pause", "Keypad0", "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6",
9619 "Keypad7", "Keypad8", "Keypad9", "KeypadDecimal", "KeypadDivide", "KeypadMultiply",
9620 "KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual",
9621 "AppBack", "AppForward",
9622 "GamepadStart", "GamepadBack",
9623 "GamepadFaceLeft", "GamepadFaceRight", "GamepadFaceUp", "GamepadFaceDown",
9624 "GamepadDpadLeft", "GamepadDpadRight", "GamepadDpadUp", "GamepadDpadDown",
9625 "GamepadL1", "GamepadR1", "GamepadL2", "GamepadR2", "GamepadL3", "GamepadR3",
9626 "GamepadLStickLeft", "GamepadLStickRight", "GamepadLStickUp", "GamepadLStickDown",
9627 "GamepadRStickLeft", "GamepadRStickRight", "GamepadRStickUp", "GamepadRStickDown",
9628 "MouseLeft", "MouseRight", "MouseMiddle", "MouseX1", "MouseX2", "MouseWheelX", "MouseWheelY",
9629 "ModCtrl", "ModShift", "ModAlt", "ModSuper", // ReservedForModXXX are showing the ModXXX names.
9630};
9631IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames));
9632
9633const char* ImGui::GetKeyName(ImGuiKey key)
9634{
9635 if (key == ImGuiKey_None)
9636 return "None";
9637 IM_ASSERT(IsNamedKeyOrMod(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code.");
9638 if (key & ImGuiMod_Mask_)
9639 key = ConvertSingleModFlagToKey(key);
9640 if (!IsNamedKey(key))
9641 return "Unknown";
9642
9643 return GKeyNames[key - ImGuiKey_NamedKey_BEGIN];
9644}
9645
9646// Return untranslated names: on macOS, Cmd key will show as Ctrl, Ctrl key will show as super.
9647// Lifetime of return value: valid until next call to same function.
9648const char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord)
9649{
9650 ImGuiContext& g = *GImGui;
9651
9652 const ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9653 if (IsLRModKey(key))
9654 key_chord &= ~GetModForLRModKey(key); // Return "Ctrl+LeftShift" instead of "Ctrl+Shift+LeftShift"
9655 ImFormatString(buf: g.TempKeychordName, IM_ARRAYSIZE(g.TempKeychordName), fmt: "%s%s%s%s%s",
9656 (key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "",
9657 (key_chord & ImGuiMod_Shift) ? "Shift+" : "",
9658 (key_chord & ImGuiMod_Alt) ? "Alt+" : "",
9659 (key_chord & ImGuiMod_Super) ? "Super+" : "",
9660 (key != ImGuiKey_None || key_chord == ImGuiKey_None) ? GetKeyName(key) : "");
9661 size_t len;
9662 if (key == ImGuiKey_None && key_chord != 0)
9663 if ((len = strlen(s: g.TempKeychordName)) != 0) // Remove trailing '+'
9664 g.TempKeychordName[len - 1] = 0;
9665 return g.TempKeychordName;
9666}
9667
9668// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
9669// t1 = current time (e.g.: g.Time)
9670// An event is triggered at:
9671// t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N
9672int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
9673{
9674 if (t1 == 0.0f)
9675 return 1;
9676 if (t0 >= t1)
9677 return 0;
9678 if (repeat_rate <= 0.0f)
9679 return (t0 < repeat_delay) && (t1 >= repeat_delay);
9680 const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
9681 const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
9682 const int count = count_t1 - count_t0;
9683 return count;
9684}
9685
9686void ImGui::GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate)
9687{
9688 ImGuiContext& g = *GImGui;
9689 switch (flags & ImGuiInputFlags_RepeatRateMask_)
9690 {
9691 case ImGuiInputFlags_RepeatRateNavMove: *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.80f; return;
9692 case ImGuiInputFlags_RepeatRateNavTweak: *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.30f; return;
9693 case ImGuiInputFlags_RepeatRateDefault: default: *repeat_delay = g.IO.KeyRepeatDelay * 1.00f; *repeat_rate = g.IO.KeyRepeatRate * 1.00f; return;
9694 }
9695}
9696
9697// Return value representing the number of presses in the last time period, for the given repeat rate
9698// (most often returns 0 or 1. The result is generally only >1 when RepeatRate is smaller than DeltaTime, aka large DeltaTime or fast RepeatRate)
9699int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_rate)
9700{
9701 ImGuiContext& g = *GImGui;
9702 const ImGuiKeyData* key_data = GetKeyData(key);
9703 if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
9704 return 0;
9705 const float t = key_data->DownDuration;
9706 return CalcTypematicRepeatAmount(t0: t - g.IO.DeltaTime, t1: t, repeat_delay, repeat_rate);
9707}
9708
9709// Return 2D vector representing the combination of four cardinal direction, with analog value support (for e.g. ImGuiKey_GamepadLStick* values).
9710ImVec2 ImGui::GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down)
9711{
9712 return ImVec2(
9713 GetKeyData(key: key_right)->AnalogValue - GetKeyData(key: key_left)->AnalogValue,
9714 GetKeyData(key: key_down)->AnalogValue - GetKeyData(key: key_up)->AnalogValue);
9715}
9716
9717// Rewrite routing data buffers to strip old entries + sort by key to make queries not touch scattered data.
9718// Entries D,A,B,B,A,C,B --> A,A,B,B,B,C,D
9719// Index A:1 B:2 C:5 D:0 --> A:0 B:2 C:5 D:6
9720// See 'Metrics->Key Owners & Shortcut Routing' to visualize the result of that operation.
9721static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt)
9722{
9723 ImGuiContext& g = *GImGui;
9724 rt->EntriesNext.resize(new_size: 0);
9725 for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
9726 {
9727 const int new_routing_start_idx = rt->EntriesNext.Size;
9728 ImGuiKeyRoutingData* routing_entry;
9729 for (int old_routing_idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; old_routing_idx != -1; old_routing_idx = routing_entry->NextEntryIndex)
9730 {
9731 routing_entry = &rt->Entries[old_routing_idx];
9732 routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore;
9733 routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry
9734 routing_entry->RoutingNext = ImGuiKeyOwner_NoOwner;
9735 routing_entry->RoutingNextScore = 255;
9736 if (routing_entry->RoutingCurr == ImGuiKeyOwner_NoOwner)
9737 continue;
9738 rt->EntriesNext.push_back(v: *routing_entry); // Write alive ones into new buffer
9739
9740 // Apply routing to owner if there's no owner already (RoutingCurr == None at this point)
9741 // This is the result of previous frame's SetShortcutRouting() call.
9742 if (routing_entry->Mods == g.IO.KeyMods)
9743 {
9744 ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(ctx: &g, key);
9745 if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)
9746 {
9747 owner_data->OwnerCurr = routing_entry->RoutingCurr;
9748 //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X) via Routing\n", GetKeyName(key), routing_entry->RoutingCurr);
9749 }
9750 }
9751 }
9752
9753 // Rewrite linked-list
9754 rt->Index[key - ImGuiKey_NamedKey_BEGIN] = (ImGuiKeyRoutingIndex)(new_routing_start_idx < rt->EntriesNext.Size ? new_routing_start_idx : -1);
9755 for (int n = new_routing_start_idx; n < rt->EntriesNext.Size; n++)
9756 rt->EntriesNext[n].NextEntryIndex = (ImGuiKeyRoutingIndex)((n + 1 < rt->EntriesNext.Size) ? n + 1 : -1);
9757 }
9758 rt->Entries.swap(rhs&: rt->EntriesNext); // Swap new and old indexes
9759}
9760
9761// owner_id may be None/Any, but routing_id needs to be always be set, so we default to GetCurrentFocusScope().
9762static inline ImGuiID GetRoutingIdFromOwnerId(ImGuiID owner_id)
9763{
9764 ImGuiContext& g = *GImGui;
9765 return (owner_id != ImGuiKeyOwner_NoOwner && owner_id != ImGuiKeyOwner_Any) ? owner_id : g.CurrentFocusScopeId;
9766}
9767
9768ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord)
9769{
9770 // Majority of shortcuts will be Key + any number of Mods
9771 // We accept _Single_ mod with ImGuiKey_None.
9772 // - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl); // Legal
9773 // - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl | ImGuiMod_Shift); // Legal
9774 // - Shortcut(ImGuiMod_Ctrl); // Legal
9775 // - Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift); // Not legal
9776 ImGuiContext& g = *GImGui;
9777 ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
9778 ImGuiKeyRoutingData* routing_data;
9779 ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9780 ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
9781 if (key == ImGuiKey_None)
9782 key = ConvertSingleModFlagToKey(key: mods);
9783 IM_ASSERT(IsNamedKey(key));
9784
9785 // Get (in the majority of case, the linked list will have one element so this should be 2 reads.
9786 // Subsequent elements will be contiguous in memory as list is sorted/rebuilt in NewFrame).
9787 for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; idx = routing_data->NextEntryIndex)
9788 {
9789 routing_data = &rt->Entries[idx];
9790 if (routing_data->Mods == mods)
9791 return routing_data;
9792 }
9793
9794 // Add to linked-list
9795 ImGuiKeyRoutingIndex routing_data_idx = (ImGuiKeyRoutingIndex)rt->Entries.Size;
9796 rt->Entries.push_back(v: ImGuiKeyRoutingData());
9797 routing_data = &rt->Entries[routing_data_idx];
9798 routing_data->Mods = (ImU16)mods;
9799 routing_data->NextEntryIndex = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; // Setup linked list
9800 rt->Index[key - ImGuiKey_NamedKey_BEGIN] = routing_data_idx;
9801 return routing_data;
9802}
9803
9804// Current score encoding (lower is highest priority):
9805// - 0: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive
9806// - 1: ImGuiInputFlags_ActiveItem or ImGuiInputFlags_RouteFocused (if item active)
9807// - 2: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused
9808// - 3+: ImGuiInputFlags_RouteFocused (if window in focus-stack)
9809// - 254: ImGuiInputFlags_RouteGlobal
9810// - 255: never route
9811// 'flags' should include an explicit routing policy
9812static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags)
9813{
9814 ImGuiContext& g = *GImGui;
9815 if (flags & ImGuiInputFlags_RouteFocused)
9816 {
9817 // ActiveID gets top priority
9818 // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it)
9819 if (owner_id != 0 && g.ActiveId == owner_id)
9820 return 1;
9821
9822 // Score based on distance to focused window (lower is better)
9823 // Assuming both windows are submitting a routing request,
9824 // - When Window....... is focused -> Window scores 3 (best), Window/ChildB scores 255 (no match)
9825 // - When Window/ChildB is focused -> Window scores 4, Window/ChildB scores 3 (best)
9826 // Assuming only WindowA is submitting a routing request,
9827 // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score.
9828 // This essentially follow the window->ParentWindowForFocusRoute chain.
9829 if (focus_scope_id == 0)
9830 return 255;
9831 for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++)
9832 if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id)
9833 return 3 + index_in_focus_path;
9834 return 255;
9835 }
9836 else if (flags & ImGuiInputFlags_RouteActive)
9837 {
9838 if (owner_id != 0 && g.ActiveId == owner_id)
9839 return 1;
9840 return 255;
9841 }
9842 else if (flags & ImGuiInputFlags_RouteGlobal)
9843 {
9844 if (flags & ImGuiInputFlags_RouteOverActive)
9845 return 0;
9846 if (flags & ImGuiInputFlags_RouteOverFocused)
9847 return 2;
9848 return 254;
9849 }
9850 IM_ASSERT(0);
9851 return 0;
9852}
9853
9854// - We need this to filter some Shortcut() routes when an item e.g. an InputText() is active
9855// e.g. ImGuiKey_G won't be considered a shortcut when item is active, but ImGuiMod|ImGuiKey_G can be.
9856// - This is also used by UpdateInputEvents() to avoid trickling in the most common case of e.g. pressing ImGuiKey_G also emitting a G character.
9857static bool IsKeyChordPotentiallyCharInput(ImGuiKeyChord key_chord)
9858{
9859 // Mimic 'ignore_char_inputs' logic in InputText()
9860 ImGuiContext& g = *GImGui;
9861
9862 // When the right mods are pressed it cannot be a char input so we won't filter the shortcut out.
9863 ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
9864 const bool ignore_char_inputs = ((mods & ImGuiMod_Ctrl) && !(mods & ImGuiMod_Alt)) || (g.IO.ConfigMacOSXBehaviors && (mods & ImGuiMod_Ctrl));
9865 if (ignore_char_inputs)
9866 return false;
9867
9868 // Return true for A-Z, 0-9 and other keys associated to char inputs. Other keys such as F1-F12 won't be filtered.
9869 ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9870 if (key == ImGuiKey_None)
9871 return false;
9872 return g.KeysMayBeCharInput.TestBit(n: key);
9873}
9874
9875// Request a desired route for an input chord (key + mods).
9876// Return true if the route is available this frame.
9877// - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state.
9878// (Conceptually this does a "Submit for next frame" + "Test for current frame".
9879// As such, it could be called TrySetXXX or SubmitXXX, or the Submit and Test operations should be separate.)
9880bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
9881{
9882 ImGuiContext& g = *GImGui;
9883 if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
9884 flags |= ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut()
9885 else
9886 IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteTypeMask_)); // Check that only 1 routing flag is used
9887 IM_ASSERT(owner_id != ImGuiKeyOwner_Any && owner_id != ImGuiKeyOwner_NoOwner);
9888 if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused))
9889 IM_ASSERT(flags & ImGuiInputFlags_RouteGlobal);
9890
9891 // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
9892 key_chord = FixupKeyChord(key_chord);
9893
9894 // [DEBUG] Debug break requested by user
9895 if (g.DebugBreakInShortcutRouting == key_chord)
9896 IM_DEBUG_BREAK();
9897
9898 if (flags & ImGuiInputFlags_RouteUnlessBgFocused)
9899 if (g.NavWindow == NULL)
9900 return false;
9901
9902 // Note how ImGuiInputFlags_RouteAlways won't set routing and thus won't set owner. May want to rework this?
9903 if (flags & ImGuiInputFlags_RouteAlways)
9904 {
9905 IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> always, no register\n", GetKeyChordName(key_chord), flags, owner_id);
9906 return true;
9907 }
9908
9909 // Specific culling when there's an active item.
9910 if (g.ActiveId != 0 && g.ActiveId != owner_id)
9911 {
9912 if (flags & ImGuiInputFlags_RouteActive)
9913 return false;
9914
9915 // Cull shortcuts with no modifiers when it could generate a character.
9916 // e.g. Shortcut(ImGuiKey_G) also generates 'g' character, should not trigger when InputText() is active.
9917 // but Shortcut(Ctrl+G) should generally trigger when InputText() is active.
9918 // TL;DR: lettered shortcut with no mods or with only Alt mod will not trigger while an item reading text input is active.
9919 // (We cannot filter based on io.InputQueueCharacters[] contents because of trickling and key<>chars submission order are undefined)
9920 if (g.IO.WantTextInput && IsKeyChordPotentiallyCharInput(key_chord))
9921 {
9922 IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> filtered as potential char input\n", GetKeyChordName(key_chord), flags, owner_id);
9923 return false;
9924 }
9925
9926 // ActiveIdUsingAllKeyboardKeys trumps all for ActiveId
9927 if ((flags & ImGuiInputFlags_RouteOverActive) == 0 && g.ActiveIdUsingAllKeyboardKeys)
9928 {
9929 ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9930 if (key == ImGuiKey_None)
9931 key = ConvertSingleModFlagToKey(key: (ImGuiKey)(key_chord & ImGuiMod_Mask_));
9932 if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
9933 return false;
9934 }
9935 }
9936
9937 // Where do we evaluate route for?
9938 ImGuiID focus_scope_id = g.CurrentFocusScopeId;
9939 if (flags & ImGuiInputFlags_RouteFromRootWindow)
9940 focus_scope_id = g.CurrentWindow->RootWindow->ID; // See PushFocusScope() call in Begin()
9941
9942 const int score = CalcRoutingScore(focus_scope_id, owner_id, flags);
9943 IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d\n", GetKeyChordName(key_chord), flags, owner_id, score);
9944 if (score == 255)
9945 return false;
9946
9947 // Submit routing for NEXT frame (assuming score is sufficient)
9948 // FIXME: Could expose a way to use a "serve last" policy for same score resolution (using <= instead of <).
9949 ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord);
9950 //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score <= routing_data->RoutingNextScore) : (score < routing_data->RoutingNextScore);
9951 if (score < routing_data->RoutingNextScore)
9952 {
9953 routing_data->RoutingNext = owner_id;
9954 routing_data->RoutingNextScore = (ImU8)score;
9955 }
9956
9957 // Return routing state for CURRENT frame
9958 if (routing_data->RoutingCurr == owner_id)
9959 IMGUI_DEBUG_LOG_INPUTROUTING("--> granting current route\n");
9960 return routing_data->RoutingCurr == owner_id;
9961}
9962
9963// Currently unused by core (but used by tests)
9964// Note: this cannot be turned into GetShortcutRouting() because we do the owner_id->routing_id translation, name would be more misleading.
9965bool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id)
9966{
9967 const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id);
9968 key_chord = FixupKeyChord(key_chord);
9969 ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry.
9970 return routing_data->RoutingCurr == routing_id;
9971}
9972
9973// Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes.
9974// Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87)
9975bool ImGui::IsKeyDown(ImGuiKey key)
9976{
9977 return IsKeyDown(key, ImGuiKeyOwner_Any);
9978}
9979
9980bool ImGui::IsKeyDown(ImGuiKey key, ImGuiID owner_id)
9981{
9982 const ImGuiKeyData* key_data = GetKeyData(key);
9983 if (!key_data->Down)
9984 return false;
9985 if (!TestKeyOwner(key, owner_id))
9986 return false;
9987 return true;
9988}
9989
9990bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat)
9991{
9992 return IsKeyPressed(key, flags: repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
9993}
9994
9995// Important: unlike legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat.
9996bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id)
9997{
9998 const ImGuiKeyData* key_data = GetKeyData(key);
9999 if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
10000 return false;
10001 const float t = key_data->DownDuration;
10002 if (t < 0.0f)
10003 return false;
10004 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function!
10005 if (flags & (ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_)) // Setting any _RepeatXXX option enables _Repeat
10006 flags |= ImGuiInputFlags_Repeat;
10007
10008 bool pressed = (t == 0.0f);
10009 if (!pressed && (flags & ImGuiInputFlags_Repeat) != 0)
10010 {
10011 float repeat_delay, repeat_rate;
10012 GetTypematicRepeatRate(flags, repeat_delay: &repeat_delay, repeat_rate: &repeat_rate);
10013 pressed = (t > repeat_delay) && GetKeyPressedAmount(key, repeat_delay, repeat_rate) > 0;
10014 if (pressed && (flags & ImGuiInputFlags_RepeatUntilMask_))
10015 {
10016 // Slightly bias 'key_pressed_time' as DownDuration is an accumulation of DeltaTime which we compare to an absolute time value.
10017 // Ideally we'd replace DownDuration with KeyPressedTime but it would break user's code.
10018 ImGuiContext& g = *GImGui;
10019 double key_pressed_time = g.Time - t + 0.00001f;
10020 if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChange) && (g.LastKeyModsChangeTime > key_pressed_time))
10021 pressed = false;
10022 if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone) && (g.LastKeyModsChangeFromNoneTime > key_pressed_time))
10023 pressed = false;
10024 if ((flags & ImGuiInputFlags_RepeatUntilOtherKeyPress) && (g.LastKeyboardKeyPressTime > key_pressed_time))
10025 pressed = false;
10026 }
10027 }
10028 if (!pressed)
10029 return false;
10030 if (!TestKeyOwner(key, owner_id))
10031 return false;
10032 return true;
10033}
10034
10035bool ImGui::IsKeyReleased(ImGuiKey key)
10036{
10037 return IsKeyReleased(key, ImGuiKeyOwner_Any);
10038}
10039
10040bool ImGui::IsKeyReleased(ImGuiKey key, ImGuiID owner_id)
10041{
10042 const ImGuiKeyData* key_data = GetKeyData(key);
10043 if (key_data->DownDurationPrev < 0.0f || key_data->Down)
10044 return false;
10045 if (!TestKeyOwner(key, owner_id))
10046 return false;
10047 return true;
10048}
10049
10050bool ImGui::IsMouseDown(ImGuiMouseButton button)
10051{
10052 ImGuiContext& g = *GImGui;
10053 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10054 return g.IO.MouseDown[button] && TestKeyOwner(key: MouseButtonToKey(button), ImGuiKeyOwner_Any); // should be same as IsKeyDown(MouseButtonToKey(button), ImGuiKeyOwner_Any), but this allows legacy code hijacking the io.Mousedown[] array.
10055}
10056
10057bool ImGui::IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id)
10058{
10059 ImGuiContext& g = *GImGui;
10060 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10061 return g.IO.MouseDown[button] && TestKeyOwner(key: MouseButtonToKey(button), owner_id); // Should be same as IsKeyDown(MouseButtonToKey(button), owner_id), but this allows legacy code hijacking the io.Mousedown[] array.
10062}
10063
10064bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
10065{
10066 return IsMouseClicked(button, flags: repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
10067}
10068
10069bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id)
10070{
10071 ImGuiContext& g = *GImGui;
10072 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10073 if (!g.IO.MouseDown[button]) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
10074 return false;
10075 const float t = g.IO.MouseDownDuration[button];
10076 if (t < 0.0f)
10077 return false;
10078 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsMouseClicked) == 0); // Passing flags not supported by this function! // FIXME: Could support RepeatRate and RepeatUntil flags here.
10079
10080 const bool repeat = (flags & ImGuiInputFlags_Repeat) != 0;
10081 const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && CalcTypematicRepeatAmount(t0: t - g.IO.DeltaTime, t1: t, repeat_delay: g.IO.KeyRepeatDelay, repeat_rate: g.IO.KeyRepeatRate) > 0);
10082 if (!pressed)
10083 return false;
10084
10085 if (!TestKeyOwner(key: MouseButtonToKey(button), owner_id))
10086 return false;
10087
10088 return true;
10089}
10090
10091bool ImGui::IsMouseReleased(ImGuiMouseButton button)
10092{
10093 ImGuiContext& g = *GImGui;
10094 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10095 return g.IO.MouseReleased[button] && TestKeyOwner(key: MouseButtonToKey(button), ImGuiKeyOwner_Any); // Should be same as IsKeyReleased(MouseButtonToKey(button), ImGuiKeyOwner_Any)
10096}
10097
10098bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id)
10099{
10100 ImGuiContext& g = *GImGui;
10101 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10102 return g.IO.MouseReleased[button] && TestKeyOwner(key: MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id)
10103}
10104
10105bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
10106{
10107 ImGuiContext& g = *GImGui;
10108 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10109 return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(key: MouseButtonToKey(button), ImGuiKeyOwner_Any);
10110}
10111
10112bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id)
10113{
10114 ImGuiContext& g = *GImGui;
10115 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10116 return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(key: MouseButtonToKey(button), owner_id);
10117}
10118
10119int ImGui::GetMouseClickedCount(ImGuiMouseButton button)
10120{
10121 ImGuiContext& g = *GImGui;
10122 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10123 return g.IO.MouseClickedCount[button];
10124}
10125
10126// Test if mouse cursor is hovering given rectangle
10127// NB- Rectangle is clipped by our current clip setting
10128// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
10129bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
10130{
10131 ImGuiContext& g = *GImGui;
10132
10133 // Clip
10134 ImRect rect_clipped(r_min, r_max);
10135 if (clip)
10136 rect_clipped.ClipWith(r: g.CurrentWindow->ClipRect);
10137
10138 // Hit testing, expanded for touch input
10139 if (!rect_clipped.ContainsWithPad(p: g.IO.MousePos, pad: g.Style.TouchExtraPadding))
10140 return false;
10141 if (!g.MouseViewport->GetMainRect().Overlaps(r: rect_clipped))
10142 return false;
10143 return true;
10144}
10145
10146// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
10147// [Internal] This doesn't test if the button is pressed
10148bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
10149{
10150 ImGuiContext& g = *GImGui;
10151 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10152 if (lock_threshold < 0.0f)
10153 lock_threshold = g.IO.MouseDragThreshold;
10154 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
10155}
10156
10157bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
10158{
10159 ImGuiContext& g = *GImGui;
10160 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10161 if (!g.IO.MouseDown[button])
10162 return false;
10163 return IsMouseDragPastThreshold(button, lock_threshold);
10164}
10165
10166ImVec2 ImGui::GetMousePos()
10167{
10168 ImGuiContext& g = *GImGui;
10169 return g.IO.MousePos;
10170}
10171
10172// This is called TeleportMousePos() and not SetMousePos() to emphasis that setting MousePosPrev will effectively clear mouse delta as well.
10173// It is expected you only call this if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) is set and supported by backend.
10174void ImGui::TeleportMousePos(const ImVec2& pos)
10175{
10176 ImGuiContext& g = *GImGui;
10177 g.IO.MousePos = g.IO.MousePosPrev = pos;
10178 g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
10179 g.IO.WantSetMousePos = true;
10180 //IMGUI_DEBUG_LOG_IO("TeleportMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y);
10181}
10182
10183// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
10184ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
10185{
10186 ImGuiContext& g = *GImGui;
10187 if (g.BeginPopupStack.Size > 0)
10188 return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
10189 return g.IO.MousePos;
10190}
10191
10192// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
10193bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
10194{
10195 // The assert is only to silence a false-positive in XCode Static Analysis.
10196 // 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).
10197 IM_ASSERT(GImGui != NULL);
10198 const float MOUSE_INVALID = -256000.0f;
10199 ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
10200 return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
10201}
10202
10203// [WILL OBSOLETE] This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid.
10204bool ImGui::IsAnyMouseDown()
10205{
10206 ImGuiContext& g = *GImGui;
10207 for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
10208 if (g.IO.MouseDown[n])
10209 return true;
10210 return false;
10211}
10212
10213// Return the delta from the initial clicking position while the mouse button is clicked or was just released.
10214// This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
10215// NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.
10216ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
10217{
10218 ImGuiContext& g = *GImGui;
10219 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10220 if (lock_threshold < 0.0f)
10221 lock_threshold = g.IO.MouseDragThreshold;
10222 if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
10223 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
10224 if (IsMousePosValid(mouse_pos: &g.IO.MousePos) && IsMousePosValid(mouse_pos: &g.IO.MouseClickedPos[button]))
10225 return g.IO.MousePos - g.IO.MouseClickedPos[button];
10226 return ImVec2(0.0f, 0.0f);
10227}
10228
10229void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
10230{
10231 ImGuiContext& g = *GImGui;
10232 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10233 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
10234 g.IO.MouseClickedPos[button] = g.IO.MousePos;
10235}
10236
10237// Get desired mouse cursor shape.
10238// Important: this is meant to be used by a platform backend, it is reset in ImGui::NewFrame(),
10239// updated during the frame, and locked in EndFrame()/Render().
10240// If you use software rendering by setting io.MouseDrawCursor then Dear ImGui will render those for you
10241ImGuiMouseCursor ImGui::GetMouseCursor()
10242{
10243 ImGuiContext& g = *GImGui;
10244 return g.MouseCursor;
10245}
10246
10247// We intentionally accept values of ImGuiMouseCursor that are outside our bounds, in case users needs to hack-in a custom cursor value.
10248// Custom cursors may be handled by custom backends. If you are using a standard backend and want to hack in a custom cursor, you may
10249// handle it before the backend _NewFrame() call and temporarily set ImGuiConfigFlags_NoMouseCursorChange during the backend _NewFrame() call.
10250void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
10251{
10252 ImGuiContext& g = *GImGui;
10253 g.MouseCursor = cursor_type;
10254}
10255
10256static void UpdateAliasKey(ImGuiKey key, bool v, float analog_value)
10257{
10258 IM_ASSERT(ImGui::IsAliasKey(key));
10259 ImGuiKeyData* key_data = ImGui::GetKeyData(key);
10260 key_data->Down = v;
10261 key_data->AnalogValue = analog_value;
10262}
10263
10264// [Internal] Do not use directly
10265static ImGuiKeyChord GetMergedModsFromKeys()
10266{
10267 ImGuiKeyChord mods = 0;
10268 if (ImGui::IsKeyDown(key: ImGuiMod_Ctrl)) { mods |= ImGuiMod_Ctrl; }
10269 if (ImGui::IsKeyDown(key: ImGuiMod_Shift)) { mods |= ImGuiMod_Shift; }
10270 if (ImGui::IsKeyDown(key: ImGuiMod_Alt)) { mods |= ImGuiMod_Alt; }
10271 if (ImGui::IsKeyDown(key: ImGuiMod_Super)) { mods |= ImGuiMod_Super; }
10272 return mods;
10273}
10274
10275static void ImGui::UpdateKeyboardInputs()
10276{
10277 ImGuiContext& g = *GImGui;
10278 ImGuiIO& io = g.IO;
10279
10280 if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
10281 io.ClearInputKeys();
10282
10283 // Update aliases
10284 for (int n = 0; n < ImGuiMouseButton_COUNT; n++)
10285 UpdateAliasKey(key: MouseButtonToKey(button: n), v: io.MouseDown[n], analog_value: io.MouseDown[n] ? 1.0f : 0.0f);
10286 UpdateAliasKey(key: ImGuiKey_MouseWheelX, v: io.MouseWheelH != 0.0f, analog_value: io.MouseWheelH);
10287 UpdateAliasKey(key: ImGuiKey_MouseWheelY, v: io.MouseWheel != 0.0f, analog_value: io.MouseWheel);
10288
10289 // Synchronize io.KeyMods and io.KeyCtrl/io.KeyShift/etc. values.
10290 // - New backends (1.87+): send io.AddKeyEvent(ImGuiMod_XXX) -> -> (here) deriving io.KeyMods + io.KeyXXX from key array.
10291 // - Legacy backends: set io.KeyXXX bools -> (above) set key array from io.KeyXXX -> (here) deriving io.KeyMods + io.KeyXXX from key array.
10292 // So with legacy backends the 4 values will do a unnecessary back-and-forth but it makes the code simpler and future facing.
10293 const ImGuiKeyChord prev_key_mods = io.KeyMods;
10294 io.KeyMods = GetMergedModsFromKeys();
10295 io.KeyCtrl = (io.KeyMods & ImGuiMod_Ctrl) != 0;
10296 io.KeyShift = (io.KeyMods & ImGuiMod_Shift) != 0;
10297 io.KeyAlt = (io.KeyMods & ImGuiMod_Alt) != 0;
10298 io.KeySuper = (io.KeyMods & ImGuiMod_Super) != 0;
10299 if (prev_key_mods != io.KeyMods)
10300 g.LastKeyModsChangeTime = g.Time;
10301 if (prev_key_mods != io.KeyMods && prev_key_mods == 0)
10302 g.LastKeyModsChangeFromNoneTime = g.Time;
10303
10304 // Clear gamepad data if disabled
10305 if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0)
10306 for (int key = ImGuiKey_Gamepad_BEGIN; key < ImGuiKey_Gamepad_END; key++)
10307 {
10308 io.KeysData[key - ImGuiKey_NamedKey_BEGIN].Down = false;
10309 io.KeysData[key - ImGuiKey_NamedKey_BEGIN].AnalogValue = 0.0f;
10310 }
10311
10312 // Update keys
10313 for (int key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key++)
10314 {
10315 ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_NamedKey_BEGIN];
10316 key_data->DownDurationPrev = key_data->DownDuration;
10317 key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f;
10318 if (key_data->DownDuration == 0.0f)
10319 {
10320 if (IsKeyboardKey(key: (ImGuiKey)key))
10321 g.LastKeyboardKeyPressTime = g.Time;
10322 else if (key == ImGuiKey_ReservedForModCtrl || key == ImGuiKey_ReservedForModShift || key == ImGuiKey_ReservedForModAlt || key == ImGuiKey_ReservedForModSuper)
10323 g.LastKeyboardKeyPressTime = g.Time;
10324 }
10325 }
10326
10327 // Update keys/input owner (named keys only): one entry per key
10328 for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
10329 {
10330 ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_NamedKey_BEGIN];
10331 ImGuiKeyOwnerData* owner_data = &g.KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN];
10332 owner_data->OwnerCurr = owner_data->OwnerNext;
10333 if (!key_data->Down) // Important: ownership is released on the frame after a release. Ensure a 'MouseDown -> CloseWindow -> MouseUp' chain doesn't lead to someone else seeing the MouseUp.
10334 owner_data->OwnerNext = ImGuiKeyOwner_NoOwner;
10335 owner_data->LockThisFrame = owner_data->LockUntilRelease = owner_data->LockUntilRelease && key_data->Down; // Clear LockUntilRelease when key is not Down anymore
10336 }
10337
10338 // Update key routing (for e.g. shortcuts)
10339 UpdateKeyRoutingTable(rt: &g.KeysRoutingTable);
10340}
10341
10342static void ImGui::UpdateMouseInputs()
10343{
10344 ImGuiContext& g = *GImGui;
10345 ImGuiIO& io = g.IO;
10346
10347 // Mouse Wheel swapping flag
10348 // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead
10349 // - We avoid doing it on OSX as it the OS input layer handles this already.
10350 // - FIXME: However this means when running on OSX over Emscripten, Shift+WheelY will incur two swapping (1 in OS, 1 here), canceling the feature.
10351 // - FIXME: When we can distinguish e.g. touchpad scroll events from mouse ones, we'll set this accordingly based on input source.
10352 io.MouseWheelRequestAxisSwap = io.KeyShift && !io.ConfigMacOSXBehaviors;
10353
10354 // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
10355 if (IsMousePosValid(mouse_pos: &io.MousePos))
10356 io.MousePos = g.MouseLastValidPos = ImFloor(v: io.MousePos);
10357
10358 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
10359 if (IsMousePosValid(mouse_pos: &io.MousePos) && IsMousePosValid(mouse_pos: &io.MousePosPrev))
10360 io.MouseDelta = io.MousePos - io.MousePosPrev;
10361 else
10362 io.MouseDelta = ImVec2(0.0f, 0.0f);
10363
10364 // Update stationary timer.
10365 // FIXME: May need to rework again to have some tolerance for occasional small movement, while being functional on high-framerates.
10366 const float mouse_stationary_threshold = (io.MouseSource == ImGuiMouseSource_Mouse) ? 2.0f : 3.0f; // Slightly higher threshold for ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen, may need rework.
10367 const bool mouse_stationary = (ImLengthSqr(lhs: io.MouseDelta) <= mouse_stationary_threshold * mouse_stationary_threshold);
10368 g.MouseStationaryTimer = mouse_stationary ? (g.MouseStationaryTimer + io.DeltaTime) : 0.0f;
10369 //IMGUI_DEBUG_LOG("%.4f\n", g.MouseStationaryTimer);
10370
10371 // If mouse moved we re-enable mouse hovering in case it was disabled by keyboard/gamepad. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true.
10372 if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)
10373 g.NavHighlightItemUnderNav = false;
10374
10375 for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
10376 {
10377 io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f;
10378 io.MouseClickedCount[i] = 0; // Will be filled below
10379 io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f;
10380 io.MouseDownDurationPrev[i] = io.MouseDownDuration[i];
10381 io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f;
10382 if (io.MouseClicked[i])
10383 {
10384 bool is_repeated_click = false;
10385 if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime)
10386 {
10387 ImVec2 delta_from_click_pos = IsMousePosValid(mouse_pos: &io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
10388 if (ImLengthSqr(lhs: delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist)
10389 is_repeated_click = true;
10390 }
10391 if (is_repeated_click)
10392 io.MouseClickedLastCount[i]++;
10393 else
10394 io.MouseClickedLastCount[i] = 1;
10395 io.MouseClickedTime[i] = g.Time;
10396 io.MouseClickedPos[i] = io.MousePos;
10397 io.MouseClickedCount[i] = io.MouseClickedLastCount[i];
10398 io.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
10399 io.MouseDragMaxDistanceSqr[i] = 0.0f;
10400 }
10401 else if (io.MouseDown[i])
10402 {
10403 // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
10404 ImVec2 delta_from_click_pos = IsMousePosValid(mouse_pos: &io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
10405 io.MouseDragMaxDistanceSqr[i] = ImMax(lhs: io.MouseDragMaxDistanceSqr[i], rhs: ImLengthSqr(lhs: delta_from_click_pos));
10406 io.MouseDragMaxDistanceAbs[i].x = ImMax(lhs: io.MouseDragMaxDistanceAbs[i].x, rhs: delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x);
10407 io.MouseDragMaxDistanceAbs[i].y = ImMax(lhs: io.MouseDragMaxDistanceAbs[i].y, rhs: delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y);
10408 }
10409
10410 // We provide io.MouseDoubleClicked[] as a legacy service
10411 io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2);
10412
10413 // Clicking any mouse button reactivate mouse hovering which may have been deactivated by keyboard/gamepad navigation
10414 if (io.MouseClicked[i])
10415 g.NavHighlightItemUnderNav = false;
10416 }
10417}
10418
10419static void LockWheelingWindow(ImGuiWindow* window, float wheel_amount)
10420{
10421 ImGuiContext& g = *GImGui;
10422 if (window)
10423 g.WheelingWindowReleaseTimer = ImMin(lhs: g.WheelingWindowReleaseTimer + ImAbs(x: wheel_amount) * WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER, rhs: WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER);
10424 else
10425 g.WheelingWindowReleaseTimer = 0.0f;
10426 if (g.WheelingWindow == window)
10427 return;
10428 IMGUI_DEBUG_LOG_IO("[io] LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL");
10429 g.WheelingWindow = window;
10430 g.WheelingWindowRefMousePos = g.IO.MousePos;
10431 if (window == NULL)
10432 {
10433 g.WheelingWindowStartFrame = -1;
10434 g.WheelingAxisAvg = ImVec2(0.0f, 0.0f);
10435 }
10436}
10437
10438static ImGuiWindow* FindBestWheelingWindow(const ImVec2& wheel)
10439{
10440 // For each axis, find window in the hierarchy that may want to use scrolling
10441 ImGuiContext& g = *GImGui;
10442 ImGuiWindow* windows[2] = { NULL, NULL };
10443 for (int axis = 0; axis < 2; axis++)
10444 if (wheel[axis] != 0.0f)
10445 for (ImGuiWindow* window = windows[axis] = g.HoveredWindow; window->Flags & ImGuiWindowFlags_ChildWindow; window = windows[axis] = window->ParentWindow)
10446 {
10447 // Bubble up into parent window if:
10448 // - a child window doesn't allow any scrolling.
10449 // - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag.
10450 //// - a child window doesn't need scrolling because it is already at the edge for the direction we are going in (FIXME-WIP)
10451 const bool has_scrolling = (window->ScrollMax[axis] != 0.0f);
10452 const bool inputs_disabled = (window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs);
10453 //const bool scrolling_past_limits = (wheel_v < 0.0f) ? (window->Scroll[axis] <= 0.0f) : (window->Scroll[axis] >= window->ScrollMax[axis]);
10454 if (has_scrolling && !inputs_disabled) // && !scrolling_past_limits)
10455 break; // select this window
10456 }
10457 if (windows[0] == NULL && windows[1] == NULL)
10458 return NULL;
10459
10460 // If there's only one window or only one axis then there's no ambiguity
10461 if (windows[0] == windows[1] || windows[0] == NULL || windows[1] == NULL)
10462 return windows[1] ? windows[1] : windows[0];
10463
10464 // If candidate are different windows we need to decide which one to prioritize
10465 // - First frame: only find a winner if one axis is zero.
10466 // - Subsequent frames: only find a winner when one is more than the other.
10467 if (g.WheelingWindowStartFrame == -1)
10468 g.WheelingWindowStartFrame = g.FrameCount;
10469 if ((g.WheelingWindowStartFrame == g.FrameCount && wheel.x != 0.0f && wheel.y != 0.0f) || (g.WheelingAxisAvg.x == g.WheelingAxisAvg.y))
10470 {
10471 g.WheelingWindowWheelRemainder = wheel;
10472 return NULL;
10473 }
10474 return (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? windows[0] : windows[1];
10475}
10476
10477// Called by NewFrame()
10478void ImGui::UpdateMouseWheel()
10479{
10480 // Reset the locked window if we move the mouse or after the timer elapses.
10481 // FIXME: Ideally we could refactor to have one timer for "changing window w/ same axis" and a shorter timer for "changing window or axis w/ other axis" (#3795)
10482 ImGuiContext& g = *GImGui;
10483 if (g.WheelingWindow != NULL)
10484 {
10485 g.WheelingWindowReleaseTimer -= g.IO.DeltaTime;
10486 if (IsMousePosValid() && ImLengthSqr(lhs: g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
10487 g.WheelingWindowReleaseTimer = 0.0f;
10488 if (g.WheelingWindowReleaseTimer <= 0.0f)
10489 LockWheelingWindow(NULL, wheel_amount: 0.0f);
10490 }
10491
10492 ImVec2 wheel;
10493 wheel.x = TestKeyOwner(key: ImGuiKey_MouseWheelX, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheelH : 0.0f;
10494 wheel.y = TestKeyOwner(key: ImGuiKey_MouseWheelY, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheel : 0.0f;
10495
10496 //IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y);
10497 ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
10498 if (!mouse_window || mouse_window->Collapsed)
10499 return;
10500
10501 // Zoom / Scale window
10502 // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
10503 if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
10504 {
10505 LockWheelingWindow(window: mouse_window, wheel_amount: wheel.y);
10506 ImGuiWindow* window = mouse_window;
10507 const float new_font_scale = ImClamp(v: window->FontWindowScale + g.IO.MouseWheel * 0.10f, mn: 0.50f, mx: 2.50f);
10508 const float scale = new_font_scale / window->FontWindowScale;
10509 window->FontWindowScale = new_font_scale;
10510 if (window == window->RootWindow)
10511 {
10512 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
10513 SetWindowPos(window, pos: window->Pos + offset, cond: 0);
10514 window->Size = ImTrunc(v: window->Size * scale);
10515 window->SizeFull = ImTrunc(v: window->SizeFull * scale);
10516 }
10517 return;
10518 }
10519 if (g.IO.KeyCtrl)
10520 return;
10521
10522 // Mouse wheel scrolling
10523 // Read about io.MouseWheelRequestAxisSwap and its issue on Mac+Emscripten in UpdateMouseInputs()
10524 if (g.IO.MouseWheelRequestAxisSwap)
10525 wheel = ImVec2(wheel.y, 0.0f);
10526
10527 // Maintain a rough average of moving magnitude on both axises
10528 // FIXME: should by based on wall clock time rather than frame-counter
10529 g.WheelingAxisAvg.x = ImExponentialMovingAverage(avg: g.WheelingAxisAvg.x, sample: ImAbs(x: wheel.x), n: 30);
10530 g.WheelingAxisAvg.y = ImExponentialMovingAverage(avg: g.WheelingAxisAvg.y, sample: ImAbs(x: wheel.y), n: 30);
10531
10532 // In the rare situation where FindBestWheelingWindow() had to defer first frame of wheeling due to ambiguous main axis, reinject it now.
10533 wheel += g.WheelingWindowWheelRemainder;
10534 g.WheelingWindowWheelRemainder = ImVec2(0.0f, 0.0f);
10535 if (wheel.x == 0.0f && wheel.y == 0.0f)
10536 return;
10537
10538 // Mouse wheel scrolling: find target and apply
10539 // - don't renew lock if axis doesn't apply on the window.
10540 // - select a main axis when both axises are being moved.
10541 if (ImGuiWindow* window = (g.WheelingWindow ? g.WheelingWindow : FindBestWheelingWindow(wheel)))
10542 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
10543 {
10544 bool do_scroll[2] = { wheel.x != 0.0f && window->ScrollMax.x != 0.0f, wheel.y != 0.0f && window->ScrollMax.y != 0.0f };
10545 if (do_scroll[ImGuiAxis_X] && do_scroll[ImGuiAxis_Y])
10546 do_scroll[(g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? ImGuiAxis_Y : ImGuiAxis_X] = false;
10547 if (do_scroll[ImGuiAxis_X])
10548 {
10549 LockWheelingWindow(window, wheel_amount: wheel.x);
10550 float max_step = window->InnerRect.GetWidth() * 0.67f;
10551 float scroll_step = ImTrunc(f: ImMin(lhs: 2 * window->CalcFontSize(), rhs: max_step));
10552 SetScrollX(window, scroll_x: window->Scroll.x - wheel.x * scroll_step);
10553 g.WheelingWindowScrolledFrame = g.FrameCount;
10554 }
10555 if (do_scroll[ImGuiAxis_Y])
10556 {
10557 LockWheelingWindow(window, wheel_amount: wheel.y);
10558 float max_step = window->InnerRect.GetHeight() * 0.67f;
10559 float scroll_step = ImTrunc(f: ImMin(lhs: 5 * window->CalcFontSize(), rhs: max_step));
10560 SetScrollY(window, scroll_y: window->Scroll.y - wheel.y * scroll_step);
10561 g.WheelingWindowScrolledFrame = g.FrameCount;
10562 }
10563 }
10564}
10565
10566void ImGui::SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard)
10567{
10568 ImGuiContext& g = *GImGui;
10569 g.WantCaptureKeyboardNextFrame = want_capture_keyboard ? 1 : 0;
10570}
10571
10572void ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse)
10573{
10574 ImGuiContext& g = *GImGui;
10575 g.WantCaptureMouseNextFrame = want_capture_mouse ? 1 : 0;
10576}
10577
10578#ifndef IMGUI_DISABLE_DEBUG_TOOLS
10579static const char* GetInputSourceName(ImGuiInputSource source)
10580{
10581 const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad" };
10582 IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT && source >= 0 && source < ImGuiInputSource_COUNT);
10583 return input_source_names[source];
10584}
10585static const char* GetMouseSourceName(ImGuiMouseSource source)
10586{
10587 const char* mouse_source_names[] = { "Mouse", "TouchScreen", "Pen" };
10588 IM_ASSERT(IM_ARRAYSIZE(mouse_source_names) == ImGuiMouseSource_COUNT && source >= 0 && source < ImGuiMouseSource_COUNT);
10589 return mouse_source_names[source];
10590}
10591static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e)
10592{
10593 ImGuiContext& g = *GImGui;
10594 if (e->Type == ImGuiInputEventType_MousePos) { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix); else IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (%.1f, %.1f) (%s)\n", prefix, e->MousePos.PosX, e->MousePos.PosY, GetMouseSourceName(e->MousePos.MouseSource)); return; }
10595 if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseButton %d %s (%s)\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up", GetMouseSourceName(e->MouseButton.MouseSource)); return; }
10596 if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseWheel (%.3f, %.3f) (%s)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY, GetMouseSourceName(e->MouseWheel.MouseSource)); return; }
10597 if (e->Type == ImGuiInputEventType_MouseViewport){IMGUI_DEBUG_LOG_IO("[io] %s: MouseViewport (0x%08X)\n", prefix, e->MouseViewport.HoveredViewportID); return; }
10598 if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG_IO("[io] %s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; }
10599 if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG_IO("[io] %s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; }
10600 if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG_IO("[io] %s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; }
10601}
10602#endif
10603
10604// Process input queue
10605// We always call this with the value of 'bool g.IO.ConfigInputTrickleEventQueue'.
10606// - trickle_fast_inputs = false : process all events, turn into flattened input state (e.g. successive down/up/down/up will be lost)
10607// - trickle_fast_inputs = true : process as many events as possible (successive down/up/down/up will be trickled over several frames so nothing is lost) (new feature in 1.87)
10608void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
10609{
10610 ImGuiContext& g = *GImGui;
10611 ImGuiIO& io = g.IO;
10612
10613 // Only trickle chars<>key when working with InputText()
10614 // FIXME: InputText() could parse event trail?
10615 // FIXME: Could specialize chars<>keys trickling rules for control keys (those not typically associated to characters)
10616 const bool trickle_interleaved_nonchar_keys_and_text = (trickle_fast_inputs && g.WantTextInputNextFrame == 1);
10617
10618 bool mouse_moved = false, mouse_wheeled = false, key_changed = false, key_changed_nonchar = false, text_inputted = false;
10619 int mouse_button_changed = 0x00;
10620 ImBitArray<ImGuiKey_NamedKey_COUNT> key_changed_mask;
10621
10622 int event_n = 0;
10623 for (; event_n < g.InputEventsQueue.Size; event_n++)
10624 {
10625 ImGuiInputEvent* e = &g.InputEventsQueue[event_n];
10626 if (e->Type == ImGuiInputEventType_MousePos)
10627 {
10628 if (g.IO.WantSetMousePos)
10629 continue;
10630 // Trickling Rule: Stop processing queued events if we already handled a mouse button change
10631 ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY);
10632 if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted))
10633 break;
10634 io.MousePos = event_pos;
10635 io.MouseSource = e->MousePos.MouseSource;
10636 mouse_moved = true;
10637 }
10638 else if (e->Type == ImGuiInputEventType_MouseButton)
10639 {
10640 // Trickling Rule: Stop processing queued events if we got multiple action on the same button
10641 const ImGuiMouseButton button = e->MouseButton.Button;
10642 IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT);
10643 if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled))
10644 break;
10645 if (trickle_fast_inputs && e->MouseButton.MouseSource == ImGuiMouseSource_TouchScreen && mouse_moved) // #2702: TouchScreen have no initial hover.
10646 break;
10647 io.MouseDown[button] = e->MouseButton.Down;
10648 io.MouseSource = e->MouseButton.MouseSource;
10649 mouse_button_changed |= (1 << button);
10650 }
10651 else if (e->Type == ImGuiInputEventType_MouseWheel)
10652 {
10653 // Trickling Rule: Stop processing queued events if we got multiple action on the event
10654 if (trickle_fast_inputs && (mouse_moved || mouse_button_changed != 0))
10655 break;
10656 io.MouseWheelH += e->MouseWheel.WheelX;
10657 io.MouseWheel += e->MouseWheel.WheelY;
10658 io.MouseSource = e->MouseWheel.MouseSource;
10659 mouse_wheeled = true;
10660 }
10661 else if (e->Type == ImGuiInputEventType_MouseViewport)
10662 {
10663 io.MouseHoveredViewport = e->MouseViewport.HoveredViewportID;
10664 }
10665 else if (e->Type == ImGuiInputEventType_Key)
10666 {
10667 // Trickling Rule: Stop processing queued events if we got multiple action on the same button
10668 if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
10669 continue;
10670 ImGuiKey key = e->Key.Key;
10671 IM_ASSERT(key != ImGuiKey_None);
10672 ImGuiKeyData* key_data = GetKeyData(key);
10673 const int key_data_index = (int)(key_data - g.IO.KeysData);
10674 if (trickle_fast_inputs && key_data->Down != e->Key.Down && (key_changed_mask.TestBit(n: key_data_index) || mouse_button_changed != 0))
10675 break;
10676
10677 const bool key_is_potentially_for_char_input = IsKeyChordPotentiallyCharInput(key_chord: GetMergedModsFromKeys() | key);
10678 if (trickle_interleaved_nonchar_keys_and_text && (text_inputted && !key_is_potentially_for_char_input))
10679 break;
10680
10681 key_data->Down = e->Key.Down;
10682 key_data->AnalogValue = e->Key.AnalogValue;
10683 key_changed = true;
10684 key_changed_mask.SetBit(key_data_index);
10685 if (trickle_interleaved_nonchar_keys_and_text && !key_is_potentially_for_char_input)
10686 key_changed_nonchar = true;
10687 }
10688 else if (e->Type == ImGuiInputEventType_Text)
10689 {
10690 if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
10691 continue;
10692 // Trickling Rule: Stop processing queued events if keys/mouse have been interacted with
10693 if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_moved || mouse_wheeled))
10694 break;
10695 if (trickle_interleaved_nonchar_keys_and_text && key_changed_nonchar)
10696 break;
10697 unsigned int c = e->Text.Char;
10698 io.InputQueueCharacters.push_back(v: c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
10699 if (trickle_interleaved_nonchar_keys_and_text)
10700 text_inputted = true;
10701 }
10702 else if (e->Type == ImGuiInputEventType_Focus)
10703 {
10704 // We intentionally overwrite this and process in NewFrame(), in order to give a chance
10705 // to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame.
10706 const bool focus_lost = !e->AppFocused.Focused;
10707 io.AppFocusLost = focus_lost;
10708 }
10709 else
10710 {
10711 IM_ASSERT(0 && "Unknown event!");
10712 }
10713 }
10714
10715 // Record trail (for domain-specific applications wanting to access a precise trail)
10716 //if (event_n != 0) IMGUI_DEBUG_LOG_IO("Processed: %d / Remaining: %d\n", event_n, g.InputEventsQueue.Size - event_n);
10717 for (int n = 0; n < event_n; n++)
10718 g.InputEventsTrail.push_back(v: g.InputEventsQueue[n]);
10719
10720 // [DEBUG]
10721#ifndef IMGUI_DISABLE_DEBUG_TOOLS
10722 if (event_n != 0 && (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO))
10723 for (int n = 0; n < g.InputEventsQueue.Size; n++)
10724 DebugPrintInputEvent(prefix: n < event_n ? "Processed" : "Remaining", e: &g.InputEventsQueue[n]);
10725#endif
10726
10727 // Remaining events will be processed on the next frame
10728 if (event_n == g.InputEventsQueue.Size)
10729 g.InputEventsQueue.resize(new_size: 0);
10730 else
10731 g.InputEventsQueue.erase(it: g.InputEventsQueue.Data, it_last: g.InputEventsQueue.Data + event_n);
10732
10733 // Clear buttons state when focus is lost
10734 // - this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle.
10735 // - we clear in EndFrame() and not now in order allow application/user code polling this flag
10736 // (e.g. custom backend may want to clear additional data, custom widgets may want to react with a "canceling" event).
10737 if (g.IO.AppFocusLost)
10738 {
10739 g.IO.ClearInputKeys();
10740 g.IO.ClearInputMouse();
10741 }
10742}
10743
10744ImGuiID ImGui::GetKeyOwner(ImGuiKey key)
10745{
10746 if (!IsNamedKeyOrMod(key))
10747 return ImGuiKeyOwner_NoOwner;
10748
10749 ImGuiContext& g = *GImGui;
10750 ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(ctx: &g, key);
10751 ImGuiID owner_id = owner_data->OwnerCurr;
10752
10753 if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)
10754 if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
10755 return ImGuiKeyOwner_NoOwner;
10756
10757 return owner_id;
10758}
10759
10760// TestKeyOwner(..., ID) : (owner == None || owner == ID)
10761// TestKeyOwner(..., None) : (owner == None)
10762// TestKeyOwner(..., Any) : no owner test
10763// All paths are also testing for key not being locked, for the rare cases that key have been locked with using ImGuiInputFlags_LockXXX flags.
10764bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id)
10765{
10766 if (!IsNamedKeyOrMod(key))
10767 return true;
10768
10769 ImGuiContext& g = *GImGui;
10770 if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)
10771 if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
10772 return false;
10773
10774 ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(ctx: &g, key);
10775 if (owner_id == ImGuiKeyOwner_Any)
10776 return (owner_data->LockThisFrame == false);
10777
10778 // Note: SetKeyOwner() sets OwnerCurr. It is not strictly required for most mouse routing overlap (because of ActiveId/HoveredId
10779 // are acting as filter before this has a chance to filter), but sane as soon as user tries to look into things.
10780 // Setting OwnerCurr in SetKeyOwner() is more consistent than testing OwnerNext here: would be inconsistent with getter and other functions.
10781 if (owner_data->OwnerCurr != owner_id)
10782 {
10783 if (owner_data->LockThisFrame)
10784 return false;
10785 if (owner_data->OwnerCurr != ImGuiKeyOwner_NoOwner)
10786 return false;
10787 }
10788
10789 return true;
10790}
10791
10792// _LockXXX flags are useful to lock keys away from code which is not input-owner aware.
10793// When using _LockXXX flags, you can use ImGuiKeyOwner_Any to lock keys from everyone.
10794// - SetKeyOwner(..., None) : clears owner
10795// - SetKeyOwner(..., Any, !Lock) : illegal (assert)
10796// - SetKeyOwner(..., Any or None, Lock) : set lock
10797void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)
10798{
10799 ImGuiContext& g = *GImGui;
10800 IM_ASSERT(IsNamedKeyOrMod(key) && (owner_id != ImGuiKeyOwner_Any || (flags & (ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease)))); // Can only use _Any with _LockXXX flags (to eat a key away without an ID to retrieve it)
10801 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetKeyOwner) == 0); // Passing flags not supported by this function!
10802 //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X, flags=%08X)\n", GetKeyName(key), owner_id, flags);
10803
10804 ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(ctx: &g, key);
10805 owner_data->OwnerCurr = owner_data->OwnerNext = owner_id;
10806
10807 // We cannot lock by default as it would likely break lots of legacy code.
10808 // In the case of using LockUntilRelease while key is not down we still lock during the frame (no key_data->Down test)
10809 owner_data->LockUntilRelease = (flags & ImGuiInputFlags_LockUntilRelease) != 0;
10810 owner_data->LockThisFrame = (flags & ImGuiInputFlags_LockThisFrame) != 0 || (owner_data->LockUntilRelease);
10811}
10812
10813// Rarely used helper
10814void ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags)
10815{
10816 if (key_chord & ImGuiMod_Ctrl) { SetKeyOwner(key: ImGuiMod_Ctrl, owner_id, flags); }
10817 if (key_chord & ImGuiMod_Shift) { SetKeyOwner(key: ImGuiMod_Shift, owner_id, flags); }
10818 if (key_chord & ImGuiMod_Alt) { SetKeyOwner(key: ImGuiMod_Alt, owner_id, flags); }
10819 if (key_chord & ImGuiMod_Super) { SetKeyOwner(key: ImGuiMod_Super, owner_id, flags); }
10820 if (key_chord & ~ImGuiMod_Mask_) { SetKeyOwner(key: (ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); }
10821}
10822
10823// This is more or less equivalent to:
10824// if (IsItemHovered() || IsItemActive())
10825// SetKeyOwner(key, GetItemID());
10826// Extensive uses of that (e.g. many calls for a single item) may want to manually perform the tests once and then call SetKeyOwner() multiple times.
10827// More advanced usage scenarios may want to call SetKeyOwner() manually based on different condition.
10828// Worth noting is that only one item can be hovered and only one item can be active, therefore this usage pattern doesn't need to bother with routing and priority.
10829void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags)
10830{
10831 ImGuiContext& g = *GImGui;
10832 ImGuiID id = g.LastItemData.ID;
10833 if (id == 0 || (g.HoveredId != id && g.ActiveId != id))
10834 return;
10835 if ((flags & ImGuiInputFlags_CondMask_) == 0)
10836 flags |= ImGuiInputFlags_CondDefault_;
10837 if ((g.HoveredId == id && (flags & ImGuiInputFlags_CondHovered)) || (g.ActiveId == id && (flags & ImGuiInputFlags_CondActive)))
10838 {
10839 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetItemKeyOwner) == 0); // Passing flags not supported by this function!
10840 SetKeyOwner(key, owner_id: id, flags: flags & ~ImGuiInputFlags_CondMask_);
10841 }
10842}
10843
10844void ImGui::SetItemKeyOwner(ImGuiKey key)
10845{
10846 SetItemKeyOwner(key, flags: ImGuiInputFlags_None);
10847}
10848
10849// This is the only public API until we expose owner_id versions of the API as replacements.
10850bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord)
10851{
10852 return IsKeyChordPressed(key_chord, flags: ImGuiInputFlags_None, ImGuiKeyOwner_Any);
10853}
10854
10855// This is equivalent to comparing KeyMods + doing a IsKeyPressed()
10856bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
10857{
10858 ImGuiContext& g = *GImGui;
10859 key_chord = FixupKeyChord(key_chord);
10860 ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
10861 if (g.IO.KeyMods != mods)
10862 return false;
10863
10864 // Special storage location for mods
10865 ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
10866 if (key == ImGuiKey_None)
10867 key = ConvertSingleModFlagToKey(key: mods);
10868 if (!IsKeyPressed(key, flags: (flags & ImGuiInputFlags_RepeatMask_), owner_id))
10869 return false;
10870 return true;
10871}
10872
10873void ImGui::SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
10874{
10875 ImGuiContext& g = *GImGui;
10876 g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasShortcut;
10877 g.NextItemData.Shortcut = key_chord;
10878 g.NextItemData.ShortcutFlags = flags;
10879}
10880
10881// Called from within ItemAdd: at this point we can read from NextItemData and write to LastItemData
10882void ImGui::ItemHandleShortcut(ImGuiID id)
10883{
10884 ImGuiContext& g = *GImGui;
10885 ImGuiInputFlags flags = g.NextItemData.ShortcutFlags;
10886 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetNextItemShortcut) == 0); // Passing flags not supported by SetNextItemShortcut()!
10887
10888 if (g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled)
10889 return;
10890 if (flags & ImGuiInputFlags_Tooltip)
10891 {
10892 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasShortcut;
10893 g.LastItemData.Shortcut = g.NextItemData.Shortcut;
10894 }
10895 if (!Shortcut(key_chord: g.NextItemData.Shortcut, flags: flags & ImGuiInputFlags_SupportedByShortcut, owner_id: id) || g.NavActivateId != 0)
10896 return;
10897
10898 // FIXME: Generalize Activation queue?
10899 g.NavActivateId = id; // Will effectively disable clipping.
10900 g.NavActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_FromShortcut;
10901 //if (g.ActiveId == 0 || g.ActiveId == id)
10902 g.NavActivateDownId = g.NavActivatePressedId = id;
10903 NavHighlightActivated(id);
10904}
10905
10906bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
10907{
10908 return Shortcut(key_chord, flags, ImGuiKeyOwner_Any);
10909}
10910
10911bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
10912{
10913 ImGuiContext& g = *GImGui;
10914 //IMGUI_DEBUG_LOG("Shortcut(%s, flags=%X, owner_id=0x%08X)\n", GetKeyChordName(key_chord, g.TempBuffer.Data, g.TempBuffer.Size), flags, owner_id);
10915
10916 // When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any.
10917 if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
10918 flags |= ImGuiInputFlags_RouteFocused;
10919
10920 // Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default)
10921 // Effectively makes Shortcut() always input-owner aware.
10922 if (owner_id == ImGuiKeyOwner_Any || owner_id == ImGuiKeyOwner_NoOwner)
10923 owner_id = GetRoutingIdFromOwnerId(owner_id);
10924
10925 if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
10926 return false;
10927
10928 // Submit route
10929 if (!SetShortcutRouting(key_chord, flags, owner_id))
10930 return false;
10931
10932 // Default repeat behavior for Shortcut()
10933 // So e.g. pressing Ctrl+W and releasing Ctrl while holding W will not trigger the W shortcut.
10934 if ((flags & ImGuiInputFlags_Repeat) != 0 && (flags & ImGuiInputFlags_RepeatUntilMask_) == 0)
10935 flags |= ImGuiInputFlags_RepeatUntilKeyModsChange;
10936
10937 if (!IsKeyChordPressed(key_chord, flags, owner_id))
10938 return false;
10939
10940 // Claim mods during the press
10941 SetKeyOwnersForKeyChord(key_chord: key_chord & ImGuiMod_Mask_, owner_id);
10942
10943 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function!
10944 return true;
10945}
10946
10947
10948//-----------------------------------------------------------------------------
10949// [SECTION] ERROR CHECKING, STATE RECOVERY
10950//-----------------------------------------------------------------------------
10951// - DebugCheckVersionAndDataLayout() (called via IMGUI_CHECKVERSION() macros)
10952// - ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
10953// - ErrorCheckNewFrameSanityChecks()
10954// - ErrorCheckEndFrameSanityChecks()
10955// - ErrorRecoveryStoreState()
10956// - ErrorRecoveryTryToRecoverState()
10957// - ErrorRecoveryTryToRecoverWindowState()
10958// - ErrorLog()
10959//-----------------------------------------------------------------------------
10960
10961// Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
10962// Called by IMGUI_CHECKVERSION().
10963// Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
10964// If this triggers you have mismatched headers and compiled code versions.
10965// - It could be because of a build issue (using new headers with old compiled code)
10966// - It could be because of mismatched configuration #define, compilation settings, packing pragma etc.
10967// THE CONFIGURATION SETTINGS MENTIONED IN imconfig.h MUST BE SET FOR ALL COMPILATION UNITS INVOLVED WITH DEAR IMGUI.
10968// Which is why it is required you put them in your imconfig file (and NOT only before including imgui.h).
10969// Otherwise it is possible that different compilation units would see different structure layout.
10970// If you don't want to modify imconfig.h you can use the IMGUI_USER_CONFIG define to change filename.
10971bool 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)
10972{
10973 bool error = false;
10974 if (strcmp(s1: version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
10975 if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
10976 if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
10977 if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
10978 if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
10979 if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
10980 if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
10981 return !error;
10982}
10983
10984// Until 1.89 (IMGUI_VERSION_NUM < 18814) it was legal to use SetCursorPos() to extend the boundary of a parent (e.g. window or table cell)
10985// This is causing issues and ambiguity and we need to retire that.
10986// See https://github.com/ocornut/imgui/issues/5548 for more details.
10987// [Scenario 1]
10988// Previously this would make the window content size ~200x200:
10989// Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End(); // NOT OK
10990// Instead, please submit an item:
10991// Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); // OK
10992// Alternative:
10993// Begin(...) + Dummy(ImVec2(200,200)) + End(); // OK
10994// [Scenario 2]
10995// For reference this is one of the issue what we aim to fix with this change:
10996// BeginGroup() + SomeItem("foobar") + SetCursorScreenPos(GetCursorScreenPos()) + EndGroup()
10997// The previous logic made SetCursorScreenPos(GetCursorScreenPos()) have a side-effect! It would erroneously incorporate ItemSpacing.y after the item into content size, making the group taller!
10998// While this code is a little twisted, no-one would expect SetXXX(GetXXX()) to have a side-effect. Using vertical alignment patterns could trigger this issue.
10999void ImGui::ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
11000{
11001 ImGuiContext& g = *GImGui;
11002 ImGuiWindow* window = g.CurrentWindow;
11003 IM_ASSERT(window->DC.IsSetPos);
11004 window->DC.IsSetPos = false;
11005#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
11006 if (window->DC.CursorPos.x <= window->DC.CursorMaxPos.x && window->DC.CursorPos.y <= window->DC.CursorMaxPos.y)
11007 return;
11008 if (window->SkipItems)
11009 return;
11010 IM_ASSERT(0 && "Code uses SetCursorPos()/SetCursorScreenPos() to extend window/parent boundaries. Please submit an item e.g. Dummy() to validate extent.");
11011#else
11012 window->DC.CursorMaxPos = ImMax(lhs: window->DC.CursorMaxPos, rhs: window->DC.CursorPos);
11013#endif
11014}
11015
11016static void ImGui::ErrorCheckNewFrameSanityChecks()
11017{
11018 ImGuiContext& g = *GImGui;
11019
11020 // Check user IM_ASSERT macro
11021 // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined!
11022 // If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
11023 // This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
11024 // #define IM_ASSERT(EXPR) if (SomeCode(EXPR)) SomeMoreCode(); // Wrong!
11025 // #define IM_ASSERT(EXPR) do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0) // Correct!
11026 if (true) IM_ASSERT(1); else IM_ASSERT(0);
11027
11028 // Emscripten backends are often imprecise in their submission of DeltaTime. (#6114, #3644)
11029 // Ideally the Emscripten app/backend should aim to fix or smooth this value and avoid feeding zero, but we tolerate it.
11030#ifdef __EMSCRIPTEN__
11031 if (g.IO.DeltaTime <= 0.0f && g.FrameCount > 0)
11032 g.IO.DeltaTime = 0.00001f;
11033#endif
11034
11035 // Check user data
11036 // (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)
11037 IM_ASSERT(g.Initialized);
11038 IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!");
11039 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
11040 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!");
11041 IM_ASSERT(g.IO.Fonts->IsBuilt() && "Font Atlas not built! Make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()");
11042 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!");
11043 IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!");
11044 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
11045 IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
11046 IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
11047 IM_ASSERT(g.Style.ColorButtonPosition == ImGuiDir_Left || g.Style.ColorButtonPosition == ImGuiDir_Right);
11048
11049 // Error handling: we do not accept 100% silent recovery! Please contact me if you feel this is getting in your way.
11050 if (g.IO.ConfigErrorRecovery)
11051 IM_ASSERT(g.IO.ConfigErrorRecoveryEnableAssert || g.IO.ConfigErrorRecoveryEnableDebugLog || g.IO.ConfigErrorRecoveryEnableTooltip || g.ErrorCallback != NULL);
11052
11053#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
11054 // Remap legacy names
11055 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)
11056 {
11057 g.IO.ConfigNavMoveSetMousePos = true;
11058 g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavEnableSetMousePos;
11059 }
11060 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)
11061 {
11062 g.IO.ConfigNavCaptureKeyboard = false;
11063 g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavNoCaptureKeyboard;
11064 }
11065
11066 // Remap legacy clipboard handlers (OBSOLETED in 1.91.1, August 2024)
11067 if (g.IO.GetClipboardTextFn != NULL && (g.PlatformIO.Platform_GetClipboardTextFn == NULL || g.PlatformIO.Platform_GetClipboardTextFn == Platform_GetClipboardTextFn_DefaultImpl))
11068 g.PlatformIO.Platform_GetClipboardTextFn = [](ImGuiContext* ctx) { return ctx->IO.GetClipboardTextFn(ctx->IO.ClipboardUserData); };
11069 if (g.IO.SetClipboardTextFn != NULL && (g.PlatformIO.Platform_SetClipboardTextFn == NULL || g.PlatformIO.Platform_SetClipboardTextFn == Platform_SetClipboardTextFn_DefaultImpl))
11070 g.PlatformIO.Platform_SetClipboardTextFn = [](ImGuiContext* ctx, const char* text) { return ctx->IO.SetClipboardTextFn(ctx->IO.ClipboardUserData, text); };
11071#endif
11072
11073 // Perform simple check: error if Docking or Viewport are enabled _exactly_ on frame 1 (instead of frame 0 or later), which is a common error leading to loss of .ini data.
11074 if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_DockingEnable) == 0)
11075 IM_ASSERT(0 && "Please set DockingEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!");
11076 if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable) == 0)
11077 IM_ASSERT(0 && "Please set ViewportsEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!");
11078
11079 // Perform simple checks: multi-viewport and platform windows support
11080 if (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
11081 {
11082 if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports))
11083 {
11084 IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()? Check examples/ applications for reference.");
11085 IM_ASSERT(g.PlatformIO.Platform_CreateWindow != NULL && "Platform init didn't install handlers?");
11086 IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?");
11087 IM_ASSERT(g.PlatformIO.Platform_GetWindowPos != NULL && "Platform init didn't install handlers?");
11088 IM_ASSERT(g.PlatformIO.Platform_SetWindowPos != NULL && "Platform init didn't install handlers?");
11089 IM_ASSERT(g.PlatformIO.Platform_GetWindowSize != NULL && "Platform init didn't install handlers?");
11090 IM_ASSERT(g.PlatformIO.Platform_SetWindowSize != NULL && "Platform init didn't install handlers?");
11091 IM_ASSERT(g.PlatformIO.Monitors.Size > 0 && "Platform init didn't setup Monitors list?");
11092 IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport.");
11093 if (g.IO.ConfigDockingTransparentPayload && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
11094 IM_ASSERT(g.PlatformIO.Platform_SetWindowAlpha != NULL && "Platform_SetWindowAlpha handler is required to use io.ConfigDockingTransparent!");
11095 }
11096 else
11097 {
11098 // Disable feature, our backends do not support it
11099 g.IO.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable;
11100 }
11101
11102 // Perform simple checks on platform monitor data + compute a total bounding box for quick early outs
11103 for (ImGuiPlatformMonitor& mon : g.PlatformIO.Monitors)
11104 {
11105 IM_UNUSED(mon);
11106 IM_ASSERT(mon.MainSize.x > 0.0f && mon.MainSize.y > 0.0f && "Monitor main bounds not setup properly.");
11107 IM_ASSERT(ImRect(mon.MainPos, mon.MainPos + mon.MainSize).Contains(ImRect(mon.WorkPos, mon.WorkPos + mon.WorkSize)) && "Monitor work bounds not setup properly. If you don't have work area information, just copy MainPos/MainSize into them.");
11108 IM_ASSERT(mon.DpiScale > 0.0f && mon.DpiScale < 99.0f && "Monitor DpiScale is invalid."); // Typical correct values would be between 1.0f and 4.0f
11109 }
11110 }
11111}
11112
11113static void ImGui::ErrorCheckEndFrameSanityChecks()
11114{
11115 // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
11116 // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
11117 // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
11118 // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
11119 // We silently accommodate for this case by ignoring the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
11120 // while still correctly asserting on mid-frame key press events.
11121 ImGuiContext& g = *GImGui;
11122 const ImGuiKeyChord key_mods = GetMergedModsFromKeys();
11123 IM_UNUSED(g);
11124 IM_UNUSED(key_mods);
11125 IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
11126 IM_UNUSED(key_mods);
11127
11128 IM_ASSERT(g.CurrentWindowStack.Size == 1);
11129 IM_ASSERT(g.CurrentWindowStack[0].Window->IsFallbackWindow);
11130}
11131
11132// Save current stack sizes. Called e.g. by NewFrame() and by Begin() but may be called for manual recovery.
11133void ImGui::ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out)
11134{
11135 ImGuiContext& g = *GImGui;
11136 state_out->SizeOfWindowStack = (short)g.CurrentWindowStack.Size;
11137 state_out->SizeOfIDStack = (short)g.CurrentWindow->IDStack.Size;
11138 state_out->SizeOfTreeStack = (short)g.CurrentWindow->DC.TreeDepth; // NOT g.TreeNodeStack.Size which is a partial stack!
11139 state_out->SizeOfColorStack = (short)g.ColorStack.Size;
11140 state_out->SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
11141 state_out->SizeOfFontStack = (short)g.FontStack.Size;
11142 state_out->SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
11143 state_out->SizeOfGroupStack = (short)g.GroupStack.Size;
11144 state_out->SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size;
11145 state_out->SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
11146 state_out->SizeOfDisabledStack = (short)g.DisabledStackSize;
11147}
11148
11149// Chosen name "Try to recover" over e.g. "Restore" to suggest this is not a 100% guaranteed recovery.
11150// Called by e.g. EndFrame() but may be called for manual recovery.
11151// Attempt to recover full window stack.
11152void ImGui::ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in)
11153{
11154 // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
11155 ImGuiContext& g = *GImGui;
11156 while (g.CurrentWindowStack.Size > state_in->SizeOfWindowStack) //-V1044
11157 {
11158 // Recap:
11159 // - Begin()/BeginChild() return false to indicate the window is collapsed or fully clipped.
11160 // - Always call a matching End() for each Begin() call, regardless of its return value!
11161 // - Begin/End and BeginChild/EndChild logic is KNOWN TO BE INCONSISTENT WITH ALL OTHER BEGIN/END FUNCTIONS.
11162 // - We will fix that in a future major update.
11163 ImGuiWindow* window = g.CurrentWindow;
11164 if (window->Flags & ImGuiWindowFlags_ChildWindow)
11165 {
11166 IM_ASSERT_USER_ERROR(0, "Missing EndChild()");
11167 EndChild();
11168 }
11169 else
11170 {
11171 IM_ASSERT_USER_ERROR(0, "Missing End()");
11172 End();
11173 }
11174 }
11175 if (g.CurrentWindowStack.Size == state_in->SizeOfWindowStack)
11176 ErrorRecoveryTryToRecoverWindowState(state_in);
11177}
11178
11179// Called by e.g. End() but may be called for manual recovery.
11180// Read '// Error Handling [BETA]' block in imgui_internal.h for details.
11181// Attempt to recover from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
11182void ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in)
11183{
11184 ImGuiContext& g = *GImGui;
11185
11186 while (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow) //-V1044
11187 {
11188 IM_ASSERT_USER_ERROR(0, "Missing EndTable()");
11189 EndTable();
11190 }
11191
11192 ImGuiWindow* window = g.CurrentWindow;
11193
11194 // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
11195 while (g.CurrentTabBar != NULL && g.CurrentTabBar->Window == window) //-V1044
11196 {
11197 IM_ASSERT_USER_ERROR(0, "Missing EndTabBar()");
11198 EndTabBar();
11199 }
11200 while (g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Storage->Window == window) //-V1044
11201 {
11202 IM_ASSERT_USER_ERROR(0, "Missing EndMultiSelect()");
11203 EndMultiSelect();
11204 }
11205 while (window->DC.TreeDepth > state_in->SizeOfTreeStack) //-V1044
11206 {
11207 IM_ASSERT_USER_ERROR(0, "Missing TreePop()");
11208 TreePop();
11209 }
11210 while (g.GroupStack.Size > state_in->SizeOfGroupStack) //-V1044
11211 {
11212 IM_ASSERT_USER_ERROR(0, "Missing EndGroup()");
11213 EndGroup();
11214 }
11215 IM_ASSERT(g.GroupStack.Size == state_in->SizeOfGroupStack);
11216 while (window->IDStack.Size > state_in->SizeOfIDStack) //-V1044
11217 {
11218 IM_ASSERT_USER_ERROR(0, "Missing PopID()");
11219 PopID();
11220 }
11221 while (g.DisabledStackSize > state_in->SizeOfDisabledStack) //-V1044
11222 {
11223 IM_ASSERT_USER_ERROR(0, "Missing EndDisabled()");
11224 if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
11225 EndDisabled();
11226 else
11227 {
11228 EndDisabledOverrideReenable();
11229 g.CurrentWindowStack.back().DisabledOverrideReenable = false;
11230 }
11231 }
11232 IM_ASSERT(g.DisabledStackSize == state_in->SizeOfDisabledStack);
11233 while (g.ColorStack.Size > state_in->SizeOfColorStack) //-V1044
11234 {
11235 IM_ASSERT_USER_ERROR(0, "Missing PopStyleColor()");
11236 PopStyleColor();
11237 }
11238 while (g.ItemFlagsStack.Size > state_in->SizeOfItemFlagsStack) //-V1044
11239 {
11240 IM_ASSERT_USER_ERROR(0, "Missing PopItemFlag()");
11241 PopItemFlag();
11242 }
11243 while (g.StyleVarStack.Size > state_in->SizeOfStyleVarStack) //-V1044
11244 {
11245 IM_ASSERT_USER_ERROR(0, "Missing PopStyleVar()");
11246 PopStyleVar();
11247 }
11248 while (g.FontStack.Size > state_in->SizeOfFontStack) //-V1044
11249 {
11250 IM_ASSERT_USER_ERROR(0, "Missing PopFont()");
11251 PopFont();
11252 }
11253 while (g.FocusScopeStack.Size > state_in->SizeOfFocusScopeStack) //-V1044
11254 {
11255 IM_ASSERT_USER_ERROR(0, "Missing PopFocusScope()");
11256 PopFocusScope();
11257 }
11258 //IM_ASSERT(g.FocusScopeStack.Size == state_in->SizeOfFocusScopeStack);
11259}
11260
11261bool ImGui::ErrorLog(const char* msg)
11262{
11263 ImGuiContext& g = *GImGui;
11264
11265 // Output to debug log
11266#ifndef IMGUI_DISABLE_DEBUG_TOOLS
11267 ImGuiWindow* window = g.CurrentWindow;
11268
11269 if (g.IO.ConfigErrorRecoveryEnableDebugLog)
11270 {
11271 if (g.ErrorFirst)
11272 IMGUI_DEBUG_LOG_ERROR("[imgui-error] (current settings: Assert=%d, Log=%d, Tooltip=%d)\n",
11273 g.IO.ConfigErrorRecoveryEnableAssert, g.IO.ConfigErrorRecoveryEnableDebugLog, g.IO.ConfigErrorRecoveryEnableTooltip);
11274 IMGUI_DEBUG_LOG_ERROR("[imgui-error] In window '%s': %s\n", window ? window->Name : "NULL", msg);
11275 }
11276 g.ErrorFirst = false;
11277
11278 // Output to tooltip
11279 if (g.IO.ConfigErrorRecoveryEnableTooltip)
11280 {
11281 if (g.WithinFrameScope && BeginErrorTooltip())
11282 {
11283 if (g.ErrorCountCurrentFrame < 20)
11284 {
11285 Text(fmt: "In window '%s': %s", window ? window->Name : "NULL", msg);
11286 if (window && (!window->IsFallbackWindow || window->WasActive))
11287 GetForegroundDrawList(window)->AddRect(p_min: window->Pos, p_max: window->Pos + window->Size, IM_COL32(255, 0, 0, 255));
11288 }
11289 if (g.ErrorCountCurrentFrame == 20)
11290 Text(fmt: "(and more errors)");
11291 // EndFrame() will amend debug buttons to this window, after all errors have been submitted.
11292 EndErrorTooltip();
11293 }
11294 g.ErrorCountCurrentFrame++;
11295 }
11296#endif
11297
11298 // Output to callback
11299 if (g.ErrorCallback != NULL)
11300 g.ErrorCallback(&g, g.ErrorCallbackUserData, msg);
11301
11302 // Return whether we should assert
11303 return g.IO.ConfigErrorRecoveryEnableAssert;
11304}
11305
11306void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip()
11307{
11308#ifndef IMGUI_DISABLE_DEBUG_TOOLS
11309 ImGuiContext& g = *GImGui;
11310 if (g.DebugDrawIdConflicts != 0 && g.IO.KeyCtrl == false)
11311 g.DebugDrawIdConflictsCount = g.HoveredIdPreviousFrameItemCount;
11312 if (g.DebugDrawIdConflicts != 0 && g.DebugItemPickerActive == false && BeginErrorTooltip())
11313 {
11314 Text(fmt: "Programmer error: %d visible items with conflicting ID!", g.DebugDrawIdConflictsCount);
11315 BulletText(fmt: "Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!");
11316 BulletText(fmt: "Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!");
11317 //BulletText("Code intending to use duplicate ID may use e.g. PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()"); // Not making this too visible for fear of it being abused.
11318 BulletText(fmt: "Set io.ConfigDebugHighlightIdConflicts=false to disable this warning in non-programmers builds.");
11319 Separator();
11320 Text(fmt: "(Hold CTRL to: use");
11321 SameLine();
11322 if (SmallButton(label: "Item Picker"))
11323 DebugStartItemPicker();
11324 SameLine();
11325 Text(fmt: "to break in item call-stack, or");
11326 SameLine();
11327 if (SmallButton(label: "Open FAQ->About ID Stack System") && g.PlatformIO.Platform_OpenInShellFn != NULL)
11328 g.PlatformIO.Platform_OpenInShellFn(&g, "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage");
11329 EndErrorTooltip();
11330 }
11331
11332 if (g.ErrorCountCurrentFrame > 0 && BeginErrorTooltip()) // Amend at end of frame
11333 {
11334 Separator();
11335 Text(fmt: "(Hold CTRL to:");
11336 SameLine();
11337 if (SmallButton(label: "Enable Asserts"))
11338 g.IO.ConfigErrorRecoveryEnableAssert = true;
11339 //SameLine();
11340 //if (SmallButton("Hide Error Tooltips"))
11341 // g.IO.ConfigErrorRecoveryEnableTooltip = false; // Too dangerous
11342 SameLine(offset_from_start_x: 0, spacing: 0);
11343 Text(fmt: ")");
11344 EndErrorTooltip();
11345 }
11346#endif
11347}
11348
11349// Pseudo-tooltip. Follow mouse until CTRL is held. When CTRL is held we lock position, allowing to click it.
11350bool ImGui::BeginErrorTooltip()
11351{
11352 ImGuiContext& g = *GImGui;
11353 ImGuiWindow* window = FindWindowByName(name: "##Tooltip_Error");
11354 const bool use_locked_pos = (g.IO.KeyCtrl && window && window->WasActive);
11355 PushStyleColor(idx: ImGuiCol_PopupBg, col: ImLerp(a: g.Style.Colors[ImGuiCol_PopupBg], b: ImVec4(1.0f, 0.0f, 0.0f, 1.0f), t: 0.15f));
11356 if (use_locked_pos)
11357 SetNextWindowPos(pos: g.ErrorTooltipLockedPos);
11358 bool is_visible = Begin(name: "##Tooltip_Error", NULL, flags: ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
11359 PopStyleColor();
11360 if (is_visible && g.CurrentWindow->BeginCount == 1)
11361 {
11362 SeparatorText(label: "MESSAGE FROM DEAR IMGUI");
11363 BringWindowToDisplayFront(window: g.CurrentWindow);
11364 BringWindowToFocusFront(window: g.CurrentWindow);
11365 g.ErrorTooltipLockedPos = GetWindowPos();
11366 }
11367 else if (!is_visible)
11368 {
11369 End();
11370 }
11371 return is_visible;
11372}
11373
11374void ImGui::EndErrorTooltip()
11375{
11376 End();
11377}
11378
11379//-----------------------------------------------------------------------------
11380// [SECTION] ITEM SUBMISSION
11381//-----------------------------------------------------------------------------
11382// - KeepAliveID()
11383// - ItemAdd()
11384//-----------------------------------------------------------------------------
11385
11386// Code not using ItemAdd() may need to call this manually otherwise ActiveId will be cleared. In IMGUI_VERSION_NUM < 18717 this was called by GetID().
11387void ImGui::KeepAliveID(ImGuiID id)
11388{
11389 ImGuiContext& g = *GImGui;
11390 if (g.ActiveId == id)
11391 g.ActiveIdIsAlive = id;
11392 if (g.ActiveIdPreviousFrame == id)
11393 g.ActiveIdPreviousFrameIsAlive = true;
11394}
11395
11396// Declare item bounding box for clipping and interaction.
11397// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
11398// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
11399// THIS IS IN THE PERFORMANCE CRITICAL PATH (UNTIL THE CLIPPING TEST AND EARLY-RETURN)
11400IM_MSVC_RUNTIME_CHECKS_OFF
11401bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags)
11402{
11403 ImGuiContext& g = *GImGui;
11404 ImGuiWindow* window = g.CurrentWindow;
11405
11406 // Set item data
11407 // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set)
11408 g.LastItemData.ID = id;
11409 g.LastItemData.Rect = bb;
11410 g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb;
11411 g.LastItemData.ItemFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags;
11412 g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;
11413 // Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared.
11414
11415 if (id != 0)
11416 {
11417 KeepAliveID(id);
11418
11419 // Directional navigation processing
11420 // Runs prior to clipping early-out
11421 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
11422 // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
11423 // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
11424 // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
11425 // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
11426 // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
11427 // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
11428 // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
11429 if (!(g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))
11430 {
11431 // FIMXE-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test.
11432 window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
11433 if (g.NavId == id || g.NavAnyRequest)
11434 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
11435 if (window == g.NavWindow || ((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened))
11436 NavProcessItem();
11437 }
11438
11439 if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasShortcut)
11440 ItemHandleShortcut(id);
11441 }
11442
11443 // Lightweight clear of SetNextItemXXX data.
11444 g.NextItemData.HasFlags = ImGuiNextItemDataFlags_None;
11445 g.NextItemData.ItemFlags = ImGuiItemFlags_None;
11446
11447#ifdef IMGUI_ENABLE_TEST_ENGINE
11448 if (id != 0)
11449 IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData);
11450#endif
11451
11452 // Clipping test
11453 // (this is an inline copy of IsClippedEx() so we can reuse the is_rect_visible value, otherwise we'd do 'if (IsClippedEx(bb, id)) return false')
11454 // g.NavActivateId is not necessarily == g.NavId, in the case of remote activation (e.g. shortcuts)
11455 const bool is_rect_visible = bb.Overlaps(r: window->ClipRect);
11456 if (!is_rect_visible)
11457 if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
11458 if (!g.ItemUnclipByLog)
11459 return false;
11460
11461 // [DEBUG]
11462#ifndef IMGUI_DISABLE_DEBUG_TOOLS
11463 if (id != 0)
11464 {
11465 if (id == g.DebugLocateId)
11466 DebugLocateItemResolveWithLastItem();
11467
11468 // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something".
11469 // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something".
11470 // READ THE FAQ: https://dearimgui.com/faq
11471 IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!");
11472 }
11473 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
11474 //if ((g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav) == 0)
11475 // window->DrawList->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, IM_COL32(255,255,0,255)); // [DEBUG]
11476#endif
11477
11478 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
11479 if (is_rect_visible)
11480 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Visible;
11481 if (IsMouseHoveringRect(r_min: bb.Min, r_max: bb.Max))
11482 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect;
11483 return true;
11484}
11485IM_MSVC_RUNTIME_CHECKS_RESTORE
11486
11487//-----------------------------------------------------------------------------
11488// [SECTION] LAYOUT
11489//-----------------------------------------------------------------------------
11490// - ItemSize()
11491// - SameLine()
11492// - GetCursorScreenPos()
11493// - SetCursorScreenPos()
11494// - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
11495// - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
11496// - GetCursorStartPos()
11497// - Indent()
11498// - Unindent()
11499// - SetNextItemWidth()
11500// - PushItemWidth()
11501// - PushMultiItemsWidths()
11502// - PopItemWidth()
11503// - CalcItemWidth()
11504// - CalcItemSize()
11505// - GetTextLineHeight()
11506// - GetTextLineHeightWithSpacing()
11507// - GetFrameHeight()
11508// - GetFrameHeightWithSpacing()
11509// - GetContentRegionMax()
11510// - GetContentRegionAvail(),
11511// - BeginGroup()
11512// - EndGroup()
11513// Also see in imgui_widgets: tab bars, and in imgui_tables: tables, columns.
11514//-----------------------------------------------------------------------------
11515
11516// Advance cursor given item size for layout.
11517// Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
11518// See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.
11519// THIS IS IN THE PERFORMANCE CRITICAL PATH.
11520IM_MSVC_RUNTIME_CHECKS_OFF
11521void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
11522{
11523 ImGuiContext& g = *GImGui;
11524 ImGuiWindow* window = g.CurrentWindow;
11525 if (window->SkipItems)
11526 return;
11527
11528 // We increase the height in this function to accommodate for baseline offset.
11529 // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
11530 // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
11531 const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(lhs: 0.0f, rhs: window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
11532
11533 const float line_y1 = window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y;
11534 const float line_height = ImMax(lhs: window->DC.CurrLineSize.y, /*ImMax(*/rhs: window->DC.CursorPos.y - line_y1/*, 0.0f)*/ + size.y + offset_to_match_baseline_y);
11535
11536 // Always align ourselves on pixel boundaries
11537 //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]
11538 window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
11539 window->DC.CursorPosPrevLine.y = line_y1;
11540 window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line
11541 window->DC.CursorPos.y = IM_TRUNC(line_y1 + line_height + g.Style.ItemSpacing.y); // Next line
11542 window->DC.CursorMaxPos.x = ImMax(lhs: window->DC.CursorMaxPos.x, rhs: window->DC.CursorPosPrevLine.x);
11543 window->DC.CursorMaxPos.y = ImMax(lhs: window->DC.CursorMaxPos.y, rhs: window->DC.CursorPos.y - g.Style.ItemSpacing.y);
11544 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
11545
11546 window->DC.PrevLineSize.y = line_height;
11547 window->DC.CurrLineSize.y = 0.0f;
11548 window->DC.PrevLineTextBaseOffset = ImMax(lhs: window->DC.CurrLineTextBaseOffset, rhs: text_baseline_y);
11549 window->DC.CurrLineTextBaseOffset = 0.0f;
11550 window->DC.IsSameLine = window->DC.IsSetPos = false;
11551
11552 // Horizontal layout mode
11553 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
11554 SameLine();
11555}
11556IM_MSVC_RUNTIME_CHECKS_RESTORE
11557
11558// Gets back to previous line and continue with horizontal layout
11559// offset_from_start_x == 0 : follow right after previous item
11560// offset_from_start_x != 0 : align to specified x position (relative to window/group left)
11561// spacing_w < 0 : use default spacing if offset_from_start_x == 0, no spacing if offset_from_start_x != 0
11562// spacing_w >= 0 : enforce spacing amount
11563void ImGui::SameLine(float offset_from_start_x, float spacing_w)
11564{
11565 ImGuiContext& g = *GImGui;
11566 ImGuiWindow* window = g.CurrentWindow;
11567 if (window->SkipItems)
11568 return;
11569
11570 if (offset_from_start_x != 0.0f)
11571 {
11572 if (spacing_w < 0.0f)
11573 spacing_w = 0.0f;
11574 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
11575 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
11576 }
11577 else
11578 {
11579 if (spacing_w < 0.0f)
11580 spacing_w = g.Style.ItemSpacing.x;
11581 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
11582 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
11583 }
11584 window->DC.CurrLineSize = window->DC.PrevLineSize;
11585 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
11586 window->DC.IsSameLine = true;
11587}
11588
11589ImVec2 ImGui::GetCursorScreenPos()
11590{
11591 ImGuiWindow* window = GetCurrentWindowRead();
11592 return window->DC.CursorPos;
11593}
11594
11595void ImGui::SetCursorScreenPos(const ImVec2& pos)
11596{
11597 ImGuiWindow* window = GetCurrentWindow();
11598 window->DC.CursorPos = pos;
11599 //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
11600 window->DC.IsSetPos = true;
11601}
11602
11603// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
11604// 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'.
11605ImVec2 ImGui::GetCursorPos()
11606{
11607 ImGuiWindow* window = GetCurrentWindowRead();
11608 return window->DC.CursorPos - window->Pos + window->Scroll;
11609}
11610
11611float ImGui::GetCursorPosX()
11612{
11613 ImGuiWindow* window = GetCurrentWindowRead();
11614 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
11615}
11616
11617float ImGui::GetCursorPosY()
11618{
11619 ImGuiWindow* window = GetCurrentWindowRead();
11620 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
11621}
11622
11623void ImGui::SetCursorPos(const ImVec2& local_pos)
11624{
11625 ImGuiWindow* window = GetCurrentWindow();
11626 window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
11627 //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
11628 window->DC.IsSetPos = true;
11629}
11630
11631void ImGui::SetCursorPosX(float x)
11632{
11633 ImGuiWindow* window = GetCurrentWindow();
11634 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
11635 //window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
11636 window->DC.IsSetPos = true;
11637}
11638
11639void ImGui::SetCursorPosY(float y)
11640{
11641 ImGuiWindow* window = GetCurrentWindow();
11642 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
11643 //window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
11644 window->DC.IsSetPos = true;
11645}
11646
11647ImVec2 ImGui::GetCursorStartPos()
11648{
11649 ImGuiWindow* window = GetCurrentWindowRead();
11650 return window->DC.CursorStartPos - window->Pos;
11651}
11652
11653void ImGui::Indent(float indent_w)
11654{
11655 ImGuiContext& g = *GImGui;
11656 ImGuiWindow* window = GetCurrentWindow();
11657 window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
11658 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
11659}
11660
11661void ImGui::Unindent(float indent_w)
11662{
11663 ImGuiContext& g = *GImGui;
11664 ImGuiWindow* window = GetCurrentWindow();
11665 window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
11666 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
11667}
11668
11669// Affect large frame+labels widgets only.
11670void ImGui::SetNextItemWidth(float item_width)
11671{
11672 ImGuiContext& g = *GImGui;
11673 g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasWidth;
11674 g.NextItemData.Width = item_width;
11675}
11676
11677// FIXME: Remove the == 0.0f behavior?
11678void ImGui::PushItemWidth(float item_width)
11679{
11680 ImGuiContext& g = *GImGui;
11681 ImGuiWindow* window = g.CurrentWindow;
11682 window->DC.ItemWidthStack.push_back(v: window->DC.ItemWidth); // Backup current width
11683 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
11684 g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth;
11685}
11686
11687void ImGui::PushMultiItemsWidths(int components, float w_full)
11688{
11689 ImGuiContext& g = *GImGui;
11690 ImGuiWindow* window = g.CurrentWindow;
11691 IM_ASSERT(components > 0);
11692 const ImGuiStyle& style = g.Style;
11693 window->DC.ItemWidthStack.push_back(v: window->DC.ItemWidth); // Backup current width
11694 float w_items = w_full - style.ItemInnerSpacing.x * (components - 1);
11695 float prev_split = w_items;
11696 for (int i = components - 1; i > 0; i--)
11697 {
11698 float next_split = IM_TRUNC(w_items * i / components);
11699 window->DC.ItemWidthStack.push_back(v: ImMax(lhs: prev_split - next_split, rhs: 1.0f));
11700 prev_split = next_split;
11701 }
11702 window->DC.ItemWidth = ImMax(lhs: prev_split, rhs: 1.0f);
11703 g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth;
11704}
11705
11706void ImGui::PopItemWidth()
11707{
11708 ImGuiContext& g = *GImGui;
11709 ImGuiWindow* window = g.CurrentWindow;
11710 if (window->DC.ItemWidthStack.Size <= 0)
11711 {
11712 IM_ASSERT_USER_ERROR(0, "Calling PopItemWidth() too many times!");
11713 return;
11714 }
11715 window->DC.ItemWidth = window->DC.ItemWidthStack.back();
11716 window->DC.ItemWidthStack.pop_back();
11717}
11718
11719// Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
11720// The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
11721float ImGui::CalcItemWidth()
11722{
11723 ImGuiContext& g = *GImGui;
11724 ImGuiWindow* window = g.CurrentWindow;
11725 float w;
11726 if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth)
11727 w = g.NextItemData.Width;
11728 else
11729 w = window->DC.ItemWidth;
11730 if (w < 0.0f)
11731 {
11732 float region_avail_x = GetContentRegionAvail().x;
11733 w = ImMax(lhs: 1.0f, rhs: region_avail_x + w);
11734 }
11735 w = IM_TRUNC(w);
11736 return w;
11737}
11738
11739// [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
11740// Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
11741// Note that only CalcItemWidth() is publicly exposed.
11742// The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)
11743ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
11744{
11745 ImVec2 avail;
11746 if (size.x < 0.0f || size.y < 0.0f)
11747 avail = GetContentRegionAvail();
11748
11749 if (size.x == 0.0f)
11750 size.x = default_w;
11751 else if (size.x < 0.0f)
11752 size.x = ImMax(lhs: 4.0f, rhs: avail.x + size.x); // <-- size.x is negative here so we are subtracting
11753
11754 if (size.y == 0.0f)
11755 size.y = default_h;
11756 else if (size.y < 0.0f)
11757 size.y = ImMax(lhs: 4.0f, rhs: avail.y + size.y); // <-- size.y is negative here so we are subtracting
11758
11759 return size;
11760}
11761
11762float ImGui::GetTextLineHeight()
11763{
11764 ImGuiContext& g = *GImGui;
11765 return g.FontSize;
11766}
11767
11768float ImGui::GetTextLineHeightWithSpacing()
11769{
11770 ImGuiContext& g = *GImGui;
11771 return g.FontSize + g.Style.ItemSpacing.y;
11772}
11773
11774float ImGui::GetFrameHeight()
11775{
11776 ImGuiContext& g = *GImGui;
11777 return g.FontSize + g.Style.FramePadding.y * 2.0f;
11778}
11779
11780float ImGui::GetFrameHeightWithSpacing()
11781{
11782 ImGuiContext& g = *GImGui;
11783 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
11784}
11785
11786ImVec2 ImGui::GetContentRegionAvail()
11787{
11788 ImGuiContext& g = *GImGui;
11789 ImGuiWindow* window = g.CurrentWindow;
11790 ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max;
11791 return mx - window->DC.CursorPos;
11792}
11793
11794#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
11795
11796// You should never need those functions. Always use GetCursorScreenPos() and GetContentRegionAvail()!
11797// They are bizarre local-coordinates which don't play well with scrolling.
11798ImVec2 ImGui::GetContentRegionMax()
11799{
11800 return GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos();
11801}
11802
11803ImVec2 ImGui::GetWindowContentRegionMin()
11804{
11805 ImGuiWindow* window = GImGui->CurrentWindow;
11806 return window->ContentRegionRect.Min - window->Pos;
11807}
11808
11809ImVec2 ImGui::GetWindowContentRegionMax()
11810{
11811 ImGuiWindow* window = GImGui->CurrentWindow;
11812 return window->ContentRegionRect.Max - window->Pos;
11813}
11814#endif
11815
11816// 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.)
11817// Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
11818// FIXME-OPT: Could we safely early out on ->SkipItems?
11819void ImGui::BeginGroup()
11820{
11821 ImGuiContext& g = *GImGui;
11822 ImGuiWindow* window = g.CurrentWindow;
11823
11824 g.GroupStack.resize(new_size: g.GroupStack.Size + 1);
11825 ImGuiGroupData& group_data = g.GroupStack.back();
11826 group_data.WindowID = window->ID;
11827 group_data.BackupCursorPos = window->DC.CursorPos;
11828 group_data.BackupCursorPosPrevLine = window->DC.CursorPosPrevLine;
11829 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
11830 group_data.BackupIndent = window->DC.Indent;
11831 group_data.BackupGroupOffset = window->DC.GroupOffset;
11832 group_data.BackupCurrLineSize = window->DC.CurrLineSize;
11833 group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
11834 group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
11835 group_data.BackupHoveredIdIsAlive = g.HoveredId != 0;
11836 group_data.BackupIsSameLine = window->DC.IsSameLine;
11837 group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
11838 group_data.EmitItem = true;
11839
11840 window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
11841 window->DC.Indent = window->DC.GroupOffset;
11842 window->DC.CursorMaxPos = window->DC.CursorPos;
11843 window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
11844 if (g.LogEnabled)
11845 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
11846}
11847
11848void ImGui::EndGroup()
11849{
11850 ImGuiContext& g = *GImGui;
11851 ImGuiWindow* window = g.CurrentWindow;
11852 IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls
11853
11854 ImGuiGroupData& group_data = g.GroupStack.back();
11855 IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window?
11856
11857 if (window->DC.IsSetPos)
11858 ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
11859
11860 // Include LastItemData.Rect.Max as a workaround for e.g. EndTable() undershooting with CursorMaxPos report. (#7543)
11861 ImRect group_bb(group_data.BackupCursorPos, ImMax(lhs: ImMax(lhs: window->DC.CursorMaxPos, rhs: g.LastItemData.Rect.Max), rhs: group_data.BackupCursorPos));
11862 window->DC.CursorPos = group_data.BackupCursorPos;
11863 window->DC.CursorPosPrevLine = group_data.BackupCursorPosPrevLine;
11864 window->DC.CursorMaxPos = ImMax(lhs: group_data.BackupCursorMaxPos, rhs: group_bb.Max);
11865 window->DC.Indent = group_data.BackupIndent;
11866 window->DC.GroupOffset = group_data.BackupGroupOffset;
11867 window->DC.CurrLineSize = group_data.BackupCurrLineSize;
11868 window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
11869 window->DC.IsSameLine = group_data.BackupIsSameLine;
11870 if (g.LogEnabled)
11871 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
11872
11873 if (!group_data.EmitItem)
11874 {
11875 g.GroupStack.pop_back();
11876 return;
11877 }
11878
11879 window->DC.CurrLineTextBaseOffset = ImMax(lhs: window->DC.PrevLineTextBaseOffset, rhs: 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.
11880 ItemSize(size: group_bb.GetSize());
11881 ItemAdd(bb: group_bb, id: 0, NULL, extra_flags: ImGuiItemFlags_NoTabStop);
11882
11883 // 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.
11884 // It would be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets.
11885 // Also if you grep for LastItemId you'll notice it is only used in that context.
11886 // (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.)
11887 const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
11888 const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true);
11889 if (group_contains_curr_active_id)
11890 g.LastItemData.ID = g.ActiveId;
11891 else if (group_contains_prev_active_id)
11892 g.LastItemData.ID = g.ActiveIdPreviousFrame;
11893 g.LastItemData.Rect = group_bb;
11894
11895 // Forward Hovered flag
11896 const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0;
11897 if (group_contains_curr_hovered_id)
11898 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
11899
11900 // Forward Edited flag
11901 if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
11902 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
11903
11904 // Forward Deactivated flag
11905 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
11906 if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
11907 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated;
11908
11909 g.GroupStack.pop_back();
11910 if (g.DebugShowGroupRects)
11911 window->DrawList->AddRect(p_min: group_bb.Min, p_max: group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
11912}
11913
11914
11915//-----------------------------------------------------------------------------
11916// [SECTION] SCROLLING
11917//-----------------------------------------------------------------------------
11918
11919// Helper to snap on edges when aiming at an item very close to the edge,
11920// So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
11921// When we refactor the scrolling API this may be configurable with a flag?
11922// Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.
11923static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
11924{
11925 if (target <= snap_min + snap_threshold)
11926 return ImLerp(a: snap_min, b: target, t: center_ratio);
11927 if (target >= snap_max - snap_threshold)
11928 return ImLerp(a: target, b: snap_max, t: center_ratio);
11929 return target;
11930}
11931
11932static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
11933{
11934 ImVec2 scroll = window->Scroll;
11935 ImVec2 decoration_size(window->DecoOuterSizeX1 + window->DecoInnerSizeX1 + window->DecoOuterSizeX2, window->DecoOuterSizeY1 + window->DecoInnerSizeY1 + window->DecoOuterSizeY2);
11936 for (int axis = 0; axis < 2; axis++)
11937 {
11938 if (window->ScrollTarget[axis] < FLT_MAX)
11939 {
11940 float center_ratio = window->ScrollTargetCenterRatio[axis];
11941 float scroll_target = window->ScrollTarget[axis];
11942 if (window->ScrollTargetEdgeSnapDist[axis] > 0.0f)
11943 {
11944 float snap_min = 0.0f;
11945 float snap_max = window->ScrollMax[axis] + window->SizeFull[axis] - decoration_size[axis];
11946 scroll_target = CalcScrollEdgeSnap(target: scroll_target, snap_min, snap_max, snap_threshold: window->ScrollTargetEdgeSnapDist[axis], center_ratio);
11947 }
11948 scroll[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]);
11949 }
11950 scroll[axis] = IM_ROUND(ImMax(scroll[axis], 0.0f));
11951 if (!window->Collapsed && !window->SkipItems)
11952 scroll[axis] = ImMin(lhs: scroll[axis], rhs: window->ScrollMax[axis]);
11953 }
11954 return scroll;
11955}
11956
11957void ImGui::ScrollToItem(ImGuiScrollFlags flags)
11958{
11959 ImGuiContext& g = *GImGui;
11960 ImGuiWindow* window = g.CurrentWindow;
11961 ScrollToRectEx(window, rect: g.LastItemData.NavRect, flags);
11962}
11963
11964void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
11965{
11966 ScrollToRectEx(window, rect: item_rect, flags);
11967}
11968
11969// Scroll to keep newly navigated item fully into view
11970ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
11971{
11972 ImGuiContext& g = *GImGui;
11973 ImRect scroll_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
11974 scroll_rect.Min.x = ImMin(lhs: scroll_rect.Min.x + window->DecoInnerSizeX1, rhs: scroll_rect.Max.x);
11975 scroll_rect.Min.y = ImMin(lhs: scroll_rect.Min.y + window->DecoInnerSizeY1, rhs: scroll_rect.Max.y);
11976 //GetForegroundDrawList(window)->AddRect(item_rect.Min, item_rect.Max, IM_COL32(255,0,0,255), 0.0f, 0, 5.0f); // [DEBUG]
11977 //GetForegroundDrawList(window)->AddRect(scroll_rect.Min, scroll_rect.Max, IM_COL32_WHITE); // [DEBUG]
11978
11979 // Check that only one behavior is selected per axis
11980 IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_));
11981 IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_));
11982
11983 // Defaults
11984 ImGuiScrollFlags in_flags = flags;
11985 if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX)
11986 flags |= ImGuiScrollFlags_KeepVisibleEdgeX;
11987 if ((flags & ImGuiScrollFlags_MaskY_) == 0)
11988 flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY;
11989
11990 const bool fully_visible_x = item_rect.Min.x >= scroll_rect.Min.x && item_rect.Max.x <= scroll_rect.Max.x;
11991 const bool fully_visible_y = item_rect.Min.y >= scroll_rect.Min.y && item_rect.Max.y <= scroll_rect.Max.y;
11992 const bool can_be_fully_visible_x = (item_rect.GetWidth() + g.Style.ItemSpacing.x * 2.0f) <= scroll_rect.GetWidth() || (window->AutoFitFramesX > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0;
11993 const bool can_be_fully_visible_y = (item_rect.GetHeight() + g.Style.ItemSpacing.y * 2.0f) <= scroll_rect.GetHeight() || (window->AutoFitFramesY > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0;
11994
11995 if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x)
11996 {
11997 if (item_rect.Min.x < scroll_rect.Min.x || !can_be_fully_visible_x)
11998 SetScrollFromPosX(window, local_x: item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, center_x_ratio: 0.0f);
11999 else if (item_rect.Max.x >= scroll_rect.Max.x)
12000 SetScrollFromPosX(window, local_x: item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, center_x_ratio: 1.0f);
12001 }
12002 else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX))
12003 {
12004 if (can_be_fully_visible_x)
12005 SetScrollFromPosX(window, local_x: ImTrunc(f: (item_rect.Min.x + item_rect.Max.x) * 0.5f) - window->Pos.x, center_x_ratio: 0.5f);
12006 else
12007 SetScrollFromPosX(window, local_x: item_rect.Min.x - window->Pos.x, center_x_ratio: 0.0f);
12008 }
12009
12010 if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y)
12011 {
12012 if (item_rect.Min.y < scroll_rect.Min.y || !can_be_fully_visible_y)
12013 SetScrollFromPosY(window, local_y: item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, center_y_ratio: 0.0f);
12014 else if (item_rect.Max.y >= scroll_rect.Max.y)
12015 SetScrollFromPosY(window, local_y: item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, center_y_ratio: 1.0f);
12016 }
12017 else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY))
12018 {
12019 if (can_be_fully_visible_y)
12020 SetScrollFromPosY(window, local_y: ImTrunc(f: (item_rect.Min.y + item_rect.Max.y) * 0.5f) - window->Pos.y, center_y_ratio: 0.5f);
12021 else
12022 SetScrollFromPosY(window, local_y: item_rect.Min.y - window->Pos.y, center_y_ratio: 0.0f);
12023 }
12024
12025 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
12026 ImVec2 delta_scroll = next_scroll - window->Scroll;
12027
12028 // Also scroll parent window to keep us into view if necessary
12029 if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow))
12030 {
12031 // FIXME-SCROLL: May be an option?
12032 if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0)
12033 in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX;
12034 if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0)
12035 in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY;
12036 delta_scroll += ScrollToRectEx(window: window->ParentWindow, item_rect: ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), flags: in_flags);
12037 }
12038
12039 return delta_scroll;
12040}
12041
12042float ImGui::GetScrollX()
12043{
12044 ImGuiWindow* window = GImGui->CurrentWindow;
12045 return window->Scroll.x;
12046}
12047
12048float ImGui::GetScrollY()
12049{
12050 ImGuiWindow* window = GImGui->CurrentWindow;
12051 return window->Scroll.y;
12052}
12053
12054float ImGui::GetScrollMaxX()
12055{
12056 ImGuiWindow* window = GImGui->CurrentWindow;
12057 return window->ScrollMax.x;
12058}
12059
12060float ImGui::GetScrollMaxY()
12061{
12062 ImGuiWindow* window = GImGui->CurrentWindow;
12063 return window->ScrollMax.y;
12064}
12065
12066void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)
12067{
12068 window->ScrollTarget.x = scroll_x;
12069 window->ScrollTargetCenterRatio.x = 0.0f;
12070 window->ScrollTargetEdgeSnapDist.x = 0.0f;
12071}
12072
12073void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)
12074{
12075 window->ScrollTarget.y = scroll_y;
12076 window->ScrollTargetCenterRatio.y = 0.0f;
12077 window->ScrollTargetEdgeSnapDist.y = 0.0f;
12078}
12079
12080void ImGui::SetScrollX(float scroll_x)
12081{
12082 ImGuiContext& g = *GImGui;
12083 SetScrollX(window: g.CurrentWindow, scroll_x);
12084}
12085
12086void ImGui::SetScrollY(float scroll_y)
12087{
12088 ImGuiContext& g = *GImGui;
12089 SetScrollY(window: g.CurrentWindow, scroll_y);
12090}
12091
12092// Note that a local position will vary depending on initial scroll value,
12093// This is a little bit confusing so bear with us:
12094// - local_pos = (absolution_pos - window->Pos)
12095// - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,
12096// and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.
12097// - They mostly exist because of legacy API.
12098// Following the rules above, when trying to work with scrolling code, consider that:
12099// - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!
12100// - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense
12101// 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
12102void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
12103{
12104 IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
12105 window->ScrollTarget.x = IM_TRUNC(local_x - window->DecoOuterSizeX1 - window->DecoInnerSizeX1 + window->Scroll.x); // Convert local position to scroll offset
12106 window->ScrollTargetCenterRatio.x = center_x_ratio;
12107 window->ScrollTargetEdgeSnapDist.x = 0.0f;
12108}
12109
12110void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
12111{
12112 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
12113 window->ScrollTarget.y = IM_TRUNC(local_y - window->DecoOuterSizeY1 - window->DecoInnerSizeY1 + window->Scroll.y); // Convert local position to scroll offset
12114 window->ScrollTargetCenterRatio.y = center_y_ratio;
12115 window->ScrollTargetEdgeSnapDist.y = 0.0f;
12116}
12117
12118void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
12119{
12120 ImGuiContext& g = *GImGui;
12121 SetScrollFromPosX(window: g.CurrentWindow, local_x, center_x_ratio);
12122}
12123
12124void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
12125{
12126 ImGuiContext& g = *GImGui;
12127 SetScrollFromPosY(window: g.CurrentWindow, local_y, center_y_ratio);
12128}
12129
12130// center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item.
12131void ImGui::SetScrollHereX(float center_x_ratio)
12132{
12133 ImGuiContext& g = *GImGui;
12134 ImGuiWindow* window = g.CurrentWindow;
12135 float spacing_x = ImMax(lhs: window->WindowPadding.x, rhs: g.Style.ItemSpacing.x);
12136 float target_pos_x = ImLerp(a: g.LastItemData.Rect.Min.x - spacing_x, b: g.LastItemData.Rect.Max.x + spacing_x, t: center_x_ratio);
12137 SetScrollFromPosX(window, local_x: target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos
12138
12139 // Tweak: snap on edges when aiming at an item very close to the edge
12140 window->ScrollTargetEdgeSnapDist.x = ImMax(lhs: 0.0f, rhs: window->WindowPadding.x - spacing_x);
12141}
12142
12143// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
12144void ImGui::SetScrollHereY(float center_y_ratio)
12145{
12146 ImGuiContext& g = *GImGui;
12147 ImGuiWindow* window = g.CurrentWindow;
12148 float spacing_y = ImMax(lhs: window->WindowPadding.y, rhs: g.Style.ItemSpacing.y);
12149 float target_pos_y = ImLerp(a: window->DC.CursorPosPrevLine.y - spacing_y, b: window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, t: center_y_ratio);
12150 SetScrollFromPosY(window, local_y: target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos
12151
12152 // Tweak: snap on edges when aiming at an item very close to the edge
12153 window->ScrollTargetEdgeSnapDist.y = ImMax(lhs: 0.0f, rhs: window->WindowPadding.y - spacing_y);
12154}
12155
12156//-----------------------------------------------------------------------------
12157// [SECTION] TOOLTIPS
12158//-----------------------------------------------------------------------------
12159
12160bool ImGui::BeginTooltip()
12161{
12162 return BeginTooltipEx(tooltip_flags: ImGuiTooltipFlags_None, extra_window_flags: ImGuiWindowFlags_None);
12163}
12164
12165bool ImGui::BeginItemTooltip()
12166{
12167 if (!IsItemHovered(flags: ImGuiHoveredFlags_ForTooltip))
12168 return false;
12169 return BeginTooltipEx(tooltip_flags: ImGuiTooltipFlags_None, extra_window_flags: ImGuiWindowFlags_None);
12170}
12171
12172bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags)
12173{
12174 ImGuiContext& g = *GImGui;
12175
12176 const bool is_dragdrop_tooltip = g.DragDropWithinSource || g.DragDropWithinTarget;
12177 if (is_dragdrop_tooltip)
12178 {
12179 // Drag and Drop tooltips are positioning differently than other tooltips:
12180 // - offset visibility to increase visibility around mouse.
12181 // - never clamp within outer viewport boundary.
12182 // We call SetNextWindowPos() to enforce position and disable clamping.
12183 // See FindBestWindowPosForPopup() for positionning logic of other tooltips (not drag and drop ones).
12184 //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
12185 const bool is_touchscreen = (g.IO.MouseSource == ImGuiMouseSource_TouchScreen);
12186 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
12187 {
12188 ImVec2 tooltip_pos = is_touchscreen ? (g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_TOUCH * g.Style.MouseCursorScale) : (g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_MOUSE * g.Style.MouseCursorScale);
12189 ImVec2 tooltip_pivot = is_touchscreen ? TOOLTIP_DEFAULT_PIVOT_TOUCH : ImVec2(0.0f, 0.0f);
12190 SetNextWindowPos(pos: tooltip_pos, cond: ImGuiCond_None, pivot: tooltip_pivot);
12191 }
12192
12193 SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
12194 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
12195 tooltip_flags |= ImGuiTooltipFlags_OverridePrevious;
12196 }
12197
12198 const char* window_name_template = is_dragdrop_tooltip ? "##Tooltip_DragDrop_%02d" : "##Tooltip_%02d";
12199 char window_name[32];
12200 ImFormatString(buf: window_name, IM_ARRAYSIZE(window_name), fmt: window_name_template, g.TooltipOverrideCount);
12201 if ((tooltip_flags & ImGuiTooltipFlags_OverridePrevious) && g.TooltipPreviousWindow != NULL && g.TooltipPreviousWindow->Active)
12202 {
12203 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
12204 //IMGUI_DEBUG_LOG("[tooltip] '%s' already active, using +1 for this frame\n", window_name);
12205 SetWindowHiddenAndSkipItemsForCurrentFrame(g.TooltipPreviousWindow);
12206 ImFormatString(buf: window_name, IM_ARRAYSIZE(window_name), fmt: window_name_template, ++g.TooltipOverrideCount);
12207 }
12208
12209 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking;
12210 Begin(name: window_name, NULL, flags: flags | extra_window_flags);
12211 // 2023-03-09: Added bool return value to the API, but currently always returning true.
12212 // If this ever returns false we need to update BeginDragDropSource() accordingly.
12213 //if (!ret)
12214 // End();
12215 //return ret;
12216 return true;
12217}
12218
12219void ImGui::EndTooltip()
12220{
12221 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
12222 End();
12223}
12224
12225void ImGui::SetTooltip(const char* fmt, ...)
12226{
12227 va_list args;
12228 va_start(args, fmt);
12229 SetTooltipV(fmt, args);
12230 va_end(args);
12231}
12232
12233void ImGui::SetTooltipV(const char* fmt, va_list args)
12234{
12235 if (!BeginTooltipEx(tooltip_flags: ImGuiTooltipFlags_OverridePrevious, extra_window_flags: ImGuiWindowFlags_None))
12236 return;
12237 TextV(fmt, args);
12238 EndTooltip();
12239}
12240
12241// Shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'.
12242// Defaults to == ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort when using the mouse.
12243void ImGui::SetItemTooltip(const char* fmt, ...)
12244{
12245 va_list args;
12246 va_start(args, fmt);
12247 if (IsItemHovered(flags: ImGuiHoveredFlags_ForTooltip))
12248 SetTooltipV(fmt, args);
12249 va_end(args);
12250}
12251
12252void ImGui::SetItemTooltipV(const char* fmt, va_list args)
12253{
12254 if (IsItemHovered(flags: ImGuiHoveredFlags_ForTooltip))
12255 SetTooltipV(fmt, args);
12256}
12257
12258
12259//-----------------------------------------------------------------------------
12260// [SECTION] POPUPS
12261//-----------------------------------------------------------------------------
12262
12263// Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
12264bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
12265{
12266 ImGuiContext& g = *GImGui;
12267 if (popup_flags & ImGuiPopupFlags_AnyPopupId)
12268 {
12269 // Return true if any popup is open at the current BeginPopup() level of the popup stack
12270 // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
12271 IM_ASSERT(id == 0);
12272 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
12273 return g.OpenPopupStack.Size > 0;
12274 else
12275 return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
12276 }
12277 else
12278 {
12279 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
12280 {
12281 // Return true if the popup is open anywhere in the popup stack
12282 for (ImGuiPopupData& popup_data : g.OpenPopupStack)
12283 if (popup_data.PopupId == id)
12284 return true;
12285 return false;
12286 }
12287 else
12288 {
12289 // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
12290 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
12291 }
12292 }
12293}
12294
12295bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
12296{
12297 ImGuiContext& g = *GImGui;
12298 ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str: str_id);
12299 if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
12300 IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
12301 return IsPopupOpen(id, popup_flags);
12302}
12303
12304// Also see FindBlockingModal(NULL)
12305ImGuiWindow* ImGui::GetTopMostPopupModal()
12306{
12307 ImGuiContext& g = *GImGui;
12308 for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
12309 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
12310 if (popup->Flags & ImGuiWindowFlags_Modal)
12311 return popup;
12312 return NULL;
12313}
12314
12315// See Demo->Stacked Modal to confirm what this is for.
12316ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal()
12317{
12318 ImGuiContext& g = *GImGui;
12319 for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
12320 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
12321 if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(window: popup))
12322 return popup;
12323 return NULL;
12324}
12325
12326void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
12327{
12328 ImGuiContext& g = *GImGui;
12329 ImGuiID id = g.CurrentWindow->GetID(str: str_id);
12330 IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopup(\"%s\" -> 0x%08X)\n", str_id, id);
12331 OpenPopupEx(id, popup_flags);
12332}
12333
12334void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags)
12335{
12336 OpenPopupEx(id, popup_flags);
12337}
12338
12339// Mark popup as open (toggle toward open state).
12340// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
12341// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
12342// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
12343void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
12344{
12345 ImGuiContext& g = *GImGui;
12346 ImGuiWindow* parent_window = g.CurrentWindow;
12347 const int current_stack_size = g.BeginPopupStack.Size;
12348
12349 if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
12350 if (IsPopupOpen(id: (ImGuiID)0, popup_flags: ImGuiPopupFlags_AnyPopupId))
12351 return;
12352
12353 ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
12354 popup_ref.PopupId = id;
12355 popup_ref.Window = NULL;
12356 popup_ref.RestoreNavWindow = g.NavWindow; // When popup closes focus may be restored to NavWindow (depend on window type).
12357 popup_ref.OpenFrameCount = g.FrameCount;
12358 popup_ref.OpenParentId = parent_window->IDStack.back();
12359 popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
12360 popup_ref.OpenMousePos = IsMousePosValid(mouse_pos: &g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
12361
12362 IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopupEx(0x%08X)\n", id);
12363 if (g.OpenPopupStack.Size < current_stack_size + 1)
12364 {
12365 g.OpenPopupStack.push_back(v: popup_ref);
12366 }
12367 else
12368 {
12369 // Gently handle the user mistakenly calling OpenPopup() every frames: it is likely a programming mistake!
12370 // However, if we were to run the regular code path, the ui would become completely unusable because the popup will always be
12371 // in hidden-while-calculating-size state _while_ claiming focus. Which is extremely confusing situation for the programmer.
12372 // Instead, for successive frames calls to OpenPopup(), we silently avoid reopening even if ImGuiPopupFlags_NoReopen is not specified.
12373 bool keep_existing = false;
12374 if (g.OpenPopupStack[current_stack_size].PopupId == id)
12375 if ((g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) || (popup_flags & ImGuiPopupFlags_NoReopen))
12376 keep_existing = true;
12377 if (keep_existing)
12378 {
12379 // No reopen
12380 g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
12381 }
12382 else
12383 {
12384 // Reopen: close child popups if any, then flag popup for open/reopen (set position, focus, init navigation)
12385 ClosePopupToLevel(remaining: current_stack_size, restore_focus_to_window_under_popup: true);
12386 g.OpenPopupStack.push_back(v: popup_ref);
12387 }
12388
12389 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
12390 // This is equivalent to what ClosePopupToLevel() does.
12391 //if (g.OpenPopupStack[current_stack_size].PopupId == id)
12392 // FocusWindow(parent_window);
12393 }
12394}
12395
12396// When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
12397// This function closes any popups that are over 'ref_window'.
12398void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
12399{
12400 ImGuiContext& g = *GImGui;
12401 if (g.OpenPopupStack.Size == 0)
12402 return;
12403
12404 // Don't close our own child popup windows.
12405 //IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\") restore_under=%d\n", ref_window ? ref_window->Name : "<NULL>", restore_focus_to_window_under_popup);
12406 int popup_count_to_keep = 0;
12407 if (ref_window)
12408 {
12409 // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
12410 for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
12411 {
12412 ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
12413 if (!popup.Window)
12414 continue;
12415 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
12416
12417 // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
12418 // - Clicking/Focusing Window2 won't close Popup1:
12419 // Window -> Popup1 -> Window2(Ref)
12420 // - Clicking/focusing Popup1 will close Popup2 and Popup3:
12421 // Window -> Popup1(Ref) -> Popup2 -> Popup3
12422 // - Each popups may contain child windows, which is why we compare ->RootWindowDockTree!
12423 // Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
12424 // We step through every popup from bottom to top to validate their position relative to reference window.
12425 bool ref_window_is_descendent_of_popup = false;
12426 for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
12427 if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
12428 //if (popup_window->RootWindowDockTree == ref_window->RootWindowDockTree) // FIXME-MERGE
12429 if (IsWindowWithinBeginStackOf(window: ref_window, potential_parent: popup_window))
12430 {
12431 ref_window_is_descendent_of_popup = true;
12432 break;
12433 }
12434 if (!ref_window_is_descendent_of_popup)
12435 break;
12436 }
12437 }
12438 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
12439 {
12440 IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\")\n", ref_window ? ref_window->Name : "<NULL>");
12441 ClosePopupToLevel(remaining: popup_count_to_keep, restore_focus_to_window_under_popup);
12442 }
12443}
12444
12445void ImGui::ClosePopupsExceptModals()
12446{
12447 ImGuiContext& g = *GImGui;
12448
12449 int popup_count_to_keep;
12450 for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--)
12451 {
12452 ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window;
12453 if (!window || (window->Flags & ImGuiWindowFlags_Modal))
12454 break;
12455 }
12456 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
12457 ClosePopupToLevel(remaining: popup_count_to_keep, restore_focus_to_window_under_popup: true);
12458}
12459
12460void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
12461{
12462 ImGuiContext& g = *GImGui;
12463 IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupToLevel(%d), restore_under=%d\n", remaining, restore_focus_to_window_under_popup);
12464 IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
12465 if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup)
12466 for (int n = remaining; n < g.OpenPopupStack.Size; n++)
12467 IMGUI_DEBUG_LOG_POPUP("[popup] - Closing PopupID 0x%08X Window \"%s\"\n", g.OpenPopupStack[n].PopupId, g.OpenPopupStack[n].Window ? g.OpenPopupStack[n].Window->Name : NULL);
12468
12469 // Trim open popup stack
12470 ImGuiPopupData prev_popup = g.OpenPopupStack[remaining];
12471 g.OpenPopupStack.resize(new_size: remaining);
12472
12473 // Restore focus (unless popup window was not yet submitted, and didn't have a chance to take focus anyhow. See #7325 for an edge case)
12474 if (restore_focus_to_window_under_popup && prev_popup.Window)
12475 {
12476 ImGuiWindow* popup_window = prev_popup.Window;
12477 ImGuiWindow* focus_window = (popup_window->Flags & ImGuiWindowFlags_ChildMenu) ? popup_window->ParentWindow : prev_popup.RestoreNavWindow;
12478 if (focus_window && !focus_window->WasActive)
12479 FocusTopMostWindowUnderOne(under_this_window: popup_window, NULL, NULL, flags: ImGuiFocusRequestFlags_RestoreFocusedChild); // Fallback
12480 else
12481 FocusWindow(window: focus_window, flags: (g.NavLayer == ImGuiNavLayer_Main) ? ImGuiFocusRequestFlags_RestoreFocusedChild : ImGuiFocusRequestFlags_None);
12482 }
12483}
12484
12485// Close the popup we have begin-ed into.
12486void ImGui::CloseCurrentPopup()
12487{
12488 ImGuiContext& g = *GImGui;
12489 int popup_idx = g.BeginPopupStack.Size - 1;
12490 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
12491 return;
12492
12493 // Closing a menu closes its top-most parent popup (unless a modal)
12494 while (popup_idx > 0)
12495 {
12496 ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
12497 ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
12498 bool close_parent = false;
12499 if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
12500 if (parent_popup_window && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar))
12501 close_parent = true;
12502 if (!close_parent)
12503 break;
12504 popup_idx--;
12505 }
12506 IMGUI_DEBUG_LOG_POPUP("[popup] CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
12507 ClosePopupToLevel(remaining: popup_idx, restore_focus_to_window_under_popup: true);
12508
12509 // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
12510 // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
12511 // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
12512 if (ImGuiWindow* window = g.NavWindow)
12513 window->DC.NavHideHighlightOneFrame = true;
12514}
12515
12516// Attention! BeginPopup() adds default flags when calling BeginPopupEx()!
12517bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags)
12518{
12519 ImGuiContext& g = *GImGui;
12520 if (!IsPopupOpen(id, popup_flags: ImGuiPopupFlags_None))
12521 {
12522 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12523 return false;
12524 }
12525
12526 char name[20];
12527 if (extra_window_flags & ImGuiWindowFlags_ChildMenu)
12528 ImFormatString(buf: name, IM_ARRAYSIZE(name), fmt: "##Menu_%02d", g.BeginMenuDepth); // Recycle windows based on depth
12529 else
12530 ImFormatString(buf: name, IM_ARRAYSIZE(name), fmt: "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
12531
12532 bool is_open = Begin(name, NULL, flags: extra_window_flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoDocking);
12533 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
12534 EndPopup();
12535
12536 //g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack;
12537
12538 return is_open;
12539}
12540
12541bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
12542{
12543 ImGuiContext& g = *GImGui;
12544 if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
12545 {
12546 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12547 return false;
12548 }
12549 flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
12550 ImGuiID id = g.CurrentWindow->GetID(str: str_id);
12551 return BeginPopupEx(id, extra_window_flags: flags);
12552}
12553
12554// If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
12555// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup).
12556// - *p_open set back to false in BeginPopupModal() when popup is not open.
12557// - if you set *p_open to false before calling BeginPopupModal(), it will close the popup.
12558bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
12559{
12560 ImGuiContext& g = *GImGui;
12561 ImGuiWindow* window = g.CurrentWindow;
12562 const ImGuiID id = window->GetID(str: name);
12563 if (!IsPopupOpen(id, popup_flags: ImGuiPopupFlags_None))
12564 {
12565 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12566 if (p_open && *p_open)
12567 *p_open = false;
12568 return false;
12569 }
12570
12571 // Center modal windows by default for increased visibility
12572 // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
12573 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
12574 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
12575 {
12576 const ImGuiViewport* viewport = window->WasActive ? window->Viewport : GetMainViewport(); // FIXME-VIEWPORT: What may be our reference viewport?
12577 SetNextWindowPos(pos: viewport->GetCenter(), cond: ImGuiCond_FirstUseEver, pivot: ImVec2(0.5f, 0.5f));
12578 }
12579
12580 flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking;
12581 const bool is_open = Begin(name, p_open, flags);
12582 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
12583 {
12584 EndPopup();
12585 if (is_open)
12586 ClosePopupToLevel(remaining: g.BeginPopupStack.Size, restore_focus_to_window_under_popup: true);
12587 return false;
12588 }
12589 return is_open;
12590}
12591
12592void ImGui::EndPopup()
12593{
12594 ImGuiContext& g = *GImGui;
12595 ImGuiWindow* window = g.CurrentWindow;
12596 IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
12597 IM_ASSERT(g.BeginPopupStack.Size > 0);
12598
12599 // Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests)
12600 if (g.NavWindow == window)
12601 NavMoveRequestTryWrapping(window, move_flags: ImGuiNavMoveFlags_LoopY);
12602
12603 // Child-popups don't need to be laid out
12604 IM_ASSERT(g.WithinEndChild == false);
12605 if (window->Flags & ImGuiWindowFlags_ChildWindow)
12606 g.WithinEndChild = true;
12607 End();
12608 g.WithinEndChild = false;
12609}
12610
12611// Helper to open a popup if mouse button is released over the item
12612// - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()
12613void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)
12614{
12615 ImGuiContext& g = *GImGui;
12616 ImGuiWindow* window = g.CurrentWindow;
12617 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12618 if (IsMouseReleased(button: mouse_button) && IsItemHovered(flags: ImGuiHoveredFlags_AllowWhenBlockedByPopup))
12619 {
12620 ImGuiID id = str_id ? window->GetID(str: str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
12621 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
12622 OpenPopupEx(id, popup_flags);
12623 }
12624}
12625
12626// This is a helper to handle the simplest case of associating one named popup to one given widget.
12627// - To create a popup associated to the last item, you generally want to pass a NULL value to str_id.
12628// - To create a popup with a specific identifier, pass it in str_id.
12629// - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call.
12630// - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id.
12631// - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
12632// This is essentially the same as:
12633// id = str_id ? GetID(str_id) : GetItemID();
12634// OpenPopupOnItemClick(str_id, ImGuiPopupFlags_MouseButtonRight);
12635// return BeginPopup(id);
12636// Which is essentially the same as:
12637// id = str_id ? GetID(str_id) : GetItemID();
12638// if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
12639// OpenPopup(id);
12640// return BeginPopup(id);
12641// The main difference being that this is tweaked to avoid computing the ID twice.
12642bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
12643{
12644 ImGuiContext& g = *GImGui;
12645 ImGuiWindow* window = g.CurrentWindow;
12646 if (window->SkipItems)
12647 return false;
12648 ImGuiID id = str_id ? window->GetID(str: str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
12649 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
12650 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12651 if (IsMouseReleased(button: mouse_button) && IsItemHovered(flags: ImGuiHoveredFlags_AllowWhenBlockedByPopup))
12652 OpenPopupEx(id, popup_flags);
12653 return BeginPopupEx(id, extra_window_flags: ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12654}
12655
12656bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
12657{
12658 ImGuiContext& g = *GImGui;
12659 ImGuiWindow* window = g.CurrentWindow;
12660 if (!str_id)
12661 str_id = "window_context";
12662 ImGuiID id = window->GetID(str: str_id);
12663 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12664 if (IsMouseReleased(button: mouse_button) && IsWindowHovered(flags: ImGuiHoveredFlags_AllowWhenBlockedByPopup))
12665 if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
12666 OpenPopupEx(id, popup_flags);
12667 return BeginPopupEx(id, extra_window_flags: ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12668}
12669
12670bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
12671{
12672 ImGuiContext& g = *GImGui;
12673 ImGuiWindow* window = g.CurrentWindow;
12674 if (!str_id)
12675 str_id = "void_context";
12676 ImGuiID id = window->GetID(str: str_id);
12677 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12678 if (IsMouseReleased(button: mouse_button) && !IsWindowHovered(flags: ImGuiHoveredFlags_AnyWindow))
12679 if (GetTopMostPopupModal() == NULL)
12680 OpenPopupEx(id, popup_flags);
12681 return BeginPopupEx(id, extra_window_flags: ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12682}
12683
12684// 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.)
12685// 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.
12686// (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor
12687// information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.
12688// this allows us to have tooltips/popups displayed out of the parent viewport.)
12689ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
12690{
12691 ImVec2 base_pos_clamped = ImClamp(v: ref_pos, mn: r_outer.Min, mx: r_outer.Max - size);
12692 //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
12693 //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
12694
12695 // Combo Box policy (we want a connecting edge)
12696 if (policy == ImGuiPopupPositionPolicy_ComboBox)
12697 {
12698 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
12699 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
12700 {
12701 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
12702 if (n != -1 && dir == *last_dir) // Already tried this direction?
12703 continue;
12704 ImVec2 pos;
12705 if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
12706 if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
12707 if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
12708 if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
12709 if (!r_outer.Contains(r: ImRect(pos, pos + size)))
12710 continue;
12711 *last_dir = dir;
12712 return pos;
12713 }
12714 }
12715
12716 // Tooltip and Default popup policy
12717 // (Always first try the direction we used on the last frame, if any)
12718 if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default)
12719 {
12720 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
12721 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
12722 {
12723 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
12724 if (n != -1 && dir == *last_dir) // Already tried this direction?
12725 continue;
12726
12727 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);
12728 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);
12729
12730 // If there's 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)
12731 if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right))
12732 continue;
12733 if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down))
12734 continue;
12735
12736 ImVec2 pos;
12737 pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
12738 pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
12739
12740 // Clamp top-left corner of popup
12741 pos.x = ImMax(lhs: pos.x, rhs: r_outer.Min.x);
12742 pos.y = ImMax(lhs: pos.y, rhs: r_outer.Min.y);
12743
12744 *last_dir = dir;
12745 return pos;
12746 }
12747 }
12748
12749 // Fallback when not enough room:
12750 *last_dir = ImGuiDir_None;
12751
12752 // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
12753 if (policy == ImGuiPopupPositionPolicy_Tooltip)
12754 return ref_pos + ImVec2(2, 2);
12755
12756 // Otherwise try to keep within display
12757 ImVec2 pos = ref_pos;
12758 pos.x = ImMax(lhs: ImMin(lhs: pos.x + size.x, rhs: r_outer.Max.x) - size.x, rhs: r_outer.Min.x);
12759 pos.y = ImMax(lhs: ImMin(lhs: pos.y + size.y, rhs: r_outer.Max.y) - size.y, rhs: r_outer.Min.y);
12760 return pos;
12761}
12762
12763// Note that this is used for popups, which can overlap the non work-area of individual viewports.
12764ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window)
12765{
12766 ImGuiContext& g = *GImGui;
12767 ImRect r_screen;
12768 if (window->ViewportAllowPlatformMonitorExtend >= 0)
12769 {
12770 // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here)
12771 const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportAllowPlatformMonitorExtend];
12772 r_screen.Min = monitor.WorkPos;
12773 r_screen.Max = monitor.WorkPos + monitor.WorkSize;
12774 }
12775 else
12776 {
12777 // Use the full viewport area (not work area) for popups
12778 r_screen = window->Viewport->GetMainRect();
12779 }
12780 ImVec2 padding = g.Style.DisplaySafeAreaPadding;
12781 r_screen.Expand(amount: ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
12782 return r_screen;
12783}
12784
12785ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
12786{
12787 ImGuiContext& g = *GImGui;
12788
12789 ImRect r_outer = GetPopupAllowedExtentRect(window);
12790 if (window->Flags & ImGuiWindowFlags_ChildMenu)
12791 {
12792 // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
12793 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
12794 ImGuiWindow* parent_window = window->ParentWindow;
12795 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).
12796 ImRect r_avoid;
12797 if (parent_window->DC.MenuBarAppending)
12798 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
12799 else
12800 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);
12801 return FindBestWindowPosForPopupEx(ref_pos: window->Pos, size: window->Size, last_dir: &window->AutoPosLastDirection, r_outer, r_avoid, policy: ImGuiPopupPositionPolicy_Default);
12802 }
12803 if (window->Flags & ImGuiWindowFlags_Popup)
12804 {
12805 return FindBestWindowPosForPopupEx(ref_pos: window->Pos, size: window->Size, last_dir: &window->AutoPosLastDirection, r_outer, r_avoid: ImRect(window->Pos, window->Pos), policy: ImGuiPopupPositionPolicy_Default); // Ideally we'd disable r_avoid here
12806 }
12807 if (window->Flags & ImGuiWindowFlags_Tooltip)
12808 {
12809 // Position tooltip (always follows mouse + clamp within outer boundaries)
12810 // FIXME:
12811 // - Too many paths. One problem is that FindBestWindowPosForPopupEx() doesn't allow passing a suggested position (so touch screen path doesn't use it by default).
12812 // - Drag and drop tooltips are not using this path either: BeginTooltipEx() manually sets their position.
12813 // - Require some tidying up. In theory we could handle both cases in same location, but requires a bit of shuffling
12814 // as drag and drop tooltips are calling SetNextWindowPos() leading to 'window_pos_set_by_api' being set in Begin().
12815 IM_ASSERT(g.CurrentWindow == window);
12816 const float scale = g.Style.MouseCursorScale;
12817 const ImVec2 ref_pos = NavCalcPreferredRefPos();
12818
12819 if (g.IO.MouseSource == ImGuiMouseSource_TouchScreen && NavCalcPreferredRefPosSource() == ImGuiInputSource_Mouse)
12820 {
12821 ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_TOUCH * scale - (TOOLTIP_DEFAULT_PIVOT_TOUCH * window->Size);
12822 if (r_outer.Contains(r: ImRect(tooltip_pos, tooltip_pos + window->Size)))
12823 return tooltip_pos;
12824 }
12825
12826 ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_MOUSE * scale;
12827 ImRect r_avoid;
12828 if (g.NavCursorVisible && g.NavHighlightItemUnderNav && !g.IO.ConfigNavMoveSetMousePos)
12829 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
12830 else
12831 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * scale, ref_pos.y + 24 * scale); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
12832 //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255));
12833
12834 return FindBestWindowPosForPopupEx(ref_pos: tooltip_pos, size: window->Size, last_dir: &window->AutoPosLastDirection, r_outer, r_avoid, policy: ImGuiPopupPositionPolicy_Tooltip);
12835 }
12836 IM_ASSERT(0);
12837 return window->Pos;
12838}
12839
12840//-----------------------------------------------------------------------------
12841// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
12842//-----------------------------------------------------------------------------
12843
12844// FIXME-NAV: The existence of SetNavID vs SetFocusID vs FocusWindow() needs to be clarified/reworked.
12845// In our terminology those should be interchangeable, yet right now this is super confusing.
12846// Those two functions are merely a legacy artifact, so at minimum naming should be clarified.
12847
12848void ImGui::SetNavCursorVisible(bool visible)
12849{
12850 ImGuiContext& g = *GImGui;
12851 if (g.IO.ConfigNavCursorVisibleAlways)
12852 visible = true;
12853 g.NavCursorVisible = visible;
12854}
12855
12856// (was called NavRestoreHighlightAfterMove() before 1.91.4)
12857void ImGui::SetNavCursorVisibleAfterMove()
12858{
12859 ImGuiContext& g = *GImGui;
12860 if (g.IO.ConfigNavCursorVisibleAuto)
12861 g.NavCursorVisible = true;
12862 g.NavHighlightItemUnderNav = g.NavMousePosDirty = true;
12863}
12864
12865void ImGui::SetNavWindow(ImGuiWindow* window)
12866{
12867 ImGuiContext& g = *GImGui;
12868 if (g.NavWindow != window)
12869 {
12870 IMGUI_DEBUG_LOG_FOCUS("[focus] SetNavWindow(\"%s\")\n", window ? window->Name : "<NULL>");
12871 g.NavWindow = window;
12872 g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
12873 }
12874 g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;
12875 NavUpdateAnyRequestFlag();
12876}
12877
12878void ImGui::NavHighlightActivated(ImGuiID id)
12879{
12880 ImGuiContext& g = *GImGui;
12881 g.NavHighlightActivatedId = id;
12882 g.NavHighlightActivatedTimer = NAV_ACTIVATE_HIGHLIGHT_TIMER;
12883}
12884
12885void ImGui::NavClearPreferredPosForAxis(ImGuiAxis axis)
12886{
12887 ImGuiContext& g = *GImGui;
12888 g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer][axis] = FLT_MAX;
12889}
12890
12891void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
12892{
12893 ImGuiContext& g = *GImGui;
12894 IM_ASSERT(g.NavWindow != NULL);
12895 IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu);
12896 g.NavId = id;
12897 g.NavLayer = nav_layer;
12898 SetNavFocusScope(focus_scope_id);
12899 g.NavWindow->NavLastIds[nav_layer] = id;
12900 g.NavWindow->NavRectRel[nav_layer] = rect_rel;
12901
12902 // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
12903 NavClearPreferredPosForAxis(axis: ImGuiAxis_X);
12904 NavClearPreferredPosForAxis(axis: ImGuiAxis_Y);
12905}
12906
12907void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
12908{
12909 ImGuiContext& g = *GImGui;
12910 IM_ASSERT(id != 0);
12911
12912 if (g.NavWindow != window)
12913 SetNavWindow(window);
12914
12915 // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and g.CurrentFocusScopeId are valid.
12916 // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
12917 const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
12918 g.NavId = id;
12919 g.NavLayer = nav_layer;
12920 SetNavFocusScope(g.CurrentFocusScopeId);
12921 window->NavLastIds[nav_layer] = id;
12922 if (g.LastItemData.ID == id)
12923 window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, r: g.LastItemData.NavRect);
12924
12925 if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
12926 g.NavHighlightItemUnderNav = true;
12927 else if (g.IO.ConfigNavCursorVisibleAuto)
12928 g.NavCursorVisible = false;
12929
12930 // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
12931 NavClearPreferredPosForAxis(axis: ImGuiAxis_X);
12932 NavClearPreferredPosForAxis(axis: ImGuiAxis_Y);
12933}
12934
12935static ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
12936{
12937 if (ImFabs(dx) > ImFabs(dy))
12938 return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
12939 return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
12940}
12941
12942static float inline NavScoreItemDistInterval(float cand_min, float cand_max, float curr_min, float curr_max)
12943{
12944 if (cand_max < curr_min)
12945 return cand_max - curr_min;
12946 if (curr_max < cand_min)
12947 return cand_min - curr_max;
12948 return 0.0f;
12949}
12950
12951// Scoring function for keyboard/gamepad directional navigation. Based on https://gist.github.com/rygorous/6981057
12952static bool ImGui::NavScoreItem(ImGuiNavItemData* result)
12953{
12954 ImGuiContext& g = *GImGui;
12955 ImGuiWindow* window = g.CurrentWindow;
12956 if (g.NavLayer != window->DC.NavLayerCurrent)
12957 return false;
12958
12959 // FIXME: Those are not good variables names
12960 ImRect cand = g.LastItemData.NavRect; // Current item nav rectangle
12961 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)
12962 g.NavScoringDebugCount++;
12963
12964 // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
12965 if (window->ParentWindow == g.NavWindow)
12966 {
12967 IM_ASSERT((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened);
12968 if (!window->ClipRect.Overlaps(r: cand))
12969 return false;
12970 cand.ClipWithFull(r: window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
12971 }
12972
12973 // Compute distance between boxes
12974 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
12975 float dbx = NavScoreItemDistInterval(cand_min: cand.Min.x, cand_max: cand.Max.x, curr_min: curr.Min.x, curr_max: curr.Max.x);
12976 float dby = NavScoreItemDistInterval(cand_min: ImLerp(a: cand.Min.y, b: cand.Max.y, t: 0.2f), cand_max: ImLerp(a: cand.Min.y, b: cand.Max.y, t: 0.8f), curr_min: ImLerp(a: curr.Min.y, b: curr.Max.y, t: 0.2f), curr_max: ImLerp(a: curr.Min.y, b: curr.Max.y, t: 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items
12977 if (dby != 0.0f && dbx != 0.0f)
12978 dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
12979 float dist_box = ImFabs(dbx) + ImFabs(dby);
12980
12981 // 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)
12982 float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
12983 float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
12984 float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
12985
12986 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
12987 ImGuiDir quadrant;
12988 float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
12989 if (dbx != 0.0f || dby != 0.0f)
12990 {
12991 // For non-overlapping boxes, use distance between boxes
12992 // FIXME-NAV: Quadrant may be incorrect because of (1) dbx bias and (2) curr.Max.y bias applied by NavBiasScoringRect() where typically curr.Max.y==curr.Min.y
12993 // One typical case where this happens, with style.WindowMenuButtonPosition == ImGuiDir_Right, pressing Left to navigate from Close to Collapse tends to fail.
12994 // Also see #6344. Calling ImGetDirQuadrantFromDelta() with unbiased values may be good but side-effects are plenty.
12995 dax = dbx;
12996 day = dby;
12997 dist_axial = dist_box;
12998 quadrant = ImGetDirQuadrantFromDelta(dx: dbx, dy: dby);
12999 }
13000 else if (dcx != 0.0f || dcy != 0.0f)
13001 {
13002 // For overlapping boxes with different centers, use distance between centers
13003 dax = dcx;
13004 day = dcy;
13005 dist_axial = dist_center;
13006 quadrant = ImGetDirQuadrantFromDelta(dx: dcx, dy: dcy);
13007 }
13008 else
13009 {
13010 // 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)
13011 quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
13012 }
13013
13014 const ImGuiDir move_dir = g.NavMoveDir;
13015#if IMGUI_DEBUG_NAV_SCORING
13016 char buf[200];
13017 if (g.IO.KeyCtrl) // Hold CTRL to preview score in matching quadrant. CTRL+Arrow to rotate.
13018 {
13019 if (quadrant == move_dir)
13020 {
13021 ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
13022 ImDrawList* draw_list = GetForegroundDrawList(window);
13023 draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 80));
13024 draw_list->AddRectFilled(cand.Min, cand.Min + CalcTextSize(buf), IM_COL32(255, 0, 0, 200));
13025 draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf);
13026 }
13027 }
13028 const bool debug_hovering = IsMouseHoveringRect(cand.Min, cand.Max);
13029 const bool debug_tty = (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_Space));
13030 if (debug_hovering || debug_tty)
13031 {
13032 ImFormatString(buf, IM_ARRAYSIZE(buf),
13033 "d-box (%7.3f,%7.3f) -> %7.3f\nd-center (%7.3f,%7.3f) -> %7.3f\nd-axial (%7.3f,%7.3f) -> %7.3f\nnav %c, quadrant %c",
13034 dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "-WENS"[move_dir+1], "-WENS"[quadrant+1]);
13035 if (debug_hovering)
13036 {
13037 ImDrawList* draw_list = GetForegroundDrawList(window);
13038 draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100));
13039 draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255, 255, 0, 200));
13040 draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40, 0, 0, 200));
13041 draw_list->AddText(cand.Max, ~0U, buf);
13042 }
13043 if (debug_tty) { IMGUI_DEBUG_LOG_NAV("id 0x%08X\n%s\n", g.LastItemData.ID, buf); }
13044 }
13045#endif
13046
13047 // Is it in the quadrant we're interested in moving to?
13048 bool new_best = false;
13049 if (quadrant == move_dir)
13050 {
13051 // Does it beat the current best candidate?
13052 if (dist_box < result->DistBox)
13053 {
13054 result->DistBox = dist_box;
13055 result->DistCenter = dist_center;
13056 return true;
13057 }
13058 if (dist_box == result->DistBox)
13059 {
13060 // Try using distance between center points to break ties
13061 if (dist_center < result->DistCenter)
13062 {
13063 result->DistCenter = dist_center;
13064 new_best = true;
13065 }
13066 else if (dist_center == result->DistCenter)
13067 {
13068 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
13069 // (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),
13070 // 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.
13071 if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
13072 new_best = true;
13073 }
13074 }
13075 }
13076
13077 // 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
13078 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
13079 // 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.
13080 // 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.
13081 // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
13082 if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
13083 if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
13084 if ((move_dir == ImGuiDir_Left && dax < 0.0f) || (move_dir == ImGuiDir_Right && dax > 0.0f) || (move_dir == ImGuiDir_Up && day < 0.0f) || (move_dir == ImGuiDir_Down && day > 0.0f))
13085 {
13086 result->DistAxial = dist_axial;
13087 new_best = true;
13088 }
13089
13090 return new_best;
13091}
13092
13093static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)
13094{
13095 ImGuiContext& g = *GImGui;
13096 ImGuiWindow* window = g.CurrentWindow;
13097 result->Window = window;
13098 result->ID = g.LastItemData.ID;
13099 result->FocusScopeId = g.CurrentFocusScopeId;
13100 result->ItemFlags = g.LastItemData.ItemFlags;
13101 result->RectRel = WindowRectAbsToRel(window, r: g.LastItemData.NavRect);
13102 if (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData)
13103 {
13104 IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
13105 result->SelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
13106 }
13107}
13108
13109// True when current work location may be scrolled horizontally when moving left / right.
13110// This is generally always true UNLESS within a column. We don't have a vertical equivalent.
13111void ImGui::NavUpdateCurrentWindowIsScrollPushableX()
13112{
13113 ImGuiContext& g = *GImGui;
13114 ImGuiWindow* window = g.CurrentWindow;
13115 window->DC.NavIsScrollPushableX = (g.CurrentTable == NULL && window->DC.CurrentColumns == NULL);
13116}
13117
13118// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
13119// This is called after LastItemData is set, but NextItemData is also still valid.
13120static void ImGui::NavProcessItem()
13121{
13122 ImGuiContext& g = *GImGui;
13123 ImGuiWindow* window = g.CurrentWindow;
13124 const ImGuiID id = g.LastItemData.ID;
13125 const ImGuiItemFlags item_flags = g.LastItemData.ItemFlags;
13126
13127 // When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221)
13128 if (window->DC.NavIsScrollPushableX == false)
13129 {
13130 g.LastItemData.NavRect.Min.x = ImClamp(v: g.LastItemData.NavRect.Min.x, mn: window->ClipRect.Min.x, mx: window->ClipRect.Max.x);
13131 g.LastItemData.NavRect.Max.x = ImClamp(v: g.LastItemData.NavRect.Max.x, mn: window->ClipRect.Min.x, mx: window->ClipRect.Max.x);
13132 }
13133 const ImRect nav_bb = g.LastItemData.NavRect;
13134
13135 // Process Init Request
13136 if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent && (item_flags & ImGuiItemFlags_Disabled) == 0)
13137 {
13138 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
13139 const bool candidate_for_nav_default_focus = (item_flags & ImGuiItemFlags_NoNavDefaultFocus) == 0;
13140 if (candidate_for_nav_default_focus || g.NavInitResult.ID == 0)
13141 {
13142 NavApplyItemToResult(result: &g.NavInitResult);
13143 }
13144 if (candidate_for_nav_default_focus)
13145 {
13146 g.NavInitRequest = false; // Found a match, clear request
13147 NavUpdateAnyRequestFlag();
13148 }
13149 }
13150
13151 // Process Move Request (scoring for navigation)
13152 // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)
13153 if (g.NavMoveScoringItems && (item_flags & ImGuiItemFlags_Disabled) == 0)
13154 {
13155 if ((g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi) || (window->Flags & ImGuiWindowFlags_NoNavInputs) == 0)
13156 {
13157 const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;
13158 if (is_tabbing)
13159 {
13160 NavProcessItemForTabbingRequest(id, item_flags, move_flags: g.NavMoveFlags);
13161 }
13162 else if (g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId))
13163 {
13164 ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
13165 if (NavScoreItem(result))
13166 NavApplyItemToResult(result);
13167
13168 // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
13169 const float VISIBLE_RATIO = 0.70f;
13170 if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(r: nav_bb))
13171 if (ImClamp(v: nav_bb.Max.y, mn: window->ClipRect.Min.y, mx: window->ClipRect.Max.y) - ImClamp(v: nav_bb.Min.y, mn: window->ClipRect.Min.y, mx: window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)
13172 if (NavScoreItem(result: &g.NavMoveResultLocalVisible))
13173 NavApplyItemToResult(result: &g.NavMoveResultLocalVisible);
13174 }
13175 }
13176 }
13177
13178 // Update information for currently focused/navigated item
13179 if (g.NavId == id)
13180 {
13181 if (g.NavWindow != window)
13182 SetNavWindow(window); // Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window.
13183 g.NavLayer = window->DC.NavLayerCurrent;
13184 SetNavFocusScope(g.CurrentFocusScopeId); // Will set g.NavFocusScopeId AND store g.NavFocusScopePath
13185 g.NavFocusScopeId = g.CurrentFocusScopeId;
13186 g.NavIdIsAlive = true;
13187 if (g.LastItemData.ItemFlags & ImGuiItemFlags_HasSelectionUserData)
13188 {
13189 IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
13190 g.NavLastValidSelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
13191 }
13192 window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, r: nav_bb); // Store item bounding box (relative to window position)
13193 }
13194}
13195
13196// Handle "scoring" of an item for a tabbing/focusing request initiated by NavUpdateCreateTabbingRequest().
13197// Note that SetKeyboardFocusHere() API calls are considered tabbing requests!
13198// - Case 1: no nav/active id: set result to first eligible item, stop storing.
13199// - Case 2: tab forward: on ref id set counter, on counter elapse store result
13200// - Case 3: tab forward wrap: set result to first eligible item (preemptively), on ref id set counter, on next frame if counter hasn't elapsed store result. // FIXME-TABBING: Could be done as a next-frame forwarded request
13201// - Case 4: tab backward: store all results, on ref id pick prev, stop storing
13202// - Case 5: tab backward wrap: store all results, on ref id if no result keep storing until last // FIXME-TABBING: Could be done as next-frame forwarded requested
13203void ImGui::NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags)
13204{
13205 ImGuiContext& g = *GImGui;
13206
13207 if ((move_flags & ImGuiNavMoveFlags_FocusApi) == 0)
13208 {
13209 if (g.NavLayer != g.CurrentWindow->DC.NavLayerCurrent)
13210 return;
13211 if (g.NavFocusScopeId != g.CurrentFocusScopeId)
13212 return;
13213 }
13214
13215 // - Can always land on an item when using API call.
13216 // - Tabbing with _NavEnableKeyboard (space/enter/arrows): goes through every item.
13217 // - Tabbing without _NavEnableKeyboard: goes through inputable items only.
13218 bool can_stop;
13219 if (move_flags & ImGuiNavMoveFlags_FocusApi)
13220 can_stop = true;
13221 else
13222 can_stop = (item_flags & ImGuiItemFlags_NoTabStop) == 0 && ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) || (item_flags & ImGuiItemFlags_Inputable));
13223
13224 // Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows)
13225 ImGuiNavItemData* result = &g.NavMoveResultLocal;
13226 if (g.NavTabbingDir == +1)
13227 {
13228 // Tab Forward or SetKeyboardFocusHere() with >= 0
13229 if (can_stop && g.NavTabbingResultFirst.ID == 0)
13230 NavApplyItemToResult(result: &g.NavTabbingResultFirst);
13231 if (can_stop && g.NavTabbingCounter > 0 && --g.NavTabbingCounter == 0)
13232 NavMoveRequestResolveWithLastItem(result);
13233 else if (g.NavId == id)
13234 g.NavTabbingCounter = 1;
13235 }
13236 else if (g.NavTabbingDir == -1)
13237 {
13238 // Tab Backward
13239 if (g.NavId == id)
13240 {
13241 if (result->ID)
13242 {
13243 g.NavMoveScoringItems = false;
13244 NavUpdateAnyRequestFlag();
13245 }
13246 }
13247 else if (can_stop)
13248 {
13249 // Keep applying until reaching NavId
13250 NavApplyItemToResult(result);
13251 }
13252 }
13253 else if (g.NavTabbingDir == 0)
13254 {
13255 if (can_stop && g.NavId == id)
13256 NavMoveRequestResolveWithLastItem(result);
13257 if (can_stop && g.NavTabbingResultFirst.ID == 0) // Tab init
13258 NavApplyItemToResult(result: &g.NavTabbingResultFirst);
13259 }
13260}
13261
13262bool ImGui::NavMoveRequestButNoResultYet()
13263{
13264 ImGuiContext& g = *GImGui;
13265 return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
13266}
13267
13268// FIXME: ScoringRect is not set
13269void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
13270{
13271 ImGuiContext& g = *GImGui;
13272 IM_ASSERT(g.NavWindow != NULL);
13273 //IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestSubmit: dir %c, window \"%s\"\n", "-WENS"[move_dir + 1], g.NavWindow->Name);
13274
13275 if (move_flags & ImGuiNavMoveFlags_IsTabbing)
13276 move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId;
13277
13278 g.NavMoveSubmitted = g.NavMoveScoringItems = true;
13279 g.NavMoveDir = move_dir;
13280 g.NavMoveDirForDebug = move_dir;
13281 g.NavMoveClipDir = clip_dir;
13282 g.NavMoveFlags = move_flags;
13283 g.NavMoveScrollFlags = scroll_flags;
13284 g.NavMoveForwardToNextFrame = false;
13285 g.NavMoveKeyMods = (move_flags & ImGuiNavMoveFlags_FocusApi) ? 0 : g.IO.KeyMods;
13286 g.NavMoveResultLocal.Clear();
13287 g.NavMoveResultLocalVisible.Clear();
13288 g.NavMoveResultOther.Clear();
13289 g.NavTabbingCounter = 0;
13290 g.NavTabbingResultFirst.Clear();
13291 NavUpdateAnyRequestFlag();
13292}
13293
13294void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result)
13295{
13296 ImGuiContext& g = *GImGui;
13297 g.NavMoveScoringItems = false; // Ensure request doesn't need more processing
13298 NavApplyItemToResult(result);
13299 NavUpdateAnyRequestFlag();
13300}
13301
13302// Called by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere
13303void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiTreeNodeStackData* tree_node_data)
13304{
13305 ImGuiContext& g = *GImGui;
13306 g.NavMoveScoringItems = false;
13307 g.LastItemData.ID = tree_node_data->ID;
13308 g.LastItemData.ItemFlags = tree_node_data->ItemFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper).
13309 g.LastItemData.NavRect = tree_node_data->NavRect;
13310 NavApplyItemToResult(result); // Result this instead of implementing a NavApplyPastTreeNodeToResult()
13311 NavClearPreferredPosForAxis(axis: ImGuiAxis_Y);
13312 NavUpdateAnyRequestFlag();
13313}
13314
13315void ImGui::NavMoveRequestCancel()
13316{
13317 ImGuiContext& g = *GImGui;
13318 g.NavMoveSubmitted = g.NavMoveScoringItems = false;
13319 NavUpdateAnyRequestFlag();
13320}
13321
13322// Forward will reuse the move request again on the next frame (generally with modifications done to it)
13323void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
13324{
13325 ImGuiContext& g = *GImGui;
13326 IM_ASSERT(g.NavMoveForwardToNextFrame == false);
13327 NavMoveRequestCancel();
13328 g.NavMoveForwardToNextFrame = true;
13329 g.NavMoveDir = move_dir;
13330 g.NavMoveClipDir = clip_dir;
13331 g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded;
13332 g.NavMoveScrollFlags = scroll_flags;
13333}
13334
13335// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
13336// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
13337void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags)
13338{
13339 ImGuiContext& g = *GImGui;
13340 IM_ASSERT((wrap_flags & ImGuiNavMoveFlags_WrapMask_ ) != 0 && (wrap_flags & ~ImGuiNavMoveFlags_WrapMask_) == 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY
13341
13342 // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it:
13343 // as NavEndFrame() will do the same test. It will end up calling NavUpdateCreateWrappingRequest().
13344 if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main)
13345 g.NavMoveFlags = (g.NavMoveFlags & ~ImGuiNavMoveFlags_WrapMask_) | wrap_flags;
13346}
13347
13348// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
13349// This way we could find the last focused window among our children. It would be much less confusing this way?
13350static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
13351{
13352 ImGuiWindow* parent = nav_window;
13353 while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
13354 parent = parent->ParentWindow;
13355 if (parent && parent != nav_window)
13356 parent->NavLastChildNavWindow = nav_window;
13357}
13358
13359// Restore the last focused child.
13360// Call when we are expected to land on the Main Layer (0) after FocusWindow()
13361static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
13362{
13363 if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)
13364 return window->NavLastChildNavWindow;
13365 if (window->DockNodeAsHost && window->DockNodeAsHost->TabBar)
13366 if (ImGuiTabItem* tab = TabBarFindMostRecentlySelectedTabForActiveWindow(tab_bar: window->DockNodeAsHost->TabBar))
13367 return tab->Window;
13368 return window;
13369}
13370
13371void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
13372{
13373 ImGuiContext& g = *GImGui;
13374 if (layer == ImGuiNavLayer_Main)
13375 {
13376 ImGuiWindow* prev_nav_window = g.NavWindow;
13377 g.NavWindow = NavRestoreLastChildNavWindow(window: g.NavWindow); // FIXME-NAV: Should clear ongoing nav requests?
13378 g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
13379 if (prev_nav_window)
13380 IMGUI_DEBUG_LOG_FOCUS("[focus] NavRestoreLayer: from \"%s\" to SetNavWindow(\"%s\")\n", prev_nav_window->Name, g.NavWindow->Name);
13381 }
13382 ImGuiWindow* window = g.NavWindow;
13383 if (window->NavLastIds[layer] != 0)
13384 {
13385 SetNavID(id: window->NavLastIds[layer], nav_layer: layer, focus_scope_id: 0, rect_rel: window->NavRectRel[layer]);
13386 }
13387 else
13388 {
13389 g.NavLayer = layer;
13390 NavInitWindow(window, force_reinit: true);
13391 }
13392}
13393
13394static inline void ImGui::NavUpdateAnyRequestFlag()
13395{
13396 ImGuiContext& g = *GImGui;
13397 g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
13398 if (g.NavAnyRequest)
13399 IM_ASSERT(g.NavWindow != NULL);
13400}
13401
13402// This needs to be called before we submit any widget (aka in or before Begin)
13403void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
13404{
13405 // FIXME: ChildWindow test here is wrong for docking
13406 ImGuiContext& g = *GImGui;
13407 IM_ASSERT(window == g.NavWindow);
13408
13409 if (window->Flags & ImGuiWindowFlags_NoNavInputs)
13410 {
13411 g.NavId = 0;
13412 SetNavFocusScope(window->NavRootFocusScopeId);
13413 return;
13414 }
13415
13416 bool init_for_nav = false;
13417 if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
13418 init_for_nav = true;
13419 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
13420 if (init_for_nav)
13421 {
13422 SetNavID(id: 0, nav_layer: g.NavLayer, focus_scope_id: window->NavRootFocusScopeId, rect_rel: ImRect());
13423 g.NavInitRequest = true;
13424 g.NavInitRequestFromMove = false;
13425 g.NavInitResult.ID = 0;
13426 NavUpdateAnyRequestFlag();
13427 }
13428 else
13429 {
13430 g.NavId = window->NavLastIds[0];
13431 SetNavFocusScope(window->NavRootFocusScopeId);
13432 }
13433}
13434
13435static ImGuiInputSource ImGui::NavCalcPreferredRefPosSource()
13436{
13437 ImGuiContext& g = *GImGui;
13438 ImGuiWindow* window = g.NavWindow;
13439 const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
13440
13441 // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag.
13442 if ((!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !window) && !activated_shortcut)
13443 return ImGuiInputSource_Mouse;
13444 else
13445 return ImGuiInputSource_Keyboard; // or Nav in general
13446}
13447
13448static ImVec2 ImGui::NavCalcPreferredRefPos()
13449{
13450 ImGuiContext& g = *GImGui;
13451 ImGuiWindow* window = g.NavWindow;
13452 ImGuiInputSource source = NavCalcPreferredRefPosSource();
13453
13454 const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
13455
13456 // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag.
13457 if (source == ImGuiInputSource_Mouse)
13458 {
13459 // Mouse (we need a fallback in case the mouse becomes invalid after being used)
13460 // The +1.0f offset when stored by OpenPopupEx() allows reopening this or another popup (same or another mouse button) while not moving the mouse, it is pretty standard.
13461 // In theory we could move that +1.0f offset in OpenPopupEx()
13462 ImVec2 p = IsMousePosValid(mouse_pos: &g.IO.MousePos) ? g.IO.MousePos : g.MouseLastValidPos;
13463 return ImVec2(p.x + 1.0f, p.y);
13464 }
13465 else
13466 {
13467 // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item
13468 ImRect ref_rect;
13469 if (activated_shortcut)
13470 ref_rect = g.LastItemData.NavRect;
13471 else
13472 ref_rect = WindowRectRelToAbs(window, r: window->NavRectRel[g.NavLayer]);
13473
13474 // Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?)
13475 if (window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX))
13476 {
13477 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
13478 ref_rect.Translate(d: window->Scroll - next_scroll);
13479 }
13480 ImVec2 pos = ImVec2(ref_rect.Min.x + ImMin(lhs: g.Style.FramePadding.x * 4, rhs: ref_rect.GetWidth()), ref_rect.Max.y - ImMin(lhs: g.Style.FramePadding.y, rhs: ref_rect.GetHeight()));
13481 ImGuiViewport* viewport = window->Viewport;
13482 return ImTrunc(v: ImClamp(v: pos, mn: viewport->Pos, mx: viewport->Pos + viewport->Size)); // ImTrunc() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.
13483 }
13484}
13485
13486float ImGui::GetNavTweakPressedAmount(ImGuiAxis axis)
13487{
13488 ImGuiContext& g = *GImGui;
13489 float repeat_delay, repeat_rate;
13490 GetTypematicRepeatRate(flags: ImGuiInputFlags_RepeatRateNavTweak, repeat_delay: &repeat_delay, repeat_rate: &repeat_rate);
13491
13492 ImGuiKey key_less, key_more;
13493 if (g.NavInputSource == ImGuiInputSource_Gamepad)
13494 {
13495 key_less = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadLeft : ImGuiKey_GamepadDpadUp;
13496 key_more = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadRight : ImGuiKey_GamepadDpadDown;
13497 }
13498 else
13499 {
13500 key_less = (axis == ImGuiAxis_X) ? ImGuiKey_LeftArrow : ImGuiKey_UpArrow;
13501 key_more = (axis == ImGuiAxis_X) ? ImGuiKey_RightArrow : ImGuiKey_DownArrow;
13502 }
13503 float amount = (float)GetKeyPressedAmount(key: key_more, repeat_delay, repeat_rate) - (float)GetKeyPressedAmount(key: key_less, repeat_delay, repeat_rate);
13504 if (amount != 0.0f && IsKeyDown(key: key_less) && IsKeyDown(key: key_more)) // Cancel when opposite directions are held, regardless of repeat phase
13505 amount = 0.0f;
13506 return amount;
13507}
13508
13509static void ImGui::NavUpdate()
13510{
13511 ImGuiContext& g = *GImGui;
13512 ImGuiIO& io = g.IO;
13513
13514 io.WantSetMousePos = false;
13515 //if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG_NAV("[nav] NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
13516
13517 // Set input source based on which keys are last pressed (as some features differs when used with Gamepad vs Keyboard)
13518 // FIXME-NAV: Now that keys are separated maybe we can get rid of NavInputSource?
13519 const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
13520 const ImGuiKey nav_gamepad_keys_to_change_source[] = { ImGuiKey_GamepadFaceRight, ImGuiKey_GamepadFaceLeft, ImGuiKey_GamepadFaceUp, ImGuiKey_GamepadFaceDown, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown };
13521 if (nav_gamepad_active)
13522 for (ImGuiKey key : nav_gamepad_keys_to_change_source)
13523 if (IsKeyDown(key))
13524 g.NavInputSource = ImGuiInputSource_Gamepad;
13525 const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13526 const ImGuiKey nav_keyboard_keys_to_change_source[] = { ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow };
13527 if (nav_keyboard_active)
13528 for (ImGuiKey key : nav_keyboard_keys_to_change_source)
13529 if (IsKeyDown(key))
13530 g.NavInputSource = ImGuiInputSource_Keyboard;
13531
13532 // Process navigation init request (select first/default focus)
13533 g.NavJustMovedToId = 0;
13534 g.NavJustMovedToFocusScopeId = g.NavJustMovedFromFocusScopeId = 0;
13535 if (g.NavInitResult.ID != 0)
13536 NavInitRequestApplyResult();
13537 g.NavInitRequest = false;
13538 g.NavInitRequestFromMove = false;
13539 g.NavInitResult.ID = 0;
13540
13541 // Process navigation move request
13542 if (g.NavMoveSubmitted)
13543 NavMoveRequestApplyResult();
13544 g.NavTabbingCounter = 0;
13545 g.NavMoveSubmitted = g.NavMoveScoringItems = false;
13546 if (g.NavCursorHideFrames > 0)
13547 if (--g.NavCursorHideFrames == 0)
13548 g.NavCursorVisible = true;
13549
13550 // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling)
13551 bool set_mouse_pos = false;
13552 if (g.NavMousePosDirty && g.NavIdIsAlive)
13553 if (g.NavCursorVisible && g.NavHighlightItemUnderNav && g.NavWindow)
13554 set_mouse_pos = true;
13555 g.NavMousePosDirty = false;
13556 IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu);
13557
13558 // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0
13559 if (g.NavWindow)
13560 NavSaveLastChildNavWindowIntoParent(nav_window: g.NavWindow);
13561 if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
13562 g.NavWindow->NavLastChildNavWindow = NULL;
13563
13564 // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
13565 NavUpdateWindowing();
13566
13567 // Set output flags for user application
13568 io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
13569 io.NavVisible = (io.NavActive && g.NavId != 0 && g.NavCursorVisible) || (g.NavWindowingTarget != NULL);
13570
13571 // Process NavCancel input (to close a popup, get back to parent, clear focus)
13572 NavUpdateCancelRequest();
13573
13574 // Process manual activation request
13575 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = 0;
13576 g.NavActivateFlags = ImGuiActivateFlags_None;
13577 if (g.NavId != 0 && g.NavCursorVisible && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
13578 {
13579 const bool activate_down = (nav_keyboard_active && IsKeyDown(key: ImGuiKey_Space, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_NoOwner));
13580 const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(key: ImGuiKey_Space, flags: 0, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, flags: 0, ImGuiKeyOwner_NoOwner)));
13581 const bool input_down = (nav_keyboard_active && (IsKeyDown(key: ImGuiKey_Enter, ImGuiKeyOwner_NoOwner) || IsKeyDown(key: ImGuiKey_KeypadEnter, ImGuiKeyOwner_NoOwner))) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadInput, ImGuiKeyOwner_NoOwner));
13582 const bool input_pressed = input_down && ((nav_keyboard_active && (IsKeyPressed(key: ImGuiKey_Enter, flags: 0, ImGuiKeyOwner_NoOwner) || IsKeyPressed(key: ImGuiKey_KeypadEnter, flags: 0, ImGuiKeyOwner_NoOwner))) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadInput, flags: 0, ImGuiKeyOwner_NoOwner)));
13583 if (g.ActiveId == 0 && activate_pressed)
13584 {
13585 g.NavActivateId = g.NavId;
13586 g.NavActivateFlags = ImGuiActivateFlags_PreferTweak;
13587 }
13588 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed)
13589 {
13590 g.NavActivateId = g.NavId;
13591 g.NavActivateFlags = ImGuiActivateFlags_PreferInput;
13592 }
13593 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_down || input_down))
13594 g.NavActivateDownId = g.NavId;
13595 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_pressed || input_pressed))
13596 {
13597 g.NavActivatePressedId = g.NavId;
13598 NavHighlightActivated(id: g.NavId);
13599 }
13600 }
13601 if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
13602 g.NavCursorVisible = false;
13603 else if (g.IO.ConfigNavCursorVisibleAlways && g.NavCursorHideFrames == 0)
13604 g.NavCursorVisible = true;
13605 if (g.NavActivateId != 0)
13606 IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
13607
13608 // Highlight
13609 if (g.NavHighlightActivatedTimer > 0.0f)
13610 g.NavHighlightActivatedTimer = ImMax(lhs: 0.0f, rhs: g.NavHighlightActivatedTimer - io.DeltaTime);
13611 if (g.NavHighlightActivatedTimer == 0.0f)
13612 g.NavHighlightActivatedId = 0;
13613
13614 // Process programmatic activation request
13615 // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others)
13616 if (g.NavNextActivateId != 0)
13617 {
13618 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId;
13619 g.NavActivateFlags = g.NavNextActivateFlags;
13620 }
13621 g.NavNextActivateId = 0;
13622
13623 // Process move requests
13624 NavUpdateCreateMoveRequest();
13625 if (g.NavMoveDir == ImGuiDir_None)
13626 NavUpdateCreateTabbingRequest();
13627 NavUpdateAnyRequestFlag();
13628 g.NavIdIsAlive = false;
13629
13630 // Scrolling
13631 if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
13632 {
13633 // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
13634 ImGuiWindow* window = g.NavWindow;
13635 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.
13636 const ImGuiDir move_dir = g.NavMoveDir;
13637 if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY && move_dir != ImGuiDir_None)
13638 {
13639 if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
13640 SetScrollX(window, scroll_x: ImTrunc(f: window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
13641 if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down)
13642 SetScrollY(window, scroll_y: ImTrunc(f: window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
13643 }
13644
13645 // *Normal* Manual scroll with LStick
13646 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
13647 if (nav_gamepad_active)
13648 {
13649 const ImVec2 scroll_dir = GetKeyMagnitude2d(key_left: ImGuiKey_GamepadLStickLeft, key_right: ImGuiKey_GamepadLStickRight, key_up: ImGuiKey_GamepadLStickUp, key_down: ImGuiKey_GamepadLStickDown);
13650 const float tweak_factor = IsKeyDown(ImGuiKey_NavGamepadTweakSlow) ? 1.0f / 10.0f : IsKeyDown(ImGuiKey_NavGamepadTweakFast) ? 10.0f : 1.0f;
13651 if (scroll_dir.x != 0.0f && window->ScrollbarX)
13652 SetScrollX(window, scroll_x: ImTrunc(f: window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor));
13653 if (scroll_dir.y != 0.0f)
13654 SetScrollY(window, scroll_y: ImTrunc(f: window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor));
13655 }
13656 }
13657
13658 // Always prioritize mouse highlight if navigation is disabled
13659 if (!nav_keyboard_active && !nav_gamepad_active)
13660 {
13661 g.NavCursorVisible = false;
13662 g.NavHighlightItemUnderNav = set_mouse_pos = false;
13663 }
13664
13665 // Update mouse position if requested
13666 // (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied)
13667 if (set_mouse_pos && io.ConfigNavMoveSetMousePos && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
13668 TeleportMousePos(pos: NavCalcPreferredRefPos());
13669
13670 // [DEBUG]
13671 g.NavScoringDebugCount = 0;
13672#if IMGUI_DEBUG_NAV_RECTS
13673 if (ImGuiWindow* debug_window = g.NavWindow)
13674 {
13675 ImDrawList* draw_list = GetForegroundDrawList(debug_window);
13676 int layer = g.NavLayer; /* for (int layer = 0; layer < 2; layer++)*/ { ImRect r = WindowRectRelToAbs(debug_window, debug_window->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 200, 0, 255)); }
13677 //if (1) { ImU32 col = (!debug_window->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); }
13678 }
13679#endif
13680}
13681
13682void ImGui::NavInitRequestApplyResult()
13683{
13684 // 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)
13685 ImGuiContext& g = *GImGui;
13686 if (!g.NavWindow)
13687 return;
13688
13689 ImGuiNavItemData* result = &g.NavInitResult;
13690 if (g.NavId != result->ID)
13691 {
13692 g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId;
13693 g.NavJustMovedToId = result->ID;
13694 g.NavJustMovedToFocusScopeId = result->FocusScopeId;
13695 g.NavJustMovedToKeyMods = 0;
13696 g.NavJustMovedToIsTabbing = false;
13697 g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
13698 }
13699
13700 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
13701 // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently.
13702 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: ApplyResult: NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
13703 SetNavID(id: result->ID, nav_layer: g.NavLayer, focus_scope_id: result->FocusScopeId, rect_rel: result->RectRel);
13704 g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result
13705 if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
13706 g.NavLastValidSelectionUserData = result->SelectionUserData;
13707 if (g.NavInitRequestFromMove)
13708 SetNavCursorVisibleAfterMove();
13709}
13710
13711// Bias scoring rect ahead of scoring + update preferred pos (if missing) using source position
13712static void NavBiasScoringRect(ImRect& r, ImVec2& preferred_pos_rel, ImGuiDir move_dir, ImGuiNavMoveFlags move_flags)
13713{
13714 // Bias initial rect
13715 ImGuiContext& g = *GImGui;
13716 const ImVec2 rel_to_abs_offset = g.NavWindow->DC.CursorStartPos;
13717
13718 // Initialize bias on departure if we don't have any. So mouse-click + arrow will record bias.
13719 // - We default to L/U bias, so moving down from a large source item into several columns will land on left-most column.
13720 // - But each successful move sets new bias on one axis, only cleared when using mouse.
13721 if ((move_flags & ImGuiNavMoveFlags_Forwarded) == 0)
13722 {
13723 if (preferred_pos_rel.x == FLT_MAX)
13724 preferred_pos_rel.x = ImMin(lhs: r.Min.x + 1.0f, rhs: r.Max.x) - rel_to_abs_offset.x;
13725 if (preferred_pos_rel.y == FLT_MAX)
13726 preferred_pos_rel.y = r.GetCenter().y - rel_to_abs_offset.y;
13727 }
13728
13729 // Apply general bias on the other axis
13730 if ((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) && preferred_pos_rel.x != FLT_MAX)
13731 r.Min.x = r.Max.x = preferred_pos_rel.x + rel_to_abs_offset.x;
13732 else if ((move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) && preferred_pos_rel.y != FLT_MAX)
13733 r.Min.y = r.Max.y = preferred_pos_rel.y + rel_to_abs_offset.y;
13734}
13735
13736void ImGui::NavUpdateCreateMoveRequest()
13737{
13738 ImGuiContext& g = *GImGui;
13739 ImGuiIO& io = g.IO;
13740 ImGuiWindow* window = g.NavWindow;
13741 const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
13742 const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13743
13744 if (g.NavMoveForwardToNextFrame && window != NULL)
13745 {
13746 // 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)
13747 // (preserve most state, which were already set by the NavMoveRequestForward() function)
13748 IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
13749 IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded);
13750 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
13751 }
13752 else
13753 {
13754 // Initiate directional inputs request
13755 g.NavMoveDir = ImGuiDir_None;
13756 g.NavMoveFlags = ImGuiNavMoveFlags_None;
13757 g.NavMoveScrollFlags = ImGuiScrollFlags_None;
13758 if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
13759 {
13760 const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateNavMove;
13761 if (!IsActiveIdUsingNavDir(dir: ImGuiDir_Left) && ((nav_gamepad_active && IsKeyPressed(key: ImGuiKey_GamepadDpadLeft, flags: repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(key: ImGuiKey_LeftArrow, flags: repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Left; }
13762 if (!IsActiveIdUsingNavDir(dir: ImGuiDir_Right) && ((nav_gamepad_active && IsKeyPressed(key: ImGuiKey_GamepadDpadRight, flags: repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(key: ImGuiKey_RightArrow, flags: repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Right; }
13763 if (!IsActiveIdUsingNavDir(dir: ImGuiDir_Up) && ((nav_gamepad_active && IsKeyPressed(key: ImGuiKey_GamepadDpadUp, flags: repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(key: ImGuiKey_UpArrow, flags: repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Up; }
13764 if (!IsActiveIdUsingNavDir(dir: ImGuiDir_Down) && ((nav_gamepad_active && IsKeyPressed(key: ImGuiKey_GamepadDpadDown, flags: repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(key: ImGuiKey_DownArrow, flags: repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Down; }
13765 }
13766 g.NavMoveClipDir = g.NavMoveDir;
13767 g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
13768 }
13769
13770 // Update PageUp/PageDown/Home/End scroll
13771 // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
13772 float scoring_rect_offset_y = 0.0f;
13773 if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active)
13774 scoring_rect_offset_y = NavUpdatePageUpPageDown();
13775 if (scoring_rect_offset_y != 0.0f)
13776 {
13777 g.NavScoringNoClipRect = window->InnerRect;
13778 g.NavScoringNoClipRect.TranslateY(dy: scoring_rect_offset_y);
13779 }
13780
13781 // [DEBUG] Always send a request when holding CTRL. Hold CTRL + Arrow change the direction.
13782#if IMGUI_DEBUG_NAV_SCORING
13783 //if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C))
13784 // g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3);
13785 if (io.KeyCtrl)
13786 {
13787 if (g.NavMoveDir == ImGuiDir_None)
13788 g.NavMoveDir = g.NavMoveDirForDebug;
13789 g.NavMoveClipDir = g.NavMoveDir;
13790 g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult;
13791 }
13792#endif
13793
13794 // Submit
13795 g.NavMoveForwardToNextFrame = false;
13796 if (g.NavMoveDir != ImGuiDir_None)
13797 NavMoveRequestSubmit(move_dir: g.NavMoveDir, clip_dir: g.NavMoveClipDir, move_flags: g.NavMoveFlags, scroll_flags: g.NavMoveScrollFlags);
13798
13799 // Moving with no reference triggers an init request (will be used as a fallback if the direction fails to find a match)
13800 if (g.NavMoveSubmitted && g.NavId == 0)
13801 {
13802 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", window ? window->Name : "<NULL>", g.NavLayer);
13803 g.NavInitRequest = g.NavInitRequestFromMove = true;
13804 g.NavInitResult.ID = 0;
13805 if (g.IO.ConfigNavCursorVisibleAuto)
13806 g.NavCursorVisible = true;
13807 }
13808
13809 // When using gamepad, we project the reference nav bounding box into window visible area.
13810 // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling,
13811 // since with gamepad all movements are relative (can't focus a visible object like we can with the mouse).
13812 if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded))
13813 {
13814 bool clamp_x = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapX)) == 0;
13815 bool clamp_y = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapY)) == 0;
13816 ImRect inner_rect_rel = WindowRectAbsToRel(window, r: ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)));
13817
13818 // Take account of changing scroll to handle triggering a new move request on a scrolling frame. (#6171)
13819 // Otherwise 'inner_rect_rel' would be off on the move result frame.
13820 inner_rect_rel.Translate(d: CalcNextScrollFromScrollTargetAndClamp(window) - window->Scroll);
13821
13822 if ((clamp_x || clamp_y) && !inner_rect_rel.Contains(r: window->NavRectRel[g.NavLayer]))
13823 {
13824 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel for gamepad move\n");
13825 float pad_x = ImMin(lhs: inner_rect_rel.GetWidth(), rhs: window->CalcFontSize() * 0.5f);
13826 float pad_y = ImMin(lhs: inner_rect_rel.GetHeight(), rhs: window->CalcFontSize() * 0.5f); // Terrible approximation for the intent of starting navigation from first fully visible item
13827 inner_rect_rel.Min.x = clamp_x ? (inner_rect_rel.Min.x + pad_x) : -FLT_MAX;
13828 inner_rect_rel.Max.x = clamp_x ? (inner_rect_rel.Max.x - pad_x) : +FLT_MAX;
13829 inner_rect_rel.Min.y = clamp_y ? (inner_rect_rel.Min.y + pad_y) : -FLT_MAX;
13830 inner_rect_rel.Max.y = clamp_y ? (inner_rect_rel.Max.y - pad_y) : +FLT_MAX;
13831 window->NavRectRel[g.NavLayer].ClipWithFull(r: inner_rect_rel);
13832 g.NavId = 0;
13833 }
13834 }
13835
13836 // 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)
13837 ImRect scoring_rect;
13838 if (window != NULL)
13839 {
13840 ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
13841 scoring_rect = WindowRectRelToAbs(window, r: nav_rect_rel);
13842 scoring_rect.TranslateY(dy: scoring_rect_offset_y);
13843 if (g.NavMoveSubmitted)
13844 NavBiasScoringRect(r&: scoring_rect, preferred_pos_rel&: window->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer], move_dir: g.NavMoveDir, move_flags: g.NavMoveFlags);
13845 IM_ASSERT(!scoring_rect.IsInverted()); // Ensure we have a non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem().
13846 //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG]
13847 //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG]
13848 }
13849 g.NavScoringRect = scoring_rect;
13850 g.NavScoringNoClipRect.Add(r: scoring_rect);
13851}
13852
13853void ImGui::NavUpdateCreateTabbingRequest()
13854{
13855 ImGuiContext& g = *GImGui;
13856 ImGuiWindow* window = g.NavWindow;
13857 IM_ASSERT(g.NavMoveDir == ImGuiDir_None);
13858 if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs))
13859 return;
13860
13861 const bool tab_pressed = IsKeyPressed(key: ImGuiKey_Tab, flags: ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner) && !g.IO.KeyCtrl && !g.IO.KeyAlt;
13862 if (!tab_pressed)
13863 return;
13864
13865 // Initiate tabbing request
13866 // (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!)
13867 // See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping.
13868 const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13869 if (nav_keyboard_active)
13870 g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavCursorVisible == false && g.ActiveId == 0) ? 0 : +1;
13871 else
13872 g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1;
13873 ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate;
13874 ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
13875 ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down;
13876 NavMoveRequestSubmit(move_dir: ImGuiDir_None, clip_dir, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
13877 g.NavTabbingCounter = -1;
13878}
13879
13880// Apply result from previous frame navigation directional move request. Always called from NavUpdate()
13881void ImGui::NavMoveRequestApplyResult()
13882{
13883 ImGuiContext& g = *GImGui;
13884#if IMGUI_DEBUG_NAV_SCORING
13885 if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times
13886 return;
13887#endif
13888
13889 // Select which result to use
13890 ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL;
13891
13892 // Tabbing forward wrap
13893 if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && result == NULL)
13894 if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID)
13895 result = &g.NavTabbingResultFirst;
13896
13897 // In a situation when there are no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)
13898 const ImGuiAxis axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
13899 if (result == NULL)
13900 {
13901 if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
13902 g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavCursorVisible;
13903 if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0)
13904 SetNavCursorVisibleAfterMove();
13905 NavClearPreferredPosForAxis(axis); // On a failed move, clear preferred pos for this axis.
13906 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveSubmitted but not led to a result!\n");
13907 return;
13908 }
13909
13910 // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
13911 if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
13912 if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId)
13913 result = &g.NavMoveResultLocalVisible;
13914
13915 // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
13916 if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
13917 if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
13918 result = &g.NavMoveResultOther;
13919 IM_ASSERT(g.NavWindow && result->Window);
13920
13921 // Scroll to keep newly navigated item fully into view.
13922 if (g.NavLayer == ImGuiNavLayer_Main)
13923 {
13924 ImRect rect_abs = WindowRectRelToAbs(window: result->Window, r: result->RectRel);
13925 ScrollToRectEx(window: result->Window, item_rect: rect_abs, flags: g.NavMoveScrollFlags);
13926
13927 if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY)
13928 {
13929 // FIXME: Should remove this? Or make more precise: use ScrollToRectEx() with edge?
13930 float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
13931 SetScrollY(window: result->Window, scroll_y: scroll_target);
13932 }
13933 }
13934
13935 if (g.NavWindow != result->Window)
13936 {
13937 IMGUI_DEBUG_LOG_FOCUS("[focus] NavMoveRequest: SetNavWindow(\"%s\")\n", result->Window->Name);
13938 g.NavWindow = result->Window;
13939 g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
13940 }
13941
13942 // Clear active id unless requested not to
13943 // FIXME: ImGuiNavMoveFlags_NoClearActiveId is currently unused as we don't have a clear strategy to preserve active id after interaction,
13944 // so this is mostly provided as a gateway for further experiments (see #1418, #2890)
13945 if (g.ActiveId != result->ID && (g.NavMoveFlags & ImGuiNavMoveFlags_NoClearActiveId) == 0)
13946 ClearActiveID();
13947
13948 // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
13949 // PageUp/PageDown however sets always set NavJustMovedTo (vs Home/End which doesn't) mimicking Windows behavior.
13950 if ((g.NavId != result->ID || (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)) && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSelect) == 0)
13951 {
13952 g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId;
13953 g.NavJustMovedToId = result->ID;
13954 g.NavJustMovedToFocusScopeId = result->FocusScopeId;
13955 g.NavJustMovedToKeyMods = g.NavMoveKeyMods;
13956 g.NavJustMovedToIsTabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;
13957 g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
13958 //IMGUI_DEBUG_LOG_NAV("[nav] NavJustMovedFromFocusScopeId = 0x%08X, NavJustMovedToFocusScopeId = 0x%08X\n", g.NavJustMovedFromFocusScopeId, g.NavJustMovedToFocusScopeId);
13959 }
13960
13961 // Apply new NavID/Focus
13962 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
13963 ImVec2 preferred_scoring_pos_rel = g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer];
13964 SetNavID(id: result->ID, nav_layer: g.NavLayer, focus_scope_id: result->FocusScopeId, rect_rel: result->RectRel);
13965 if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
13966 g.NavLastValidSelectionUserData = result->SelectionUserData;
13967
13968 // Restore last preferred position for current axis
13969 // (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..)
13970 if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) == 0)
13971 {
13972 preferred_scoring_pos_rel[axis] = result->RectRel.GetCenter()[axis];
13973 g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer] = preferred_scoring_pos_rel;
13974 }
13975
13976 // Tabbing: Activates Inputable, otherwise only Focus
13977 if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && (result->ItemFlags & ImGuiItemFlags_Inputable) == 0)
13978 g.NavMoveFlags &= ~ImGuiNavMoveFlags_Activate;
13979
13980 // Activate
13981 if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate)
13982 {
13983 g.NavNextActivateId = result->ID;
13984 g.NavNextActivateFlags = ImGuiActivateFlags_None;
13985 if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
13986 g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState | ImGuiActivateFlags_FromTabbing;
13987 }
13988
13989 // Make nav cursor visible
13990 if ((g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0)
13991 SetNavCursorVisibleAfterMove();
13992}
13993
13994// Process Escape/NavCancel input (to close a popup, get back to parent, clear focus)
13995// FIXME: In order to support e.g. Escape to clear a selection we'll need:
13996// - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it.
13997// - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept
13998static void ImGui::NavUpdateCancelRequest()
13999{
14000 ImGuiContext& g = *GImGui;
14001 const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
14002 const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
14003 if (!(nav_keyboard_active && IsKeyPressed(key: ImGuiKey_Escape, flags: 0, ImGuiKeyOwner_NoOwner)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, flags: 0, ImGuiKeyOwner_NoOwner)))
14004 return;
14005
14006 IMGUI_DEBUG_LOG_NAV("[nav] NavUpdateCancelRequest()\n");
14007 if (g.ActiveId != 0)
14008 {
14009 ClearActiveID();
14010 }
14011 else if (g.NavLayer != ImGuiNavLayer_Main)
14012 {
14013 // Leave the "menu" layer
14014 NavRestoreLayer(layer: ImGuiNavLayer_Main);
14015 SetNavCursorVisibleAfterMove();
14016 }
14017 else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow)
14018 {
14019 // Exit child window
14020 ImGuiWindow* child_window = g.NavWindow->RootWindowForNav;
14021 ImGuiWindow* parent_window = child_window->ParentWindow;
14022 IM_ASSERT(child_window->ChildId != 0);
14023 FocusWindow(window: parent_window);
14024 SetNavID(id: child_window->ChildId, nav_layer: ImGuiNavLayer_Main, focus_scope_id: 0, rect_rel: WindowRectAbsToRel(window: parent_window, r: child_window->Rect()));
14025 SetNavCursorVisibleAfterMove();
14026 }
14027 else if (g.OpenPopupStack.Size > 0 && g.OpenPopupStack.back().Window != NULL && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
14028 {
14029 // Close open popup/menu
14030 ClosePopupToLevel(remaining: g.OpenPopupStack.Size - 1, restore_focus_to_window_under_popup: true);
14031 }
14032 else
14033 {
14034 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
14035 // FIXME-NAV: This should happen on window appearing.
14036 if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow)
14037 if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup)))// || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
14038 g.NavWindow->NavLastIds[0] = 0;
14039
14040 // Clear nav focus
14041 if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow)
14042 g.NavId = 0;
14043 if (g.IO.ConfigNavEscapeClearFocusWindow)
14044 FocusWindow(NULL);
14045 }
14046}
14047
14048// Handle PageUp/PageDown/Home/End keys
14049// Called from NavUpdateCreateMoveRequest() which will use our output to create a move request
14050// FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference
14051// FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid?
14052static float ImGui::NavUpdatePageUpPageDown()
14053{
14054 ImGuiContext& g = *GImGui;
14055 ImGuiWindow* window = g.NavWindow;
14056 if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL)
14057 return 0.0f;
14058
14059 const bool page_up_held = IsKeyDown(key: ImGuiKey_PageUp, ImGuiKeyOwner_NoOwner);
14060 const bool page_down_held = IsKeyDown(key: ImGuiKey_PageDown, ImGuiKeyOwner_NoOwner);
14061 const bool home_pressed = IsKeyPressed(key: ImGuiKey_Home, flags: ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
14062 const bool end_pressed = IsKeyPressed(key: ImGuiKey_End, flags: ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
14063 if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out
14064 return 0.0f;
14065
14066 if (g.NavLayer != ImGuiNavLayer_Main)
14067 NavRestoreLayer(layer: ImGuiNavLayer_Main);
14068
14069 if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY)
14070 {
14071 // Fallback manual-scroll when window has no navigable item
14072 if (IsKeyPressed(key: ImGuiKey_PageUp, flags: ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
14073 SetScrollY(window, scroll_y: window->Scroll.y - window->InnerRect.GetHeight());
14074 else if (IsKeyPressed(key: ImGuiKey_PageDown, flags: ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
14075 SetScrollY(window, scroll_y: window->Scroll.y + window->InnerRect.GetHeight());
14076 else if (home_pressed)
14077 SetScrollY(window, scroll_y: 0.0f);
14078 else if (end_pressed)
14079 SetScrollY(window, scroll_y: window->ScrollMax.y);
14080 }
14081 else
14082 {
14083 ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
14084 const float page_offset_y = ImMax(lhs: 0.0f, rhs: window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
14085 float nav_scoring_rect_offset_y = 0.0f;
14086 if (IsKeyPressed(key: ImGuiKey_PageUp, repeat: true))
14087 {
14088 nav_scoring_rect_offset_y = -page_offset_y;
14089 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)
14090 g.NavMoveClipDir = ImGuiDir_Up;
14091 g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;
14092 }
14093 else if (IsKeyPressed(key: ImGuiKey_PageDown, repeat: true))
14094 {
14095 nav_scoring_rect_offset_y = +page_offset_y;
14096 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)
14097 g.NavMoveClipDir = ImGuiDir_Down;
14098 g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;
14099 }
14100 else if (home_pressed)
14101 {
14102 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
14103 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result.
14104 // Preserve current horizontal position if we have any.
14105 nav_rect_rel.Min.y = nav_rect_rel.Max.y = 0.0f;
14106 if (nav_rect_rel.IsInverted())
14107 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
14108 g.NavMoveDir = ImGuiDir_Down;
14109 g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
14110 // FIXME-NAV: MoveClipDir left to _None, intentional?
14111 }
14112 else if (end_pressed)
14113 {
14114 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ContentSize.y;
14115 if (nav_rect_rel.IsInverted())
14116 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
14117 g.NavMoveDir = ImGuiDir_Up;
14118 g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
14119 // FIXME-NAV: MoveClipDir left to _None, intentional?
14120 }
14121 return nav_scoring_rect_offset_y;
14122 }
14123 return 0.0f;
14124}
14125
14126static void ImGui::NavEndFrame()
14127{
14128 ImGuiContext& g = *GImGui;
14129
14130 // Show CTRL+TAB list window
14131 if (g.NavWindowingTarget != NULL)
14132 NavUpdateWindowingOverlay();
14133
14134 // Perform wrap-around in menus
14135 // FIXME-NAV: Wrap may need to apply a weight bias on the other axis. e.g. 4x4 grid with 2 last items missing on last item won't handle LoopY/WrapY correctly.
14136 // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame.
14137 if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & ImGuiNavMoveFlags_WrapMask_) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)
14138 NavUpdateCreateWrappingRequest();
14139}
14140
14141static void ImGui::NavUpdateCreateWrappingRequest()
14142{
14143 ImGuiContext& g = *GImGui;
14144 ImGuiWindow* window = g.NavWindow;
14145
14146 bool do_forward = false;
14147 ImRect bb_rel = window->NavRectRel[g.NavLayer];
14148 ImGuiDir clip_dir = g.NavMoveDir;
14149
14150 const ImGuiNavMoveFlags move_flags = g.NavMoveFlags;
14151 //const ImGuiAxis move_axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
14152 if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
14153 {
14154 bb_rel.Min.x = bb_rel.Max.x = window->ContentSize.x + window->WindowPadding.x;
14155 if (move_flags & ImGuiNavMoveFlags_WrapX)
14156 {
14157 bb_rel.TranslateY(dy: -bb_rel.GetHeight()); // Previous row
14158 clip_dir = ImGuiDir_Up;
14159 }
14160 do_forward = true;
14161 }
14162 if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
14163 {
14164 bb_rel.Min.x = bb_rel.Max.x = -window->WindowPadding.x;
14165 if (move_flags & ImGuiNavMoveFlags_WrapX)
14166 {
14167 bb_rel.TranslateY(dy: +bb_rel.GetHeight()); // Next row
14168 clip_dir = ImGuiDir_Down;
14169 }
14170 do_forward = true;
14171 }
14172 if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
14173 {
14174 bb_rel.Min.y = bb_rel.Max.y = window->ContentSize.y + window->WindowPadding.y;
14175 if (move_flags & ImGuiNavMoveFlags_WrapY)
14176 {
14177 bb_rel.TranslateX(dx: -bb_rel.GetWidth()); // Previous column
14178 clip_dir = ImGuiDir_Left;
14179 }
14180 do_forward = true;
14181 }
14182 if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
14183 {
14184 bb_rel.Min.y = bb_rel.Max.y = -window->WindowPadding.y;
14185 if (move_flags & ImGuiNavMoveFlags_WrapY)
14186 {
14187 bb_rel.TranslateX(dx: +bb_rel.GetWidth()); // Next column
14188 clip_dir = ImGuiDir_Right;
14189 }
14190 do_forward = true;
14191 }
14192 if (!do_forward)
14193 return;
14194 window->NavRectRel[g.NavLayer] = bb_rel;
14195 NavClearPreferredPosForAxis(axis: ImGuiAxis_X);
14196 NavClearPreferredPosForAxis(axis: ImGuiAxis_Y);
14197 NavMoveRequestForward(move_dir: g.NavMoveDir, clip_dir, move_flags, scroll_flags: g.NavMoveScrollFlags);
14198}
14199
14200static int ImGui::FindWindowFocusIndex(ImGuiWindow* window)
14201{
14202 ImGuiContext& g = *GImGui;
14203 IM_UNUSED(g);
14204 int order = window->FocusOrder;
14205 IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking)
14206 IM_ASSERT(g.WindowsFocusOrder[order] == window);
14207 return order;
14208}
14209
14210static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
14211{
14212 ImGuiContext& g = *GImGui;
14213 for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
14214 if (ImGui::IsWindowNavFocusable(window: g.WindowsFocusOrder[i]))
14215 return g.WindowsFocusOrder[i];
14216 return NULL;
14217}
14218
14219static void NavUpdateWindowingTarget(int focus_change_dir)
14220{
14221 ImGuiContext& g = *GImGui;
14222 IM_ASSERT(g.NavWindowingTarget);
14223 if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
14224 return;
14225
14226 const int i_current = ImGui::FindWindowFocusIndex(window: g.NavWindowingTarget);
14227 ImGuiWindow* window_target = FindWindowNavFocusable(i_start: i_current + focus_change_dir, i_stop: -INT_MAX, dir: focus_change_dir);
14228 if (!window_target)
14229 window_target = FindWindowNavFocusable(i_start: (focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_stop: i_current, dir: focus_change_dir);
14230 if (window_target) // Don't reset windowing target if there's a single window in the list
14231 {
14232 g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
14233 g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
14234 }
14235 g.NavWindowingToggleLayer = false;
14236}
14237
14238// Windowing management mode
14239// Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
14240// Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
14241static void ImGui::NavUpdateWindowing()
14242{
14243 ImGuiContext& g = *GImGui;
14244 ImGuiIO& io = g.IO;
14245
14246 ImGuiWindow* apply_focus_window = NULL;
14247 bool apply_toggle_layer = false;
14248
14249 ImGuiWindow* modal_window = GetTopMostPopupModal();
14250 bool allow_windowing = (modal_window == NULL); // FIXME: This prevent CTRL+TAB from being usable with windows that are inside the Begin-stack of that modal.
14251 if (!allow_windowing)
14252 g.NavWindowingTarget = NULL;
14253
14254 // Fade out
14255 if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
14256 {
14257 g.NavWindowingHighlightAlpha = ImMax(lhs: g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, rhs: 0.0f);
14258 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
14259 g.NavWindowingTargetAnim = NULL;
14260 }
14261
14262 // Start CTRL+Tab or Square+L/R window selection
14263 // (g.ConfigNavWindowingKeyNext/g.ConfigNavWindowingKeyPrev defaults are ImGuiMod_Ctrl|ImGuiKey_Tab and ImGuiMod_Ctrl|ImGuiMod_Shift|ImGuiKey_Tab)
14264 const ImGuiID owner_id = ImHashStr(data_p: "###NavUpdateWindowing");
14265 const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
14266 const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
14267 const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(key_chord: g.ConfigNavWindowingKeyNext, flags: ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
14268 const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(key_chord: g.ConfigNavWindowingKeyPrev, flags: ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
14269 const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, flags: ImGuiInputFlags_None);
14270 const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard!
14271 bool just_started_windowing_from_null_focus = false;
14272 if (start_windowing_with_gamepad || start_windowing_with_keyboard)
14273 if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(i_start: g.WindowsFocusOrder.Size - 1, i_stop: -INT_MAX, dir: -1))
14274 {
14275 g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // Current location
14276 g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
14277 g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
14278 g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer
14279 g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;
14280 if (g.NavWindow == NULL)
14281 just_started_windowing_from_null_focus = true;
14282
14283 // Manually register ownership of our mods. Using a global route in the Shortcut() calls instead would probably be correct but may have more side-effects.
14284 if (keyboard_next_window || keyboard_prev_window)
14285 SetKeyOwnersForKeyChord(key_chord: (g.ConfigNavWindowingKeyNext | g.ConfigNavWindowingKeyPrev) & ImGuiMod_Mask_, owner_id);
14286 }
14287
14288 // Gamepad update
14289 g.NavWindowingTimer += io.DeltaTime;
14290 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Gamepad)
14291 {
14292 // 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
14293 g.NavWindowingHighlightAlpha = ImMax(lhs: g.NavWindowingHighlightAlpha, rhs: ImSaturate(f: (g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
14294
14295 // Select window to focus
14296 const int focus_change_dir = (int)IsKeyPressed(key: ImGuiKey_GamepadL1) - (int)IsKeyPressed(key: ImGuiKey_GamepadR1);
14297 if (focus_change_dir != 0 && !just_started_windowing_from_null_focus)
14298 {
14299 NavUpdateWindowingTarget(focus_change_dir);
14300 g.NavWindowingHighlightAlpha = 1.0f;
14301 }
14302
14303 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
14304 if (!IsKeyDown(ImGuiKey_NavGamepadMenu))
14305 {
14306 g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
14307 if (g.NavWindowingToggleLayer && g.NavWindow)
14308 apply_toggle_layer = true;
14309 else if (!g.NavWindowingToggleLayer)
14310 apply_focus_window = g.NavWindowingTarget;
14311 g.NavWindowingTarget = NULL;
14312 }
14313 }
14314
14315 // Keyboard: Focus
14316 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Keyboard)
14317 {
14318 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
14319 ImGuiKeyChord shared_mods = ((g.ConfigNavWindowingKeyNext ? g.ConfigNavWindowingKeyNext : ImGuiMod_Mask_) & (g.ConfigNavWindowingKeyPrev ? g.ConfigNavWindowingKeyPrev : ImGuiMod_Mask_)) & ImGuiMod_Mask_;
14320 IM_ASSERT(shared_mods != 0); // Next/Prev shortcut currently needs a shared modifier to "hold", otherwise Prev actions would keep cycling between two windows.
14321 g.NavWindowingHighlightAlpha = ImMax(lhs: g.NavWindowingHighlightAlpha, rhs: ImSaturate(f: (g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
14322 if ((keyboard_next_window || keyboard_prev_window) && !just_started_windowing_from_null_focus)
14323 NavUpdateWindowingTarget(focus_change_dir: keyboard_next_window ? -1 : +1);
14324 else if ((io.KeyMods & shared_mods) != shared_mods)
14325 apply_focus_window = g.NavWindowingTarget;
14326 }
14327
14328 // Keyboard: Press and Release ALT to toggle menu layer
14329 const ImGuiKey windowing_toggle_keys[] = { ImGuiKey_LeftAlt, ImGuiKey_RightAlt };
14330 bool windowing_toggle_layer_start = false;
14331 for (ImGuiKey windowing_toggle_key : windowing_toggle_keys)
14332 if (nav_keyboard_active && IsKeyPressed(key: windowing_toggle_key, flags: 0, ImGuiKeyOwner_NoOwner))
14333 {
14334 windowing_toggle_layer_start = true;
14335 g.NavWindowingToggleLayer = true;
14336 g.NavWindowingToggleKey = windowing_toggle_key;
14337 g.NavInputSource = ImGuiInputSource_Keyboard;
14338 break;
14339 }
14340 if (g.NavWindowingToggleLayer && g.NavInputSource == ImGuiInputSource_Keyboard)
14341 {
14342 // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)
14343 // We cancel toggling nav layer when other modifiers are pressed. (See #4439)
14344 // - AltGR is Alt+Ctrl on some layout but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl).
14345 // We cancel toggling nav layer if an owner has claimed the key.
14346 if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper)
14347 g.NavWindowingToggleLayer = false;
14348 else if (windowing_toggle_layer_start == false && g.LastKeyboardKeyPressTime == g.Time)
14349 g.NavWindowingToggleLayer = false;
14350 else if (TestKeyOwner(key: g.NavWindowingToggleKey, ImGuiKeyOwner_NoOwner) == false || TestKeyOwner(key: ImGuiMod_Alt, ImGuiKeyOwner_NoOwner) == false)
14351 g.NavWindowingToggleLayer = false;
14352
14353 // Apply layer toggle on Alt release
14354 // Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss.
14355 if (IsKeyReleased(key: g.NavWindowingToggleKey) && g.NavWindowingToggleLayer)
14356 if (g.ActiveId == 0 || g.ActiveIdAllowOverlap)
14357 if (IsMousePosValid(mouse_pos: &io.MousePos) == IsMousePosValid(mouse_pos: &io.MousePosPrev))
14358 apply_toggle_layer = true;
14359 if (!IsKeyDown(key: g.NavWindowingToggleKey))
14360 g.NavWindowingToggleLayer = false;
14361 }
14362
14363 // Move window
14364 if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
14365 {
14366 ImVec2 nav_move_dir;
14367 if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift)
14368 nav_move_dir = GetKeyMagnitude2d(key_left: ImGuiKey_LeftArrow, key_right: ImGuiKey_RightArrow, key_up: ImGuiKey_UpArrow, key_down: ImGuiKey_DownArrow);
14369 if (g.NavInputSource == ImGuiInputSource_Gamepad)
14370 nav_move_dir = GetKeyMagnitude2d(key_left: ImGuiKey_GamepadLStickLeft, key_right: ImGuiKey_GamepadLStickRight, key_up: ImGuiKey_GamepadLStickUp, key_down: ImGuiKey_GamepadLStickDown);
14371 if (nav_move_dir.x != 0.0f || nav_move_dir.y != 0.0f)
14372 {
14373 const float NAV_MOVE_SPEED = 800.0f;
14374 const float move_step = NAV_MOVE_SPEED * io.DeltaTime * ImMin(lhs: io.DisplayFramebufferScale.x, rhs: io.DisplayFramebufferScale.y);
14375 g.NavWindowingAccumDeltaPos += nav_move_dir * move_step;
14376 g.NavHighlightItemUnderNav = true;
14377 ImVec2 accum_floored = ImTrunc(v: g.NavWindowingAccumDeltaPos);
14378 if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
14379 {
14380 ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindowDockTree;
14381 SetWindowPos(window: moving_window, pos: moving_window->Pos + accum_floored, cond: ImGuiCond_Always);
14382 g.NavWindowingAccumDeltaPos -= accum_floored;
14383 }
14384 }
14385 }
14386
14387 // Apply final focus
14388 if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
14389 {
14390 // FIXME: Many actions here could be part of a higher-level/reused function. Why aren't they in FocusWindow()
14391 // Investigate for each of them: ClearActiveID(), NavRestoreHighlightAfterMove(), NavRestoreLastChildNavWindow(), ClosePopupsOverWindow(), NavInitWindow()
14392 ImGuiViewport* previous_viewport = g.NavWindow ? g.NavWindow->Viewport : NULL;
14393 ClearActiveID();
14394 SetNavCursorVisibleAfterMove();
14395 ClosePopupsOverWindow(ref_window: apply_focus_window, restore_focus_to_window_under_popup: false);
14396 FocusWindow(window: apply_focus_window, flags: ImGuiFocusRequestFlags_RestoreFocusedChild);
14397 apply_focus_window = g.NavWindow;
14398 if (apply_focus_window->NavLastIds[0] == 0)
14399 NavInitWindow(window: apply_focus_window, force_reinit: false);
14400
14401 // If the window has ONLY a menu layer (no main layer), select it directly
14402 // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame,
14403 // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since
14404 // the target window as already been previewed once.
14405 // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases,
14406 // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask*
14407 // won't be valid.
14408 if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu))
14409 g.NavLayer = ImGuiNavLayer_Menu;
14410
14411 // Request OS level focus
14412 if (apply_focus_window->Viewport != previous_viewport && g.PlatformIO.Platform_SetWindowFocus)
14413 g.PlatformIO.Platform_SetWindowFocus(apply_focus_window->Viewport);
14414 }
14415 if (apply_focus_window)
14416 g.NavWindowingTarget = NULL;
14417
14418 // Apply menu/layer toggle
14419 if (apply_toggle_layer && g.NavWindow)
14420 {
14421 ClearActiveID();
14422
14423 // Move to parent menu if necessary
14424 ImGuiWindow* new_nav_window = g.NavWindow;
14425 while (new_nav_window->ParentWindow
14426 && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
14427 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
14428 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
14429 new_nav_window = new_nav_window->ParentWindow;
14430 if (new_nav_window != g.NavWindow)
14431 {
14432 ImGuiWindow* old_nav_window = g.NavWindow;
14433 FocusWindow(window: new_nav_window);
14434 new_nav_window->NavLastChildNavWindow = old_nav_window;
14435 }
14436
14437 // Toggle layer
14438 const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
14439 if (new_nav_layer != g.NavLayer)
14440 {
14441 // Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?)
14442 const bool preserve_layer_1_nav_id = (new_nav_window->DockNodeAsHost != NULL);
14443 if (new_nav_layer == ImGuiNavLayer_Menu && !preserve_layer_1_nav_id)
14444 g.NavWindow->NavLastIds[new_nav_layer] = 0;
14445 NavRestoreLayer(layer: new_nav_layer);
14446 SetNavCursorVisibleAfterMove();
14447 }
14448 }
14449}
14450
14451// Window has already passed the IsWindowNavFocusable()
14452static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
14453{
14454 if (window->Flags & ImGuiWindowFlags_Popup)
14455 return ImGui::LocalizeGetMsg(key: ImGuiLocKey_WindowingPopup);
14456 if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(s1: window->Name, s2: "##MainMenuBar") == 0)
14457 return ImGui::LocalizeGetMsg(key: ImGuiLocKey_WindowingMainMenuBar);
14458 if (window->DockNodeAsHost)
14459 return "(Dock node)"; // Not normally shown to user.
14460 return ImGui::LocalizeGetMsg(key: ImGuiLocKey_WindowingUntitled);
14461}
14462
14463// Overlay displayed when using CTRL+TAB. Called by EndFrame().
14464void ImGui::NavUpdateWindowingOverlay()
14465{
14466 ImGuiContext& g = *GImGui;
14467 IM_ASSERT(g.NavWindowingTarget != NULL);
14468
14469 if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
14470 return;
14471
14472 if (g.NavWindowingListWindow == NULL)
14473 g.NavWindowingListWindow = FindWindowByName(name: "###NavWindowingList");
14474 const ImGuiViewport* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ GetMainViewport();
14475 SetNextWindowSizeConstraints(size_min: ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), size_max: ImVec2(FLT_MAX, FLT_MAX));
14476 SetNextWindowPos(pos: viewport->GetCenter(), cond: ImGuiCond_Always, pivot: ImVec2(0.5f, 0.5f));
14477 PushStyleVar(idx: ImGuiStyleVar_WindowPadding, val: g.Style.WindowPadding * 2.0f);
14478 Begin(name: "###NavWindowingList", NULL, flags: ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
14479 if (g.ContextName[0] != 0)
14480 SeparatorText(label: g.ContextName);
14481 for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
14482 {
14483 ImGuiWindow* window = g.WindowsFocusOrder[n];
14484 IM_ASSERT(window != NULL); // Fix static analyzers
14485 if (!IsWindowNavFocusable(window))
14486 continue;
14487 const char* label = window->Name;
14488 if (label == FindRenderedTextEnd(text: label))
14489 label = GetFallbackWindowNameForWindowingList(window);
14490 Selectable(label, selected: g.NavWindowingTarget == window);
14491 }
14492 End();
14493 PopStyleVar();
14494}
14495
14496
14497//-----------------------------------------------------------------------------
14498// [SECTION] DRAG AND DROP
14499//-----------------------------------------------------------------------------
14500
14501bool ImGui::IsDragDropActive()
14502{
14503 ImGuiContext& g = *GImGui;
14504 return g.DragDropActive;
14505}
14506
14507void ImGui::ClearDragDrop()
14508{
14509 ImGuiContext& g = *GImGui;
14510 if (g.DragDropActive)
14511 IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] ClearDragDrop()\n");
14512 g.DragDropActive = false;
14513 g.DragDropPayload.Clear();
14514 g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
14515 g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
14516 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
14517 g.DragDropAcceptFrameCount = -1;
14518
14519 g.DragDropPayloadBufHeap.clear();
14520 memset(s: &g.DragDropPayloadBufLocal, c: 0, n: sizeof(g.DragDropPayloadBufLocal));
14521}
14522
14523bool ImGui::BeginTooltipHidden()
14524{
14525 ImGuiContext& g = *GImGui;
14526 bool ret = Begin(name: "##Tooltip_Hidden", NULL, flags: ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
14527 SetWindowHiddenAndSkipItemsForCurrentFrame(g.CurrentWindow);
14528 return ret;
14529}
14530
14531// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
14532// If the item has an identifier:
14533// - This assume/require the item to be activated (typically via ButtonBehavior).
14534// - Therefore if you want to use this with a mouse button other than left mouse button, it is up to the item itself to activate with another button.
14535// - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag.
14536// If the item has no identifier:
14537// - Currently always assume left mouse button.
14538bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
14539{
14540 ImGuiContext& g = *GImGui;
14541 ImGuiWindow* window = g.CurrentWindow;
14542
14543 // FIXME-DRAGDROP: While in the common-most "drag from non-zero active id" case we can tell the mouse button,
14544 // in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic).
14545 ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
14546
14547 bool source_drag_active = false;
14548 ImGuiID source_id = 0;
14549 ImGuiID source_parent_id = 0;
14550 if ((flags & ImGuiDragDropFlags_SourceExtern) == 0)
14551 {
14552 source_id = g.LastItemData.ID;
14553 if (source_id != 0)
14554 {
14555 // Common path: items with ID
14556 if (g.ActiveId != source_id)
14557 return false;
14558 if (g.ActiveIdMouseButton != -1)
14559 mouse_button = g.ActiveIdMouseButton;
14560 if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
14561 return false;
14562 g.ActiveIdAllowOverlap = false;
14563 }
14564 else
14565 {
14566 // Uncommon path: items without ID
14567 if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
14568 return false;
14569 if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
14570 return false;
14571
14572 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
14573 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag.
14574 if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
14575 {
14576 IM_ASSERT(0);
14577 return false;
14578 }
14579
14580 // Magic fallback to handle items with no assigned ID, e.g. Text(), Image()
14581 // We build a throwaway ID based on current ID stack + relative AABB of items in window.
14582 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING/RESIZINGG OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
14583 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
14584 // Rely on keeping other window->LastItemXXX fields intact.
14585 source_id = g.LastItemData.ID = window->GetIDFromRectangle(r_abs: g.LastItemData.Rect);
14586 KeepAliveID(id: source_id);
14587 bool is_hovered = ItemHoverable(bb: g.LastItemData.Rect, id: source_id, item_flags: g.LastItemData.ItemFlags);
14588 if (is_hovered && g.IO.MouseClicked[mouse_button])
14589 {
14590 SetActiveID(id: source_id, window);
14591 FocusWindow(window);
14592 }
14593 if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
14594 g.ActiveIdAllowOverlap = is_hovered;
14595 }
14596 if (g.ActiveId != source_id)
14597 return false;
14598 source_parent_id = window->IDStack.back();
14599 source_drag_active = IsMouseDragging(button: mouse_button);
14600
14601 // Disable navigation and key inputs while dragging + cancel existing request if any
14602 SetActiveIdUsingAllKeyboardKeys();
14603 }
14604 else
14605 {
14606 // When ImGuiDragDropFlags_SourceExtern is set:
14607 window = NULL;
14608 source_id = ImHashStr(data_p: "#SourceExtern");
14609 source_drag_active = true;
14610 mouse_button = g.IO.MouseDown[0] ? 0 : -1;
14611 KeepAliveID(id: source_id);
14612 SetActiveID(id: source_id, NULL);
14613 }
14614
14615 IM_ASSERT(g.DragDropWithinTarget == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14616 if (!source_drag_active)
14617 return false;
14618
14619 // Activate drag and drop
14620 if (!g.DragDropActive)
14621 {
14622 IM_ASSERT(source_id != 0);
14623 ClearDragDrop();
14624 IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] BeginDragDropSource() DragDropActive = true, source_id = 0x%08X%s\n",
14625 source_id, (flags & ImGuiDragDropFlags_SourceExtern) ? " (EXTERN)" : "");
14626 ImGuiPayload& payload = g.DragDropPayload;
14627 payload.SourceId = source_id;
14628 payload.SourceParentId = source_parent_id;
14629 g.DragDropActive = true;
14630 g.DragDropSourceFlags = flags;
14631 g.DragDropMouseButton = mouse_button;
14632 if (payload.SourceId == g.ActiveId)
14633 g.ActiveIdNoClearOnFocusLoss = true;
14634 }
14635 g.DragDropSourceFrameCount = g.FrameCount;
14636 g.DragDropWithinSource = true;
14637
14638 if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
14639 {
14640 // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
14641 // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
14642 bool ret;
14643 if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
14644 ret = BeginTooltipHidden();
14645 else
14646 ret = BeginTooltip();
14647 IM_ASSERT(ret); // FIXME-NEWBEGIN: If this ever becomes false, we need to Begin("##Hidden", NULL, ImGuiWindowFlags_NoSavedSettings) + SetWindowHiddendAndSkipItemsForCurrentFrame().
14648 IM_UNUSED(ret);
14649 }
14650
14651 if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
14652 g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
14653
14654 return true;
14655}
14656
14657void ImGui::EndDragDropSource()
14658{
14659 ImGuiContext& g = *GImGui;
14660 IM_ASSERT(g.DragDropActive);
14661 IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?");
14662
14663 if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
14664 EndTooltip();
14665
14666 // Discard the drag if have not called SetDragDropPayload()
14667 if (g.DragDropPayload.DataFrameCount == -1)
14668 ClearDragDrop();
14669 g.DragDropWithinSource = false;
14670}
14671
14672// Use 'cond' to choose to submit payload on drag start or every frame
14673bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
14674{
14675 ImGuiContext& g = *GImGui;
14676 ImGuiPayload& payload = g.DragDropPayload;
14677 if (cond == 0)
14678 cond = ImGuiCond_Always;
14679
14680 IM_ASSERT(type != NULL);
14681 IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
14682 IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
14683 IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
14684 IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
14685
14686 if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
14687 {
14688 // Copy payload
14689 ImStrncpy(dst: payload.DataType, src: type, IM_ARRAYSIZE(payload.DataType));
14690 g.DragDropPayloadBufHeap.resize(new_size: 0);
14691 if (data_size > sizeof(g.DragDropPayloadBufLocal))
14692 {
14693 // Store in heap
14694 g.DragDropPayloadBufHeap.resize(new_size: (int)data_size);
14695 payload.Data = g.DragDropPayloadBufHeap.Data;
14696 memcpy(dest: payload.Data, src: data, n: data_size);
14697 }
14698 else if (data_size > 0)
14699 {
14700 // Store locally
14701 memset(s: &g.DragDropPayloadBufLocal, c: 0, n: sizeof(g.DragDropPayloadBufLocal));
14702 payload.Data = g.DragDropPayloadBufLocal;
14703 memcpy(dest: payload.Data, src: data, n: data_size);
14704 }
14705 else
14706 {
14707 payload.Data = NULL;
14708 }
14709 payload.DataSize = (int)data_size;
14710 }
14711 payload.DataFrameCount = g.FrameCount;
14712
14713 // Return whether the payload has been accepted
14714 return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
14715}
14716
14717bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
14718{
14719 ImGuiContext& g = *GImGui;
14720 if (!g.DragDropActive)
14721 return false;
14722
14723 ImGuiWindow* window = g.CurrentWindow;
14724 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
14725 if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree)
14726 return false;
14727 IM_ASSERT(id != 0);
14728 if (!IsMouseHoveringRect(r_min: bb.Min, r_max: bb.Max) || (id == g.DragDropPayload.SourceId))
14729 return false;
14730 if (window->SkipItems)
14731 return false;
14732
14733 IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14734 g.DragDropTargetRect = bb;
14735 g.DragDropTargetClipRect = window->ClipRect; // May want to be overridden by user depending on use case?
14736 g.DragDropTargetId = id;
14737 g.DragDropWithinTarget = true;
14738 return true;
14739}
14740
14741// We don't use BeginDragDropTargetCustom() and duplicate its code because:
14742// 1) we use LastItemData's ImGuiItemStatusFlags_HoveredRect which handles items that push a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
14743// 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
14744// Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
14745bool ImGui::BeginDragDropTarget()
14746{
14747 ImGuiContext& g = *GImGui;
14748 if (!g.DragDropActive)
14749 return false;
14750
14751 ImGuiWindow* window = g.CurrentWindow;
14752 if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect))
14753 return false;
14754 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
14755 if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree || window->SkipItems)
14756 return false;
14757
14758 const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect;
14759 ImGuiID id = g.LastItemData.ID;
14760 if (id == 0)
14761 {
14762 id = window->GetIDFromRectangle(r_abs: display_rect);
14763 KeepAliveID(id);
14764 }
14765 if (g.DragDropPayload.SourceId == id)
14766 return false;
14767
14768 IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14769 g.DragDropTargetRect = display_rect;
14770 g.DragDropTargetClipRect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect : window->ClipRect;
14771 g.DragDropTargetId = id;
14772 g.DragDropWithinTarget = true;
14773 return true;
14774}
14775
14776bool ImGui::IsDragDropPayloadBeingAccepted()
14777{
14778 ImGuiContext& g = *GImGui;
14779 return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
14780}
14781
14782const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
14783{
14784 ImGuiContext& g = *GImGui;
14785 ImGuiPayload& payload = g.DragDropPayload;
14786 IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
14787 IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
14788 if (type != NULL && !payload.IsDataType(type))
14789 return NULL;
14790
14791 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
14792 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
14793 const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
14794 ImRect r = g.DragDropTargetRect;
14795 float r_surface = r.GetWidth() * r.GetHeight();
14796 if (r_surface > g.DragDropAcceptIdCurrRectSurface)
14797 return NULL;
14798
14799 g.DragDropAcceptFlags = flags;
14800 g.DragDropAcceptIdCurr = g.DragDropTargetId;
14801 g.DragDropAcceptIdCurrRectSurface = r_surface;
14802 //IMGUI_DEBUG_LOG("AcceptDragDropPayload(): %08X: accept\n", g.DragDropTargetId);
14803
14804 // Render default drop visuals
14805 payload.Preview = was_accepted_previously;
14806 flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that live for 1 frame)
14807 if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
14808 RenderDragDropTargetRect(bb: r, item_clip_rect: g.DragDropTargetClipRect);
14809
14810 g.DragDropAcceptFrameCount = g.FrameCount;
14811 if ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) && g.DragDropMouseButton == -1)
14812 payload.Delivery = was_accepted_previously && (g.DragDropSourceFrameCount < g.FrameCount);
14813 else
14814 payload.Delivery = was_accepted_previously && !IsMouseDown(button: g.DragDropMouseButton); // For extern drag sources affecting OS window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased()
14815 if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
14816 return NULL;
14817
14818 if (payload.Delivery)
14819 IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] AcceptDragDropPayload(): 0x%08X: payload delivery\n", g.DragDropTargetId);
14820 return &payload;
14821}
14822
14823// FIXME-STYLE FIXME-DRAGDROP: Settle on a proper default visuals for drop target.
14824void ImGui::RenderDragDropTargetRect(const ImRect& bb, const ImRect& item_clip_rect)
14825{
14826 ImGuiContext& g = *GImGui;
14827 ImGuiWindow* window = g.CurrentWindow;
14828 ImRect bb_display = bb;
14829 bb_display.ClipWith(r: item_clip_rect); // Clip THEN expand so we have a way to visualize that target is not entirely visible.
14830 bb_display.Expand(amount: 3.5f);
14831 bool push_clip_rect = !window->ClipRect.Contains(r: bb_display);
14832 if (push_clip_rect)
14833 window->DrawList->PushClipRectFullScreen();
14834 window->DrawList->AddRect(p_min: bb_display.Min, p_max: bb_display.Max, col: GetColorU32(idx: ImGuiCol_DragDropTarget), rounding: 0.0f, flags: 0, thickness: 2.0f);
14835 if (push_clip_rect)
14836 window->DrawList->PopClipRect();
14837}
14838
14839const ImGuiPayload* ImGui::GetDragDropPayload()
14840{
14841 ImGuiContext& g = *GImGui;
14842 return (g.DragDropActive && g.DragDropPayload.DataFrameCount != -1) ? &g.DragDropPayload : NULL;
14843}
14844
14845void ImGui::EndDragDropTarget()
14846{
14847 ImGuiContext& g = *GImGui;
14848 IM_ASSERT(g.DragDropActive);
14849 IM_ASSERT(g.DragDropWithinTarget);
14850 g.DragDropWithinTarget = false;
14851
14852 // Clear drag and drop state payload right after delivery
14853 if (g.DragDropPayload.Delivery)
14854 ClearDragDrop();
14855}
14856
14857//-----------------------------------------------------------------------------
14858// [SECTION] LOGGING/CAPTURING
14859//-----------------------------------------------------------------------------
14860// All text output from the interface can be captured into tty/file/clipboard.
14861// By default, tree nodes are automatically opened during logging.
14862//-----------------------------------------------------------------------------
14863
14864// Pass text data straight to log (without being displayed)
14865static inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args)
14866{
14867 if (g.LogFile)
14868 {
14869 g.LogBuffer.Buf.resize(new_size: 0);
14870 g.LogBuffer.appendfv(fmt, args);
14871 ImFileWrite(data: g.LogBuffer.c_str(), sz: sizeof(char), count: (ImU64)g.LogBuffer.size(), f: g.LogFile);
14872 }
14873