openMSX
imgui.cc
Go to the documentation of this file.
1// dear imgui, v1.91.5 WIP
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 - BEFORE 1.87, BACKENDS USED TO WRITE TO io.NavInputs[]. This is now obsolete. Please call io functions instead!
179 - If you need to share inputs between your game and the Dear ImGui interface, the easiest approach is to go all-or-nothing,
180 with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
181
182 - REMOTE INPUTS SHARING & MOUSE EMULATION
183 - PS4/PS5 users: Consider emulating a mouse cursor with DualShock touch pad or a spare analog stick as a mouse-emulation fallback.
184 - 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)
185 in order to share your PC mouse/keyboard.
186 - See https://github.com/ocornut/imgui/wiki/Useful-Extensions#remoting for other remoting solutions.
187 - 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.
188 Enabling io.ConfigNavMoveSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs Dear ImGui to move your mouse cursor along with navigation movements.
189 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.
190 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.
191 (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!)
192 (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
193 to set a boolean to ignore your other external mouse positions until the external source is moved again.)
194
195
196 PROGRAMMER GUIDE
197 ================
198
199 READ FIRST
200 ----------
201 - Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki)
202 - Your code creates the UI every frame of your application loop, if your code doesn't run the UI is gone!
203 The UI can be highly dynamic, there are no construction or destruction steps, less superfluous
204 data retention on your side, less state duplication, less state synchronization, fewer bugs.
205 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
206 Or browse https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html for interactive web version.
207 - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
208 - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
209 You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki.
210 - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
211 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,
212 where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
213 - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
214 - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
215 If you get an assert, read the messages and comments around the assert.
216 - This codebase aims to be highly optimized:
217 - A typical idle frame should never call malloc/free.
218 - We rely on a maximum of constant-time or O(N) algorithms. Limiting searches/scans as much as possible.
219 - We put particular energy in making sure performances are decent with typical "Debug" build settings as well.
220 Which mean we tend to avoid over-relying on "zero-cost abstraction" as they aren't zero-cost at all.
221 - This codebase aims to be both highly opinionated and highly flexible:
222 - This code works because of the things it choose to solve or not solve.
223 - C++: this is a pragmatic C-ish codebase: we don't use fancy C++ features, we don't include C++ headers,
224 and ImGui:: is a namespace. We rarely use member functions (and when we did, I am mostly regretting it now).
225 This is to increase compatibility, increase maintainability and facilitate use from other languages.
226 - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
227 See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
228 We can can optionally export math operators for ImVec2/ImVec4 using IMGUI_DEFINE_MATH_OPERATORS, which we use internally.
229 - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction
230 (so don't use ImVector in your code or at our own risk!).
231 - Building: We don't use nor mandate a build system for the main library.
232 This is in an effort to ensure that it works in the real world aka with any esoteric build setup.
233 This is also because providing a build system for the main library would be of little-value.
234 The build problems are almost never coming from the main library but from specific backends.
235
236
237 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
238 ----------------------------------------------
239 - Update submodule or copy/overwrite every file.
240 - About imconfig.h:
241 - You may modify your copy of imconfig.h, in this case don't overwrite it.
242 - or you may locally branch to modify imconfig.h and merge/rebase latest.
243 - or you may '#define IMGUI_USER_CONFIG "my_config_file.h"' globally from your build system to
244 specify a custom path for your imconfig.h file and instead not have to modify the default one.
245
246 - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h)
247 - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master".
248 - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file.
249 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
250 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
251 from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
252 likely be a comment about it. Please report any issue to the GitHub page!
253 - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file.
254 - Try to keep your copy of Dear ImGui reasonably up to date!
255
256
257 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
258 ---------------------------------------------------------------
259 - See https://github.com/ocornut/imgui/wiki/Getting-Started.
260 - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
261 - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder.
262 - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system.
263 It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL).
264 - 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.
265 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
266 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
267 Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
268 phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui::Render().
269 - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code.
270 - 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.
271
272
273 HOW A SIMPLE APPLICATION MAY LOOK LIKE
274 --------------------------------------
275 EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder).
276 The sub-folders in examples/ contain examples applications following this structure.
277
278 // Application init: create a dear imgui context, setup some options, load fonts
279 ImGui::CreateContext();
280 ImGuiIO& io = ImGui::GetIO();
281 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
282 // TODO: Fill optional fields of the io structure later.
283 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
284
285 // Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
286 ImGui_ImplWin32_Init(hwnd);
287 ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
288
289 // Application main loop
290 while (true)
291 {
292 // Feed inputs to dear imgui, start new frame
293 ImGui_ImplDX11_NewFrame();
294 ImGui_ImplWin32_NewFrame();
295 ImGui::NewFrame();
296
297 // Any application code here
298 ImGui::Text("Hello, world!");
299
300 // Render dear imgui into screen
301 ImGui::Render();
302 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
303 g_pSwapChain->Present(1, 0);
304 }
305
306 // Shutdown
307 ImGui_ImplDX11_Shutdown();
308 ImGui_ImplWin32_Shutdown();
309 ImGui::DestroyContext();
310
311 EXHIBIT 2: IMPLEMENTING CUSTOM BACKEND / CUSTOM ENGINE
312
313 // Application init: create a dear imgui context, setup some options, load fonts
314 ImGui::CreateContext();
315 ImGuiIO& io = ImGui::GetIO();
316 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
317 // TODO: Fill optional fields of the io structure later.
318 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
319
320 // Build and load the texture atlas into a texture
321 // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
322 int width, height;
323 unsigned char* pixels = nullptr;
324 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
325
326 // At this point you've got the texture data and you need to upload that to your graphic system:
327 // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
328 // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
329 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
330 io.Fonts->SetTexID((void*)texture);
331
332 // Application main loop
333 while (true)
334 {
335 // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
336 // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends)
337 io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds)
338 io.DisplaySize.x = 1920.0f; // set the current display width
339 io.DisplaySize.y = 1280.0f; // set the current display height here
340 io.AddMousePosEvent(mouse_x, mouse_y); // update mouse position
341 io.AddMouseButtonEvent(0, mouse_b[0]); // update mouse button states
342 io.AddMouseButtonEvent(1, mouse_b[1]); // update mouse button states
343
344 // Call NewFrame(), after this point you can use ImGui::* functions anytime
345 // (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere)
346 ImGui::NewFrame();
347
348 // Most of your application code here
349 ImGui::Text("Hello, world!");
350 MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
351 MyGameRender(); // may use any Dear ImGui functions as well!
352
353 // Render dear imgui, swap buffers
354 // (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)
355 ImGui::EndFrame();
356 ImGui::Render();
357 ImDrawData* draw_data = ImGui::GetDrawData();
358 MyImGuiRenderFunction(draw_data);
359 SwapBuffers();
360 }
361
362 // Shutdown
363 ImGui::DestroyContext();
364
365 To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application,
366 you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
367 Please read the FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" about this.
368
369
370 HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
371 ---------------------------------------------
372 The backends in impl_impl_XXX.cpp files contain many working implementations of a rendering function.
373
374 void MyImGuiRenderFunction(ImDrawData* draw_data)
375 {
376 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
377 // TODO: Setup texture sampling state: sample with bilinear filtering (NOT point/nearest filtering). Use 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines;' to allow point/nearest filtering.
378 // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
379 // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
380 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
381 ImVec2 clip_off = draw_data->DisplayPos;
382 for (int n = 0; n < draw_data->CmdListsCount; n++)
383 {
384 const ImDrawList* cmd_list = draw_data->CmdLists[n];
385 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by Dear ImGui
386 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by Dear ImGui
387 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
388 {
389 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
390 if (pcmd->UserCallback)
391 {
392 pcmd->UserCallback(cmd_list, pcmd);
393 }
394 else
395 {
396 // Project scissor/clipping rectangles into framebuffer space
397 ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
398 ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
399 if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
400 continue;
401
402 // We are using scissoring to clip some objects. All low-level graphics API should support it.
403 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
404 // (some elements visible outside their bounds) but you can fix that once everything else works!
405 // - Clipping coordinates are provided in imgui coordinates space:
406 // - For a given viewport, draw_data->DisplayPos == viewport->Pos and draw_data->DisplaySize == viewport->Size
407 // - 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.
408 // - In the interest of supporting multi-viewport applications (see 'docking' branch on github),
409 // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
410 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
411 MyEngineSetScissor(clip_min.x, clip_min.y, clip_max.x, clip_max.y);
412
413 // The texture for the draw call is specified by pcmd->GetTexID().
414 // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
415 MyEngineBindTexture((MyTexture*)pcmd->GetTexID());
416
417 // Render 'pcmd->ElemCount/3' indexed triangles.
418 // 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.
419 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer + pcmd->IdxOffset, vtx_buffer, pcmd->VtxOffset);
420 }
421 }
422 }
423 }
424
425
426 API BREAKING CHANGES
427 ====================
428
429 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
430 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.
431 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.
432 You can read releases logs https://github.com/ocornut/imgui/releases for more details.
433
434(Docking/Viewport Branch)
435 - 2024/XX/XX (1.XXXX) - when multi-viewports are enabled, all positions will be in your natural OS coordinates space. It means that:
436 - reference to hard-coded positions such as in SetNextWindowPos(ImVec2(0,0)) are probably not what you want anymore.
437 you may use GetMainViewport()->Pos to offset hard-coded positions, e.g. SetNextWindowPos(GetMainViewport()->Pos)
438 - likewise io.MousePos and GetMousePos() will use OS coordinates.
439 If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos.
440
441 - 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).
442 - 2024/10/14 (1.91.4) - moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool.
443 moved ImGuiConfigFlags_NavNoCaptureKeyboard to standalone io.ConfigNavCaptureKeyboard bool (note the inverted value!).
444 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!
445 - 2024/10/10 (1.91.4) - the typedef for ImTextureID now defaults to ImU64 instead of void*. (#1641)
446 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.
447 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().
448 in doubt it is almost always better to do an intermediate intptr_t cast, since it allows casting any pointer/integer type without warning:
449 - May warn: ImGui::Image((void*)MyTextureData, ...);
450 - May warn: ImGui::Image((void*)(intptr_t)MyTextureData, ...);
451 - Won't warn: ImGui::Image((ImTextureID)(intptr_t)MyTextureData), ...);
452 - note that you can always define ImTextureID to be your own high-level structures (with dedicated constructors) if you like.
453 - 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)
454 - 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).
455 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)
456 - 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)
457 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()
458 - 2024/08/23 (1.91.1) - renamed ImGuiChildFlags_Border to ImGuiChildFlags_Borders for consistency. kept inline redirection flag.
459 - 2024/08/22 (1.91.1) - moved some functions from ImGuiIO to ImGuiPlatformIO structure:
460 - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn + changed 'void* user_data' to 'ImGuiContext* ctx'. Pull your user data from platform_io.ClipboardUserData.
461 - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn + same as above line.
462 - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn (#7660)
463 - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn
464 - io.PlatformLocaleDecimalPoint -> platform_io.Platform_LocaleDecimalPoint (#7389, #6719, #2278)
465 - access those via GetPlatformIO() instead of GetIO().
466 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.
467 - commented the old ImageButton() signature obsoleted in 1.89 (~August 2022). As a reminder:
468 - 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)
469 - new ImageButton() since 1.89 requires an explicit 'const char* str_id'
470 - old ImageButton() before 1.89 had frame_padding' override argument.
471 - new ImageButton() since 1.89 always use style.FramePadding, which you can freely override with PushStyleVar()/PopStyleVar().
472 - 2024/07/25 (1.91.0) - obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). (see #7838 on GitHub for more info)
473 you should never need those functions. you can do everything with GetCursorScreenPos() and GetContentRegionAvail() in a more simple way.
474 - instead of: GetWindowContentRegionMax().x - GetCursorPos().x
475 - you can use: GetContentRegionAvail().x
476 - instead of: GetWindowContentRegionMax().x + GetWindowPos().x
477 - you can use: GetCursorScreenPos().x + GetContentRegionAvail().x // when called from left edge of window
478 - instead of: GetContentRegionMax()
479 - you can use: GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos() // right edge in local coordinates
480 - instead of: GetWindowContentRegionMax().x - GetWindowContentRegionMin().x
481 - you can use: GetContentRegionAvail() // when called from left edge of window
482 - 2024/07/15 (1.91.0) - renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups. (#1379, #1468, #2200, #4936, #5216, #7302, #7573)
483 (internals: also renamed ImGuiItemFlags_SelectableDontClosePopup into ImGuiItemFlags_AutoClosePopups with inverted behaviors)
484 - 2024/07/15 (1.91.0) - obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag(ImGuiItemFlags_ButtonRepeat, ...)/PopItemFlag().
485 - 2024/07/02 (1.91.0) - commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456)
486 - commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456)
487 - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc.
488 - 2024/07/02 (1.91.0) - IO, IME: renamed platform IME hook and added explicit context for consistency and future-proofness.
489 - old: io.SetPlatformImeDataFn(ImGuiViewport* viewport, ImGuiPlatformImeData* data);
490 - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);
491 - 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.
492 - old: BeginChild("Name", size, 0, ImGuiWindowFlags_NavFlattened);
493 - new: BeginChild("Name", size, ImGuiChildFlags_NavFlattened, 0);
494 - 2024/06/21 (1.90.9) - io: ClearInputKeys() (first exposed in 1.89.8) doesn't clear mouse data, newly added ClearInputMouse() does.
495 - 2024/06/20 (1.90.9) - renamed ImGuiDragDropFlags_SourceAutoExpirePayload to ImGuiDragDropFlags_PayloadAutoExpire.
496 - 2024/06/18 (1.90.9) - style: renamed ImGuiCol_TabActive -> ImGuiCol_TabSelected, ImGuiCol_TabUnfocused -> ImGuiCol_TabDimmed, ImGuiCol_TabUnfocusedActive -> ImGuiCol_TabDimmedSelected.
497 - 2024/06/10 (1.90.9) - removed old nested structure: renaming ImGuiStorage::ImGuiStoragePair type to ImGuiStoragePair (simpler for many languages).
498 - 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.
499 - 2024/06/06 (1.90.8) - removed 'ImGuiButtonFlags_MouseButtonDefault_ = ImGuiButtonFlags_MouseButtonLeft', was mostly unused and misleading.
500 - 2024/05/27 (1.90.7) - commented out obsolete symbols marked obsolete in 1.88 (May 2022):
501 - old: CaptureKeyboardFromApp(bool)
502 - new: SetNextFrameWantCaptureKeyboard(bool)
503 - old: CaptureMouseFromApp(bool)
504 - new: SetNextFrameWantCaptureMouse(bool)
505 - 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).
506 - inputs (internals): renamed ImGuiInputFlags_RouteGlobalLow -> ImGuiInputFlags_RouteGlobal, ImGuiInputFlags_RouteGlobal -> ImGuiInputFlags_RouteGlobalOverFocused, ImGuiInputFlags_RouteGlobalHigh -> ImGuiInputFlags_RouteGlobalHighest.
507 - inputs (internals): Shortcut(), SetShortcutRouting(): swapped last two parameters order in function signatures:
508 - old: Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0);
509 - new: Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0, ImGuiID owner_id = 0);
510 - inputs (internals): owner-aware versions of IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(): swapped last two parameters order in function signatures.
511 - old: IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
512 - new: IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0);
513 - old: IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0);
514 - new: IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0);
515 for various reasons those changes makes sense. They are being made because making some of those API public.
516 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.
517 - 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)
518 - old: DockSpaceOverViewport(const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, ...);
519 - new: DockSpaceOverViewport(ImGuiID dockspace_id = 0, const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, ...);
520 - 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.
521 - it shouldn't really affect you unless you had custom shortcut swapping in place for macOS X apps.
522 - 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)
523 - 2024/05/14 (1.90.7) - backends: SDL_Renderer2 and SDL_Renderer3 backend now take a SDL_Renderer* in their RenderDrawData() functions.
524 - 2024/04/18 (1.90.6) - TreeNode: Fixed a layout inconsistency when using an empty/hidden label followed by a SameLine() call. (#7505, #282)
525 - 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).
526 - new: TreeNode("##Hidden"); SameLine(0, 0); Text("Hello"); // <-- This is correct for all styles values.
527 with the fix, IF you were successfully using TreeNode("")+SameLine(); you will now have extra spacing between your TreeNode and the following item.
528 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.
529 (Note: when using this idiom you are likely to also use ImGuiTreeNodeFlags_SpanAvailWidth).
530 - 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)
531 - 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)
532 - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX)
533 - 2024/01/15 (1.90.2) - commented out obsolete ImGuiIO::ImeWindowHandle marked obsolete in 1.87, favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'.
534 - 2023/12/19 (1.90.1) - commented out obsolete ImGuiKey_KeyPadEnter redirection to ImGuiKey_KeypadEnter.
535 - 2023/11/06 (1.90.1) - removed CalcListClipping() marked obsolete in 1.86. Prefer using ImGuiListClipper which can return non-contiguous ranges.
536 - 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.
537 - 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.
538 - 2023/11/09 (1.90.0) - removed IM_OFFSETOF() macro in favor of using offsetof() available in C++11. Kept redirection define (will obsolete).
539 - 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).
540 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.
541 - 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.
542 - old: BeginChild("Name", size, true)
543 - new: BeginChild("Name", size, ImGuiChildFlags_Border)
544 - old: BeginChild("Name", size, false)
545 - new: BeginChild("Name", size) or BeginChild("Name", 0) or BeginChild("Name", size, ImGuiChildFlags_None)
546 **AMEND FROM THE FUTURE: from 1.91.1, 'ImGuiChildFlags_Border' is called 'ImGuiChildFlags_Borders'**
547 - 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.
548 - old: BeginChild("Name", size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding);
549 - new: BeginChild("Name", size, ImGuiChildFlags_AlwaysUseWindowPadding, 0);
550 - 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).
551 - 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)
552 - 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).
553 - old: bool Combo(const char* label, int* current_item, bool (*getter)(void* user_data, int idx, const char** out_text), ...)
554 - new: bool Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
555 - old: bool ListBox(const char* label, int* current_item, bool (*getting)(void* user_data, int idx, const char** out_text), ...);
556 - new: bool ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
557 - 2023/09/08 (1.90.0) - commented out obsolete redirecting functions:
558 - GetWindowContentRegionWidth() -> use GetWindowContentRegionMax().x - GetWindowContentRegionMin().x. Consider that generally 'GetContentRegionAvail().x' is more useful.
559 - ImDrawCornerFlags_XXX -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details + grep commented names in sources.
560 - 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
561 - 2023/08/25 (1.89.9) - clipper: Renamed IncludeRangeByIndices() (also called ForceDisplayRangeByIndices() before 1.89.6) to IncludeItemsByIndex(). Kept inline redirection function. Sorry!
562 - 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)
563 - 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).
564 - 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete).
565 - 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.
566 - 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)
567 - 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.
568 - 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.
569 - 2023/05/22 (1.89.6) - listbox: commented out obsolete/redirecting functions that were marked obsolete more than two years ago:
570 - ListBoxHeader() -> use BeginListBox() (note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for reference)
571 - ListBoxFooter() -> use EndListBox()
572 - 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().
573 - 2023/05/15 (1.89.6) - clipper: renamed ImGuiListClipper::ForceDisplayRangeByIndices() to ImGuiListClipper::IncludeRangeByIndices().
574 - 2023/03/14 (1.89.4) - commented out redirecting enums/functions names that were marked obsolete two years ago:
575 - ImGuiSliderFlags_ClampOnInput -> use ImGuiSliderFlags_AlwaysClamp
576 - ImGuiInputTextFlags_AlwaysInsertMode -> use ImGuiInputTextFlags_AlwaysOverwrite
577 - ImDrawList::AddBezierCurve() -> use ImDrawList::AddBezierCubic()
578 - ImDrawList::PathBezierCurveTo() -> use ImDrawList::PathBezierCubicCurveTo()
579 - 2023/03/09 (1.89.4) - renamed PushAllowKeyboardFocus()/PopAllowKeyboardFocus() to PushTabStop()/PopTabStop(). Kept inline redirection functions (will obsolete).
580 - 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.
581 - 2023/02/15 (1.89.4) - moved the optional "courtesy maths operators" implementation from imgui_internal.h in imgui.h.
582 Even though we encourage using your own maths types and operators by setting up IM_VEC2_CLASS_EXTRA,
583 it has been frequently requested by people to use our own. We had an opt-in define which was
584 previously fulfilled in imgui_internal.h. It is now fulfilled in imgui.h. (#6164)
585 - OK: #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui.h" / #include "imgui_internal.h"
586 - Error: #include "imgui.h" / #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui_internal.h"
587 - 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.
588 - 2022/10/26 (1.89) - commented out redirecting OpenPopupContextItem() which was briefly the name of OpenPopupOnItemClick() from 1.77 to 1.79.
589 - 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.
590 - 2022/09/26 (1.89) - renamed and merged keyboard modifiers key enums and flags into a same set. Kept inline redirection enums (will obsolete).
591 - ImGuiKey_ModCtrl and ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl
592 - ImGuiKey_ModShift and ImGuiModFlags_Shift -> ImGuiMod_Shift
593 - ImGuiKey_ModAlt and ImGuiModFlags_Alt -> ImGuiMod_Alt
594 - ImGuiKey_ModSuper and ImGuiModFlags_Super -> ImGuiMod_Super
595 the ImGuiKey_ModXXX were introduced in 1.87 and mostly used by backends.
596 the ImGuiModFlags_XXX have been exposed in imgui.h but not really used by any public api only by third-party extensions.
597 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.
598 - 2022/09/20 (1.89) - ImGuiKey is now a typed enum, allowing ImGuiKey_XXX symbols to be named in debuggers.
599 this will require uses of legacy backend-dependent indices to be casted, e.g.
600 - with imgui_impl_glfw: IsKeyPressed(GLFW_KEY_A) -> IsKeyPressed((ImGuiKey)GLFW_KEY_A);
601 - with imgui_impl_win32: IsKeyPressed('A') -> IsKeyPressed((ImGuiKey)'A')
602 - etc. However if you are upgrading code you might well use the better, backend-agnostic IsKeyPressed(ImGuiKey_A) now!
603 - 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);
604 - 2022/09/05 (1.89) - commented out redirecting functions/enums names that were marked obsolete in 1.77 and 1.78 (June 2020):
605 - 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.
606 - 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.
607 - BeginPopupContextWindow(const char*, ImGuiMouseButton, bool) -> use BeginPopupContextWindow(const char*, ImGuiPopupFlags)
608 - 2022/09/02 (1.89) - obsoleted using SetCursorPos()/SetCursorScreenPos() to extend parent window/cell boundaries.
609 this relates to when moving the cursor position beyond current boundaries WITHOUT submitting an item.
610 - previously this would make the window content size ~200x200:
611 Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();
612 - instead, please submit an item:
613 Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End();
614 - alternative:
615 Begin(...) + Dummy(ImVec2(200,200)) + End();
616 - content size is now only extended when submitting an item!
617 - with '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will now be detected and assert.
618 - without '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will silently be fixed until we obsolete it.
619 - 2022/08/03 (1.89) - changed signature of ImageButton() function. Kept redirection function (will obsolete).
620 - added 'const char* str_id' parameter + removed 'int frame_padding = -1' parameter.
621 - 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));
622 - 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.
623 - had a FramePadding override which was inconsistent with other functions and made the already-long signature even longer.
624 - 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));
625 - requires an explicit identifier. You may still use e.g. PushID() calls and then pass an empty identifier.
626 - always uses style.FramePadding for padding, to be consistent with other buttons. You may use PushStyleVar() to alter this.
627 - 2022/07/08 (1.89) - inputs: removed io.NavInputs[] and ImGuiNavInput enum (following 1.87 changes).
628 - Official backends from 1.87+ -> no issue.
629 - Official backends from 1.60 to 1.86 -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need updating!
630 - Custom backends not writing to io.NavInputs[] -> no issue.
631 - Custom backends writing to io.NavInputs[] -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need fixing!
632 - TL;DR: Backends should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values instead of filling io.NavInput[].
633 - 2022/06/15 (1.88) - renamed IMGUI_DISABLE_METRICS_WINDOW to IMGUI_DISABLE_DEBUG_TOOLS for correctness. kept support for old define (will obsolete).
634 - 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.
635 - 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.
636 - 2022/01/20 (1.87) - inputs: reworded gamepad IO.
637 - Backend writing to io.NavInputs[] -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values.
638 - 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).
639 - 2022/01/17 (1.87) - inputs: reworked mouse IO.
640 - Backend writing to io.MousePos -> backend should call io.AddMousePosEvent()
641 - Backend writing to io.MouseDown[] -> backend should call io.AddMouseButtonEvent()
642 - Backend writing to io.MouseWheel -> backend should call io.AddMouseWheelEvent()
643 - Backend writing to io.MouseHoveredViewport -> backend should call io.AddMouseViewportEvent() [Docking branch w/ multi-viewports only]
644 note: for all calls to IO new functions, the Dear ImGui context should be bound/current.
645 read https://github.com/ocornut/imgui/issues/4921 for details.
646 - 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.
647 - IsKeyPressed(MY_NATIVE_KEY_XXX) -> use IsKeyPressed(ImGuiKey_XXX)
648 - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX)
649 - 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).
650 - 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.*
651 - 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.
652 - 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.
653 - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum.
654 - 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'.
655 - 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)
656 - ImGui::SetNextTreeNodeOpen() -> use ImGui::SetNextItemOpen()
657 - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x
658 - ImGui::TreeAdvanceToLabelPos() -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing());
659 - ImFontAtlas::CustomRect -> use ImFontAtlasCustomRect
660 - ImGuiColorEditFlags_RGB/HSV/HEX -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex
661 - 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
662 - 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.
663 - 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.
664 - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019):
665 - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList()
666 - ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder
667 - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID().
668 - if you are using official backends from the source tree: you have nothing to do.
669 - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID().
670 - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags.
671 - ImDrawCornerFlags_TopLeft -> use ImDrawFlags_RoundCornersTopLeft
672 - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight
673 - ImDrawCornerFlags_None -> use ImDrawFlags_RoundCornersNone etc.
674 flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API.
675 breaking: the default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners":
676 - rounding == 0.0f + flags == 0 --> meant no rounding --> unchanged (common use)
677 - rounding > 0.0f + flags != 0 --> meant rounding --> unchanged (common use)
678 - rounding == 0.0f + flags != 0 --> meant no rounding --> unchanged (unlikely use)
679 - rounding > 0.0f + flags == 0 --> meant no rounding --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f.
680 this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok.
681 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.
682 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).
683 - 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018):
684 - ImGui::SetScrollHere() -> use ImGui::SetScrollHereY()
685 - 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.
686 - 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.
687 - 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'.
688 - 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.
689 - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete).
690 - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete).
691 - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete).
692 - 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.
693 - renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags.
694 - renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API.
695 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018):
696 - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit().
697 - ImGuiCol_ModalWindowDarkening -> use ImGuiCol_ModalWindowDimBg
698 - ImGuiInputTextCallback -> use ImGuiTextEditCallback
699 - ImGuiInputTextCallbackData -> use ImGuiTextEditCallbackData
700 - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete).
701 - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added!
702 - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API.
703 - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures
704 - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/.
705 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018):
706 - io.RenderDrawListsFn pointer -> use ImGui::GetDrawData() value and call the render function of your backend
707 - ImGui::IsAnyWindowFocused() -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)
708 - ImGui::IsAnyWindowHovered() -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
709 - ImGuiStyleVar_Count_ -> use ImGuiStyleVar_COUNT
710 - ImGuiMouseCursor_Count_ -> use ImGuiMouseCursor_COUNT
711 - removed redirecting functions names that were marked obsolete in 1.61 (May 2018):
712 - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision.
713 - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter.
714 - 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).
715 - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently).
716 - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton.
717 - 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.
718 - 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.
719 - 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!
720 - 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().
721 replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
722 worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
723 - if you omitted the 'power' parameter (likely!), you are not affected.
724 - 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.
725 - 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.
726 see https://github.com/ocornut/imgui/issues/3361 for all details.
727 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.
728 for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.
729 - 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.
730 - 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.
731 - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79]
732 - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
733 - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
734 - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
735 - 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.
736 - 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.
737 - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
738 - ShowTestWindow() -> use ShowDemoWindow()
739 - IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
740 - IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
741 - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
742 - GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing()
743 - ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg
744 - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding
745 - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
746 - IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS
747 - 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.
748 - 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).
749 - 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.
750 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
751 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
752 - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
753 - Begin() [old 5 args version] -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
754 - IsRootWindowOrAnyChildHovered() -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
755 - AlignFirstTextHeightToWidgets() -> use AlignTextToFramePadding()
756 - SetNextWindowPosCenter() -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
757 - ImFont::Glyph -> use ImFontGlyph
758 - 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.
759 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.
760 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).
761 If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
762 - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
763 - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
764 - 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.
765 - 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
766 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.
767 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.
768 Please reach out if you are affected.
769 - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
770 - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
771 - 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.
772 - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
773 - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
774 - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
775 - 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!
776 - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
777 - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
778 - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
779 - 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.
780 - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
781 - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
782 - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
783 - 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.
784 If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
785 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
786 - 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.
787 NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
788 Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
789 - 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).
790 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
791 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
792 - 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.
793 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
794 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
795 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
796 - 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.).
797 old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports.
798 when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call.
799 in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
800 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
801 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
802 - 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.
803 If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
804 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.
805 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.
806 - 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",
807 consistent with other functions. Kept redirection functions (will obsolete).
808 - 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.
809 - 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).
810 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
811 - 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.
812 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
813 - 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.
814 - 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.
815 - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
816 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
817 - removed Shutdown() function, as DestroyContext() serve this purpose.
818 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
819 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
820 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
821 - 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.
822 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
823 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
824 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
825 - 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.
826 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
827 - 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
828 - 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.
829 - 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.
830 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
831 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
832 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
833 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
834 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
835 - 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.
836 - 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.
837 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.
838 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
839 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
840 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
841 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
842 - 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.
843 - 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.
844 - 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.
845 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
846 IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
847 IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
848 IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
849 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
850 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
851 - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
852 - 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).
853 - 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)".
854 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
855 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
856 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
857 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
858 - 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.
859 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
860 - 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.
861 - 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).
862 - 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).
863 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
864 - 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.
865 - 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.
866 - 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))'
867 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
868 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
869 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
870 - 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().
871 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
872 - 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.
873 - 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.
874 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
875 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.
876 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:
877 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); }
878 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.
879 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
880 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
881 - 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).
882 - 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.
883 - 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).
884 - 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)
885 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
886 - 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.
887 - 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.
888 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
889 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
890 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
891 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.
892 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!
893 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
894 - 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.
895 - 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
896 - 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.
897 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
898 - 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.
899 this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
900 - 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.
901 - the signature of the io.RenderDrawListsFn handler has changed!
902 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
903 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
904 parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
905 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
906 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
907 - 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.
908 - 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!
909 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
910 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
911 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
912 - 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.
913 - 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
914 - 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!
915 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
916 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
917 - 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.
918 - 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.
919 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
920 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
921 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
922 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
923 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
924 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
925 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
926 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
927 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
928 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
929 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
930 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
931 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
932 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
933 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
934 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
935 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
936 - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
937 - old: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
938 - new: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier);
939 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.
940 - 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()
941 - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
942 - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
943 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
944 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
945 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
946 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
947 - 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)
948 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
949 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
950 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
951 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
952 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
953 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
954
955
956 FREQUENTLY ASKED QUESTIONS (FAQ)
957 ================================
958
959 Read all answers online:
960 https://www.dearimgui.com/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
961 Read all answers locally (with a text editor or ideally a Markdown viewer):
962 docs/FAQ.md
963 Some answers are copied down here to facilitate searching in code.
964
965 Q&A: Basics
966 ===========
967
968 Q: Where is the documentation?
969 A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++.
970 - Run the examples/ applications and explore them.
971 - Read Getting Started (https://github.com/ocornut/imgui/wiki/Getting-Started) guide.
972 - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
973 - The demo covers most features of Dear ImGui, so you can read the code and see its output.
974 - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
975 - 20+ standalone example applications using e.g. OpenGL/DirectX are provided in the
976 examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
977 - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
978 - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
979 - Your programming IDE is your friend, find the type or function declaration to find comments
980 associated with it.
981
982 Q: What is this library called?
983 Q: Which version should I get?
984 >> This library is called "Dear ImGui", please don't call it "ImGui" :)
985 >> See https://www.dearimgui.com/faq for details.
986
987 Q&A: Integration
988 ================
989
990 Q: How to get started?
991 A: Read https://github.com/ocornut/imgui/wiki/Getting-Started. Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
992
993 Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?
994 A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
995 >> See https://www.dearimgui.com/faq for a fully detailed answer. You really want to read this.
996
997 Q. How can I enable keyboard or gamepad controls?
998 Q: How can I use this on a machine without mouse, keyboard or screen? (input share, remote display)
999 Q: I integrated Dear ImGui in my engine and little squares are showing instead of text...
1000 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...
1001 Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...
1002 >> See https://www.dearimgui.com/faq
1003
1004 Q&A: Usage
1005 ----------
1006
1007 Q: About the ID Stack system..
1008 - Why is my widget not reacting when I click on it?
1009 - How can I have widgets with an empty label?
1010 - How can I have multiple widgets with the same label?
1011 - How can I have multiple windows with the same label?
1012 Q: How can I display an image? What is ImTextureID, how does it work?
1013 Q: How can I use my own math types instead of ImVec2?
1014 Q: How can I interact with standard C++ types (such as std::string and std::vector)?
1015 Q: How can I display custom shapes? (using low-level ImDrawList API)
1016 >> See https://www.dearimgui.com/faq
1017
1018 Q&A: Fonts, Text
1019 ================
1020
1021 Q: How should I handle DPI in my application?
1022 Q: How can I load a different font than the default?
1023 Q: How can I easily use icons in my application?
1024 Q: How can I load multiple fonts?
1025 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
1026 >> See https://www.dearimgui.com/faq and https://github.com/ocornut/imgui/blob/master/docs/FONTS.md
1027
1028 Q&A: Concerns
1029 =============
1030
1031 Q: Who uses Dear ImGui?
1032 Q: Can you create elaborate/serious tools with Dear ImGui?
1033 Q: Can you reskin the look of Dear ImGui?
1034 Q: Why using C++ (as opposed to C)?
1035 >> See https://www.dearimgui.com/faq
1036
1037 Q&A: Community
1038 ==============
1039
1040 Q: How can I help?
1041 A: - Businesses: please reach out to "omar AT dearimgui DOT com" if you work in a place using Dear ImGui!
1042 We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
1043 This is among the most useful thing you can do for Dear ImGui. With increased funding, we sustain and grow work on this project.
1044 >>> See https://github.com/ocornut/imgui/wiki/Funding
1045 - Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.
1046 - 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!
1047 - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
1048 You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers.
1049 But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions.
1050 - 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).
1051
1052*/
1053
1054//-------------------------------------------------------------------------
1055// [SECTION] INCLUDES
1056//-------------------------------------------------------------------------
1057
1058#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
1059#define _CRT_SECURE_NO_WARNINGS
1060#endif
1061
1062#ifndef IMGUI_DEFINE_MATH_OPERATORS
1063#define IMGUI_DEFINE_MATH_OPERATORS
1064#endif
1065
1066#include "imgui.h"
1067#ifndef IMGUI_DISABLE
1068#include "imgui_internal.h"
1069
1070// System includes
1071#include <stdio.h> // vsnprintf, sscanf, printf
1072#include <stdint.h> // intptr_t
1073
1074// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
1075#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
1076#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
1077#endif
1078
1079// [Windows] OS specific includes (optional)
1080#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)
1081#define IMGUI_DISABLE_WIN32_FUNCTIONS
1082#endif
1083#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
1084#ifndef WIN32_LEAN_AND_MEAN
1085#define WIN32_LEAN_AND_MEAN
1086#endif
1087#ifndef NOMINMAX
1088#define NOMINMAX
1089#endif
1090#ifndef __MINGW32__
1091#include <Windows.h> // _wfopen, OpenClipboard
1092#else
1093#include <windows.h>
1094#endif
1095#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP || WINAPI_FAMILY == WINAPI_FAMILY_GAMES)
1096// The UWP and GDK Win32 API subsets don't support clipboard nor IME functions
1097#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
1098#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
1099#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
1100#endif
1101#endif
1102
1103// [Apple] OS specific includes
1104#if defined(__APPLE__)
1105#include <TargetConditionals.h>
1106#endif
1107
1108// Visual Studio warnings
1109#ifdef _MSC_VER
1110#pragma warning (disable: 4127) // condition expression is constant
1111#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
1112#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
1113#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
1114#endif
1115#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).
1116#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
1117#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
1118#endif
1119
1120// Clang/GCC warnings with -Weverything
1121#if defined(__clang__)
1122#if __has_warning("-Wunknown-warning-option")
1123#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!
1124#endif
1125#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
1126#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
1127#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.
1128#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.
1129#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.
1130#pragma clang diagnostic ignored "-Wglobal-constructors" // warning: declaration requires a global destructor // similar to above, not sure what the exact difference is.
1131#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
1132#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.
1133#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type 'int'
1134#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
1135#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.
1136#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
1137#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
1138#elif defined(__GNUC__)
1139// We disable -Wpragmas because GCC doesn't provide a has_warning equivalent and some forks/patches may not follow the warning/version association.
1140#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
1141#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
1142#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
1143#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
1144#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
1145#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
1146#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
1147#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
1148#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
1149#endif
1150
1151// Debug options
1152#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.
1153#define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window
1154
1155// 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.
1156static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in
1157static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
1158
1159static const float NAV_ACTIVATE_HIGHLIGHT_TIMER = 0.10f; // Time to highlight an item activated by a shortcut.
1160
1161// Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend)
1162static const float WINDOWS_HOVER_PADDING = 4.0f; // Extend outside window for hovering/resizing (maxxed with TouchPadding) and inside windows for borders. Affect FindHoveredWindow().
1163static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
1164static 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.
1165
1166// Tooltip offset
1167static const ImVec2 TOOLTIP_DEFAULT_OFFSET_MOUSE = ImVec2(16, 10); // Multiplied by g.Style.MouseCursorScale
1168static const ImVec2 TOOLTIP_DEFAULT_OFFSET_TOUCH = ImVec2(0, -20); // Multiplied by g.Style.MouseCursorScale
1169static const ImVec2 TOOLTIP_DEFAULT_PIVOT_TOUCH = ImVec2(0.5f, 1.0f); // Multiplied by g.Style.MouseCursorScale
1170
1171// Docking
1172static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA = 0.50f; // For use with io.ConfigDockingTransparentPayload. Apply to Viewport _or_ WindowBg in host viewport.
1173
1174//-------------------------------------------------------------------------
1175// [SECTION] FORWARD DECLARATIONS
1176//-------------------------------------------------------------------------
1177
1178static void SetCurrentWindow(ImGuiWindow* window);
1179static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags);
1180static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
1181
1182static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
1183
1184// Settings
1185static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
1186static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
1187static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
1188static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
1189static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
1190
1191// Platform Dependents default implementation for ImGuiPlatformIO functions
1192static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx);
1193static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext* ctx, const char* text);
1194static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);
1195static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext* ctx, const char* path);
1196
1197namespace ImGui
1198{
1199// Item
1200static void ItemHandleShortcut(ImGuiID id);
1201
1202// Navigation
1203static void NavUpdate();
1204static void NavUpdateWindowing();
1205static void NavUpdateWindowingOverlay();
1206static void NavUpdateCancelRequest();
1207static void NavUpdateCreateMoveRequest();
1208static void NavUpdateCreateTabbingRequest();
1209static float NavUpdatePageUpPageDown();
1210static inline void NavUpdateAnyRequestFlag();
1211static void NavUpdateCreateWrappingRequest();
1212static void NavEndFrame();
1213static bool NavScoreItem(ImGuiNavItemData* result);
1214static void NavApplyItemToResult(ImGuiNavItemData* result);
1215static void NavProcessItem();
1216static void NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags);
1217static ImGuiInputSource NavCalcPreferredRefPosSource();
1218static ImVec2 NavCalcPreferredRefPos();
1219static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
1220static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
1221static void NavRestoreLayer(ImGuiNavLayer layer);
1222static int FindWindowFocusIndex(ImGuiWindow* window);
1223
1224// Error Checking and Debug Tools
1225static void ErrorCheckNewFrameSanityChecks();
1226static void ErrorCheckEndFrameSanityChecks();
1227static void UpdateDebugToolItemPicker();
1228static void UpdateDebugToolStackQueries();
1229static void UpdateDebugToolFlashStyleColor();
1230
1231// Inputs
1232static void UpdateKeyboardInputs();
1233static void UpdateMouseInputs();
1234static void UpdateMouseWheel();
1235static void UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt);
1236
1237// Misc
1238static void UpdateSettings();
1239static 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);
1240static void RenderWindowOuterBorders(ImGuiWindow* window);
1241static 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);
1242static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
1243static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col);
1244static void RenderDimmedBackgrounds();
1245static void SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect);
1246
1247// Viewports
1248const 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.
1249static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags);
1250static void DestroyViewport(ImGuiViewportP* viewport);
1251static void UpdateViewportsNewFrame();
1252static void UpdateViewportsEndFrame();
1253static void WindowSelectViewport(ImGuiWindow* window);
1254static void WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_window_in_stack);
1255static bool UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport);
1256static bool UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window);
1257static bool GetWindowAlwaysWantOwnViewport(ImGuiWindow* window);
1258static int FindPlatformMonitorForPos(const ImVec2& pos);
1259static int FindPlatformMonitorForRect(const ImRect& r);
1260static void UpdateViewportPlatformMonitor(ImGuiViewportP* viewport);
1261
1262}
1263
1264//-----------------------------------------------------------------------------
1265// [SECTION] CONTEXT AND MEMORY ALLOCATORS
1266//-----------------------------------------------------------------------------
1267
1268// DLL users:
1269// - Heaps and globals are not shared across DLL boundaries!
1270// - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from.
1271// - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL).
1272// - 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.
1273// - 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).
1274
1275// Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
1276// - ImGui::CreateContext() will automatically set this pointer if it is NULL.
1277// Change to a different context by calling ImGui::SetCurrentContext().
1278// - Important: Dear ImGui functions are not thread-safe because of this pointer.
1279// If you want thread-safety to allow N threads to access N different contexts:
1280// - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h:
1281// struct ImGuiContext;
1282// extern thread_local ImGuiContext* MyImGuiTLS;
1283// #define GImGui MyImGuiTLS
1284// 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.
1285// - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
1286// - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace.
1287// - DLL users: read comments above.
1288#ifndef GImGui
1289ImGuiContext* GImGui = NULL;
1290#endif
1291
1292// Memory Allocator functions. Use SetAllocatorFunctions() to change them.
1293// - 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.
1294// - DLL users: read comments above.
1295#ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
1296static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); }
1297static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); }
1298#else
1299static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; }
1300static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
1301#endif
1302static ImGuiMemAllocFunc GImAllocatorAllocFunc = MallocWrapper;
1303static ImGuiMemFreeFunc GImAllocatorFreeFunc = FreeWrapper;
1304static void* GImAllocatorUserData = NULL;
1305
1306//-----------------------------------------------------------------------------
1307// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO)
1308//-----------------------------------------------------------------------------
1309
1310ImGuiStyle::ImGuiStyle()
1311{
1312 Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui.
1313 DisabledAlpha = 0.60f; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.
1314 WindowPadding = ImVec2(8,8); // Padding within a window
1315 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.
1316 WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1317 WindowMinSize = ImVec2(32,32); // Minimum window size
1318 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
1319 WindowMenuButtonPosition = ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
1320 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
1321 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1322 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
1323 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1324 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
1325 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
1326 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
1327 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
1328 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
1329 CellPadding = ImVec2(4,2); // Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows.
1330 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!
1331 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
1332 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
1333 ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
1334 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
1335 GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar
1336 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1337 LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
1338 TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
1339 TabBorderSize = 0.0f; // Thickness of border around tabs.
1340 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.
1341 TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus.
1342 TabBarOverlineSize = 2.0f; // Thickness of tab-bar overline, which highlights the selected tab-bar.
1343 TableAngledHeadersAngle = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees).
1344 TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell
1345 ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
1346 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1347 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.
1348 SeparatorTextBorderSize = 3.0f; // Thickness of border in SeparatorText()
1349 SeparatorTextAlign = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center).
1350 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.
1351 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.
1352 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.
1353 DockingSeparatorSize = 2.0f; // Thickness of resizing border between docked windows
1354 MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1355 AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
1356 AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).
1357 AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
1358 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.
1359 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.
1360
1361 // Behaviors
1362 HoverStationaryDelay = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.
1363 HoverDelayShort = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay.
1364 HoverDelayNormal = 0.40f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). "
1365 HoverFlagsForTooltipMouse = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.
1366 HoverFlagsForTooltipNav = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.
1367
1368 // Default theme
1369 ImGui::StyleColorsDark(this);
1370}
1371
1372// 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.
1373// 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.
1374void ImGuiStyle::ScaleAllSizes(float scale_factor)
1375{
1376 WindowPadding = ImTrunc(WindowPadding * scale_factor);
1377 WindowRounding = ImTrunc(WindowRounding * scale_factor);
1378 WindowMinSize = ImTrunc(WindowMinSize * scale_factor);
1379 ChildRounding = ImTrunc(ChildRounding * scale_factor);
1380 PopupRounding = ImTrunc(PopupRounding * scale_factor);
1381 FramePadding = ImTrunc(FramePadding * scale_factor);
1382 FrameRounding = ImTrunc(FrameRounding * scale_factor);
1383 ItemSpacing = ImTrunc(ItemSpacing * scale_factor);
1384 ItemInnerSpacing = ImTrunc(ItemInnerSpacing * scale_factor);
1385 CellPadding = ImTrunc(CellPadding * scale_factor);
1386 TouchExtraPadding = ImTrunc(TouchExtraPadding * scale_factor);
1387 IndentSpacing = ImTrunc(IndentSpacing * scale_factor);
1388 ColumnsMinSpacing = ImTrunc(ColumnsMinSpacing * scale_factor);
1389 ScrollbarSize = ImTrunc(ScrollbarSize * scale_factor);
1390 ScrollbarRounding = ImTrunc(ScrollbarRounding * scale_factor);
1391 GrabMinSize = ImTrunc(GrabMinSize * scale_factor);
1392 GrabRounding = ImTrunc(GrabRounding * scale_factor);
1393 LogSliderDeadzone = ImTrunc(LogSliderDeadzone * scale_factor);
1394 TabRounding = ImTrunc(TabRounding * scale_factor);
1395 TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImTrunc(TabMinWidthForCloseButton * scale_factor) : FLT_MAX;
1396 TabBarOverlineSize = ImTrunc(TabBarOverlineSize * scale_factor);
1397 SeparatorTextPadding = ImTrunc(SeparatorTextPadding * scale_factor);
1398 DockingSeparatorSize = ImTrunc(DockingSeparatorSize * scale_factor);
1399 DisplayWindowPadding = ImTrunc(DisplayWindowPadding * scale_factor);
1400 DisplaySafeAreaPadding = ImTrunc(DisplaySafeAreaPadding * scale_factor);
1401 MouseCursorScale = ImTrunc(MouseCursorScale * scale_factor);
1402}
1403
1404ImGuiIO::ImGuiIO()
1405{
1406 // Most fields are initialized with zero
1407 memset(this, 0, sizeof(*this));
1408 IM_STATIC_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT);
1409
1410 // Settings
1411 ConfigFlags = ImGuiConfigFlags_None;
1412 BackendFlags = ImGuiBackendFlags_None;
1413 DisplaySize = ImVec2(-1.0f, -1.0f);
1414 DeltaTime = 1.0f / 60.0f;
1415 IniSavingRate = 5.0f;
1416 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).
1417 LogFilename = "imgui_log.txt";
1418#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1419 for (int i = 0; i < ImGuiKey_COUNT; i++)
1420 KeyMap[i] = -1;
1421#endif
1422 UserData = NULL;
1423
1424 Fonts = NULL;
1425 FontGlobalScale = 1.0f;
1426 FontDefault = NULL;
1427 FontAllowUserScaling = false;
1428 DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1429
1430 // Keyboard/Gamepad Navigation options
1431 ConfigNavSwapGamepadButtons = false;
1432 ConfigNavMoveSetMousePos = false;
1433 ConfigNavCaptureKeyboard = true;
1434 ConfigNavEscapeClearFocusItem = true;
1435 ConfigNavEscapeClearFocusWindow = false;
1436 ConfigNavCursorVisibleAuto = true;
1437 ConfigNavCursorVisibleAlways = false;
1438
1439 // Docking options (when ImGuiConfigFlags_DockingEnable is set)
1440 ConfigDockingNoSplit = false;
1441 ConfigDockingWithShift = false;
1442 ConfigDockingAlwaysTabBar = false;
1443 ConfigDockingTransparentPayload = false;
1444
1445 // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set)
1446 ConfigViewportsNoAutoMerge = false;
1447 ConfigViewportsNoTaskBarIcon = false;
1448 ConfigViewportsNoDecoration = true;
1449 ConfigViewportsNoDefaultParent = false;
1450
1451 // Miscellaneous options
1452 MouseDrawCursor = false;
1453#ifdef __APPLE__
1454 ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
1455#else
1456 ConfigMacOSXBehaviors = false;
1457#endif
1458 ConfigInputTrickleEventQueue = true;
1459 ConfigInputTextCursorBlink = true;
1460 ConfigInputTextEnterKeepActive = false;
1461 ConfigDragClickToInputText = false;
1462 ConfigWindowsResizeFromEdges = true;
1463 ConfigWindowsMoveFromTitleBarOnly = false;
1464 ConfigScrollbarScrollByPage = true;
1465 ConfigMemoryCompactTimer = 60.0f;
1466 ConfigDebugIsDebuggerPresent = false;
1467 ConfigDebugHighlightIdConflicts = true;
1468 ConfigDebugBeginReturnValueOnce = false;
1469 ConfigDebugBeginReturnValueLoop = false;
1470
1471 ConfigErrorRecovery = true;
1472 ConfigErrorRecoveryEnableAssert = true;
1473 ConfigErrorRecoveryEnableDebugLog = true;
1474 ConfigErrorRecoveryEnableTooltip = true;
1475
1476 // Inputs Behaviors
1477 MouseDoubleClickTime = 0.30f;
1478 MouseDoubleClickMaxDist = 6.0f;
1479 MouseDragThreshold = 6.0f;
1480 KeyRepeatDelay = 0.275f;
1481 KeyRepeatRate = 0.050f;
1482
1483 // Platform Functions
1484 // Note: Initialize() will setup default clipboard/ime handlers.
1485 BackendPlatformName = BackendRendererName = NULL;
1486 BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1487
1488 // Input (NB: we already have memset zero the entire structure!)
1489 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1490 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1491 MouseSource = ImGuiMouseSource_Mouse;
1492 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1493 for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; }
1494 AppAcceptingEvents = true;
1495 BackendUsingLegacyKeyArrays = (ImS8)-1;
1496 BackendUsingLegacyNavInputArray = true; // assume using legacy array until proven wrong
1497}
1498
1499// Pass in translated ASCII characters for text input.
1500// - with glfw you can get those from the callback set in glfwSetCharCallback()
1501// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
1502// FIXME: Should in theory be called "AddCharacterEvent()" to be consistent with new API
1503void ImGuiIO::AddInputCharacter(unsigned int c)
1504{
1505 IM_ASSERT(Ctx != NULL);
1506 ImGuiContext& g = *Ctx;
1507 if (c == 0 || !AppAcceptingEvents)
1508 return;
1509
1510 ImGuiInputEvent e;
1511 e.Type = ImGuiInputEventType_Text;
1512 e.Source = ImGuiInputSource_Keyboard;
1513 e.EventId = g.InputEventsNextEventId++;
1514 e.Text.Char = c;
1515 g.InputEventsQueue.push_back(e);
1516}
1517
1518// UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
1519// we should save the high surrogate.
1520void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
1521{
1522 if ((c == 0 && InputQueueSurrogate == 0) || !AppAcceptingEvents)
1523 return;
1524
1525 if ((c & 0xFC00) == 0xD800) // High surrogate, must save
1526 {
1527 if (InputQueueSurrogate != 0)
1528 AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);
1529 InputQueueSurrogate = c;
1530 return;
1531 }
1532
1533 ImWchar cp = c;
1534 if (InputQueueSurrogate != 0)
1535 {
1536 if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
1537 {
1538 AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);
1539 }
1540 else
1541 {
1542#if IM_UNICODE_CODEPOINT_MAX == 0xFFFF
1543 cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar
1544#else
1545 cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
1546#endif
1547 }
1548
1549 InputQueueSurrogate = 0;
1550 }
1551 AddInputCharacter((unsigned)cp);
1552}
1553
1554void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1555{
1556 if (!AppAcceptingEvents)
1557 return;
1558 while (*utf8_chars != 0)
1559 {
1560 unsigned int c = 0;
1561 utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1562 AddInputCharacter(c);
1563 }
1564}
1565
1566// Clear all incoming events.
1567void ImGuiIO::ClearEventsQueue()
1568{
1569 IM_ASSERT(Ctx != NULL);
1570 ImGuiContext& g = *Ctx;
1571 g.InputEventsQueue.clear();
1572}
1573
1574// Clear current keyboard/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.
1575void ImGuiIO::ClearInputKeys()
1576{
1577#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1578 memset(KeysDown, 0, sizeof(KeysDown));
1579#endif
1580 for (int n = 0; n < IM_ARRAYSIZE(KeysData); n++)
1581 {
1582 if (ImGui::IsMouseKey((ImGuiKey)(n + ImGuiKey_KeysData_OFFSET)))
1583 continue;
1584 KeysData[n].Down = false;
1585 KeysData[n].DownDuration = -1.0f;
1586 KeysData[n].DownDurationPrev = -1.0f;
1587 }
1588 KeyCtrl = KeyShift = KeyAlt = KeySuper = false;
1589 KeyMods = ImGuiMod_None;
1590 InputQueueCharacters.resize(0); // Behavior of old ClearInputCharacters().
1591}
1592
1593void ImGuiIO::ClearInputMouse()
1594{
1595 for (ImGuiKey key = ImGuiKey_Mouse_BEGIN; key < ImGuiKey_Mouse_END; key = (ImGuiKey)(key + 1))
1596 {
1597 ImGuiKeyData* key_data = &KeysData[key - ImGuiKey_KeysData_OFFSET];
1598 key_data->Down = false;
1599 key_data->DownDuration = -1.0f;
1600 key_data->DownDurationPrev = -1.0f;
1601 }
1602 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1603 for (int n = 0; n < IM_ARRAYSIZE(MouseDown); n++)
1604 {
1605 MouseDown[n] = false;
1606 MouseDownDuration[n] = MouseDownDurationPrev[n] = -1.0f;
1607 }
1608 MouseWheel = MouseWheelH = 0.0f;
1609}
1610
1611// Removed this as it is ambiguous/misleading and generally incorrect to use with the existence of a higher-level input queue.
1612// Current frame character buffer is now also cleared by ClearInputKeys().
1613#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1614void ImGuiIO::ClearInputCharacters()
1615{
1616 InputQueueCharacters.resize(0);
1617}
1618#endif
1619
1620static ImGuiInputEvent* FindLatestInputEvent(ImGuiContext* ctx, ImGuiInputEventType type, int arg = -1)
1621{
1622 ImGuiContext& g = *ctx;
1623 for (int n = g.InputEventsQueue.Size - 1; n >= 0; n--)
1624 {
1625 ImGuiInputEvent* e = &g.InputEventsQueue[n];
1626 if (e->Type != type)
1627 continue;
1628 if (type == ImGuiInputEventType_Key && e->Key.Key != arg)
1629 continue;
1630 if (type == ImGuiInputEventType_MouseButton && e->MouseButton.Button != arg)
1631 continue;
1632 return e;
1633 }
1634 return NULL;
1635}
1636
1637// Queue a new key down/up event.
1638// - ImGuiKey key: Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character)
1639// - bool down: Is the key down? use false to signify a key release.
1640// - float analog_value: 0.0f..1.0f
1641// IMPORTANT: THIS FUNCTION AND OTHER "ADD" GRABS THE CONTEXT FROM OUR INSTANCE.
1642// WE NEED TO ENSURE THAT ALL FUNCTION CALLS ARE FULFILLING THIS, WHICH IS WHY GetKeyData() HAS AN EXPLICIT CONTEXT.
1643void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value)
1644{
1645 //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); }
1646 IM_ASSERT(Ctx != NULL);
1647 if (key == ImGuiKey_None || !AppAcceptingEvents)
1648 return;
1649 ImGuiContext& g = *Ctx;
1650 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.
1651 IM_ASSERT(ImGui::IsAliasKey(key) == false); // Backend cannot submit ImGuiKey_MouseXXX values they are automatically inferred from AddMouseXXX() events.
1652
1653 // MacOS: swap Cmd(Super) and Ctrl
1654 if (g.IO.ConfigMacOSXBehaviors)
1655 {
1656 if (key == ImGuiMod_Super) { key = ImGuiMod_Ctrl; }
1657 else if (key == ImGuiMod_Ctrl) { key = ImGuiMod_Super; }
1658 else if (key == ImGuiKey_LeftSuper) { key = ImGuiKey_LeftCtrl; }
1659 else if (key == ImGuiKey_RightSuper){ key = ImGuiKey_RightCtrl; }
1660 else if (key == ImGuiKey_LeftCtrl) { key = ImGuiKey_LeftSuper; }
1661 else if (key == ImGuiKey_RightCtrl) { key = ImGuiKey_RightSuper; }
1662 }
1663
1664 // Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data.
1665#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1666 IM_ASSERT((BackendUsingLegacyKeyArrays == -1 || BackendUsingLegacyKeyArrays == 0) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
1667 if (BackendUsingLegacyKeyArrays == -1)
1668 for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++)
1669 IM_ASSERT(KeyMap[n] == -1 && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
1670 BackendUsingLegacyKeyArrays = 0;
1671#endif
1672 if (ImGui::IsGamepadKey(key))
1673 BackendUsingLegacyNavInputArray = false;
1674
1675 // Filter duplicate (in particular: key mods and gamepad analog values are commonly spammed)
1676 const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)key);
1677 const ImGuiKeyData* key_data = ImGui::GetKeyData(&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(e);
1692}
1693
1694void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down)
1695{
1696 if (!AppAcceptingEvents)
1697 return;
1698 AddKeyAnalogEvent(key, down, 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(native_keycode); // Yet unused
1711 IM_UNUSED(native_scancode); // Yet unused
1712
1713 // Build native->imgui map so old user code can still call key functions with native 0..511 values.
1714#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1715 const int legacy_key = (native_legacy_index != -1) ? native_legacy_index : native_keycode;
1716 if (!ImGui::IsLegacyKey((ImGuiKey)legacy_key))
1717 return;
1718 KeyMap[legacy_key] = key;
1719 KeyMap[key] = legacy_key;
1720#else
1721 IM_UNUSED(key);
1722 IM_UNUSED(native_legacy_index);
1723#endif
1724}
1725
1726// 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.
1727void ImGuiIO::SetAppAcceptingEvents(bool accepting_events)
1728{
1729 AppAcceptingEvents = accepting_events;
1730}
1731
1732// Queue a mouse move event
1733void ImGuiIO::AddMousePosEvent(float x, float y)
1734{
1735 IM_ASSERT(Ctx != NULL);
1736 ImGuiContext& g = *Ctx;
1737 if (!AppAcceptingEvents)
1738 return;
1739
1740 // Apply same flooring as UpdateMouseInputs()
1741 ImVec2 pos((x > -FLT_MAX) ? ImFloor(x) : x, (y > -FLT_MAX) ? ImFloor(y) : y);
1742
1743 // Filter duplicate
1744 const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MousePos);
1745 const ImVec2 latest_pos = latest_event ? ImVec2(latest_event->MousePos.PosX, latest_event->MousePos.PosY) : g.IO.MousePos;
1746 if (latest_pos.x == pos.x && latest_pos.y == pos.y)
1747 return;
1748
1749 ImGuiInputEvent e;
1750 e.Type = ImGuiInputEventType_MousePos;
1751 e.Source = ImGuiInputSource_Mouse;
1752 e.EventId = g.InputEventsNextEventId++;
1753 e.MousePos.PosX = pos.x;
1754 e.MousePos.PosY = pos.y;
1755 e.MousePos.MouseSource = g.InputEventsNextMouseSource;
1756 g.InputEventsQueue.push_back(e);
1757}
1758
1759void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down)
1760{
1761 IM_ASSERT(Ctx != NULL);
1762 ImGuiContext& g = *Ctx;
1763 IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT);
1764 if (!AppAcceptingEvents)
1765 return;
1766
1767 // On MacOS X: Convert Ctrl(Super)+Left click into Right-click: handle held button.
1768 if (ConfigMacOSXBehaviors && mouse_button == 0 && MouseCtrlLeftAsRightClick)
1769 {
1770 // Order of both statements matterns: this event will still release mouse button 1
1771 mouse_button = 1;
1772 if (!down)
1773 MouseCtrlLeftAsRightClick = false;
1774 }
1775
1776 // Filter duplicate
1777 const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseButton, (int)mouse_button);
1778 const bool latest_button_down = latest_event ? latest_event->MouseButton.Down : g.IO.MouseDown[mouse_button];
1779 if (latest_button_down == down)
1780 return;
1781
1782 // On MacOS X: Convert Ctrl(Super)+Left click into Right-click.
1783 // - Note that this is actual physical Ctrl which is ImGuiMod_Super for us.
1784 // - At this point we want from !down to down, so this is handling the initial press.
1785 if (ConfigMacOSXBehaviors && mouse_button == 0 && down)
1786 {
1787 const ImGuiInputEvent* latest_super_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)ImGuiMod_Super);
1788 if (latest_super_event ? latest_super_event->Key.Down : g.IO.KeySuper)
1789 {
1790 IMGUI_DEBUG_LOG_IO("[io] Super+Left Click aliased into Right Click\n");
1791 MouseCtrlLeftAsRightClick = true;
1792 AddMouseButtonEvent(1, true); // This is just quicker to write that passing through, as we need to filter duplicate again.
1793 return;
1794 }
1795 }
1796
1797 ImGuiInputEvent e;
1798 e.Type = ImGuiInputEventType_MouseButton;
1799 e.Source = ImGuiInputSource_Mouse;
1800 e.EventId = g.InputEventsNextEventId++;
1801 e.MouseButton.Button = mouse_button;
1802 e.MouseButton.Down = down;
1803 e.MouseButton.MouseSource = g.InputEventsNextMouseSource;
1804 g.InputEventsQueue.push_back(e);
1805}
1806
1807// Queue a mouse wheel event (some mouse/API may only have a Y component)
1808void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y)
1809{
1810 IM_ASSERT(Ctx != NULL);
1811 ImGuiContext& g = *Ctx;
1812
1813 // Filter duplicate (unlike most events, wheel values are relative and easy to filter)
1814 if (!AppAcceptingEvents || (wheel_x == 0.0f && wheel_y == 0.0f))
1815 return;
1816
1817 ImGuiInputEvent e;
1818 e.Type = ImGuiInputEventType_MouseWheel;
1819 e.Source = ImGuiInputSource_Mouse;
1820 e.EventId = g.InputEventsNextEventId++;
1821 e.MouseWheel.WheelX = wheel_x;
1822 e.MouseWheel.WheelY = wheel_y;
1823 e.MouseWheel.MouseSource = g.InputEventsNextMouseSource;
1824 g.InputEventsQueue.push_back(e);
1825}
1826
1827// This is not a real event, the data is latched in order to be stored in actual Mouse events.
1828// This is so that duplicate events (e.g. Windows sending extraneous WM_MOUSEMOVE) gets filtered and are not leading to actual source changes.
1829void ImGuiIO::AddMouseSourceEvent(ImGuiMouseSource source)
1830{
1831 IM_ASSERT(Ctx != NULL);
1832 ImGuiContext& g = *Ctx;
1833 g.InputEventsNextMouseSource = source;
1834}
1835
1836void ImGuiIO::AddMouseViewportEvent(ImGuiID viewport_id)
1837{
1838 IM_ASSERT(Ctx != NULL);
1839 ImGuiContext& g = *Ctx;
1840 //IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport);
1841 if (!AppAcceptingEvents)
1842 return;
1843
1844 // Filter duplicate
1845 const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseViewport);
1846 const ImGuiID latest_viewport_id = latest_event ? latest_event->MouseViewport.HoveredViewportID : g.IO.MouseHoveredViewport;
1847 if (latest_viewport_id == viewport_id)
1848 return;
1849
1850 ImGuiInputEvent e;
1851 e.Type = ImGuiInputEventType_MouseViewport;
1852 e.Source = ImGuiInputSource_Mouse;
1853 e.MouseViewport.HoveredViewportID = viewport_id;
1854 g.InputEventsQueue.push_back(e);
1855}
1856
1857void ImGuiIO::AddFocusEvent(bool focused)
1858{
1859 IM_ASSERT(Ctx != NULL);
1860 ImGuiContext& g = *Ctx;
1861
1862 // Filter duplicate
1863 const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Focus);
1864 const bool latest_focused = latest_event ? latest_event->AppFocused.Focused : !g.IO.AppFocusLost;
1865 if (latest_focused == focused || (ConfigDebugIgnoreFocusLoss && !focused))
1866 return;
1867
1868 ImGuiInputEvent e;
1869 e.Type = ImGuiInputEventType_Focus;
1870 e.EventId = g.InputEventsNextEventId++;
1871 e.AppFocused.Focused = focused;
1872 g.InputEventsQueue.push_back(e);
1873}
1874
1875ImGuiPlatformIO::ImGuiPlatformIO()
1876{
1877 // Most fields are initialized with zero
1878 memset(this, 0, sizeof(*this));
1879 Platform_LocaleDecimalPoint = '.';
1880}
1881
1882//-----------------------------------------------------------------------------
1883// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1884//-----------------------------------------------------------------------------
1885
1886ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1887{
1888 IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau()
1889 ImVec2 p_last = p1;
1890 ImVec2 p_closest;
1891 float p_closest_dist2 = FLT_MAX;
1892 float t_step = 1.0f / (float)num_segments;
1893 for (int i_step = 1; i_step <= num_segments; i_step++)
1894 {
1895 ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step);
1896 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1897 float dist2 = ImLengthSqr(p - p_line);
1898 if (dist2 < p_closest_dist2)
1899 {
1900 p_closest = p_line;
1901 p_closest_dist2 = dist2;
1902 }
1903 p_last = p_current;
1904 }
1905 return p_closest;
1906}
1907
1908// Closely mimics PathBezierToCasteljau() in imgui_draw.cpp
1909static 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)
1910{
1911 float dx = x4 - x1;
1912 float dy = y4 - y1;
1913 float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
1914 float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
1915 d2 = (d2 >= 0) ? d2 : -d2;
1916 d3 = (d3 >= 0) ? d3 : -d3;
1917 if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
1918 {
1919 ImVec2 p_current(x4, y4);
1920 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1921 float dist2 = ImLengthSqr(p - p_line);
1922 if (dist2 < p_closest_dist2)
1923 {
1924 p_closest = p_line;
1925 p_closest_dist2 = dist2;
1926 }
1927 p_last = p_current;
1928 }
1929 else if (level < 10)
1930 {
1931 float x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f;
1932 float x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f;
1933 float x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f;
1934 float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f;
1935 float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f;
1936 float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
1937 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
1938 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
1939 }
1940}
1941
1942// tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
1943// Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.
1944ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
1945{
1946 IM_ASSERT(tess_tol > 0.0f);
1947 ImVec2 p_last = p1;
1948 ImVec2 p_closest;
1949 float p_closest_dist2 = FLT_MAX;
1950 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0);
1951 return p_closest;
1952}
1953
1954ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1955{
1956 ImVec2 ap = p - a;
1957 ImVec2 ab_dir = b - a;
1958 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1959 if (dot < 0.0f)
1960 return a;
1961 float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1962 if (dot > ab_len_sqr)
1963 return b;
1964 return a + ab_dir * dot / ab_len_sqr;
1965}
1966
1967bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1968{
1969 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1970 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1971 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1972 return ((b1 == b2) && (b2 == b3));
1973}
1974
1975void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1976{
1977 ImVec2 v0 = b - a;
1978 ImVec2 v1 = c - a;
1979 ImVec2 v2 = p - a;
1980 const float denom = v0.x * v1.y - v1.x * v0.y;
1981 out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1982 out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1983 out_u = 1.0f - out_v - out_w;
1984}
1985
1986ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1987{
1988 ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1989 ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1990 ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1991 float dist2_ab = ImLengthSqr(p - proj_ab);
1992 float dist2_bc = ImLengthSqr(p - proj_bc);
1993 float dist2_ca = ImLengthSqr(p - proj_ca);
1994 float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1995 if (m == dist2_ab)
1996 return proj_ab;
1997 if (m == dist2_bc)
1998 return proj_bc;
1999 return proj_ca;
2000}
2001
2002//-----------------------------------------------------------------------------
2003// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
2004//-----------------------------------------------------------------------------
2005
2006// Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.
2007int ImStricmp(const char* str1, const char* str2)
2008{
2009 int d;
2010 while ((d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; }
2011 return d;
2012}
2013
2014int ImStrnicmp(const char* str1, const char* str2, size_t count)
2015{
2016 int d = 0;
2017 while (count > 0 && (d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
2018 return d;
2019}
2020
2021void ImStrncpy(char* dst, const char* src, size_t count)
2022{
2023 if (count < 1)
2024 return;
2025 if (count > 1)
2026 strncpy(dst, src, count - 1);
2027 dst[count - 1] = 0;
2028}
2029
2030char* ImStrdup(const char* str)
2031{
2032 size_t len = strlen(str);
2033 void* buf = IM_ALLOC(len + 1);
2034 return (char*)memcpy(buf, (const void*)str, len + 1);
2035}
2036
2037char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
2038{
2039 size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
2040 size_t src_size = strlen(src) + 1;
2041 if (dst_buf_size < src_size)
2042 {
2043 IM_FREE(dst);
2044 dst = (char*)IM_ALLOC(src_size);
2045 if (p_dst_size)
2046 *p_dst_size = src_size;
2047 }
2048 return (char*)memcpy(dst, (const void*)src, src_size);
2049}
2050
2051const char* ImStrchrRange(const char* str, const char* str_end, char c)
2052{
2053 const char* p = (const char*)memchr(str, (int)c, str_end - str);
2054 return p;
2055}
2056
2057int ImStrlenW(const ImWchar* str)
2058{
2059 //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bit
2060 int n = 0;
2061 while (*str++) n++;
2062 return n;
2063}
2064
2065// Find end-of-line. Return pointer will point to either first \n, either str_end.
2066const char* ImStreolRange(const char* str, const char* str_end)
2067{
2068 const char* p = (const char*)memchr(str, '\n', str_end - str);
2069 return p ? p : str_end;
2070}
2071
2072const char* ImStrbol(const char* buf_mid_line, const char* buf_begin) // find beginning-of-line
2073{
2074 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
2075 buf_mid_line--;
2076 return buf_mid_line;
2077}
2078
2079const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
2080{
2081 if (!needle_end)
2082 needle_end = needle + strlen(needle);
2083
2084 const char un0 = (char)ImToUpper(*needle);
2085 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
2086 {
2087 if (ImToUpper(*haystack) == un0)
2088 {
2089 const char* b = needle + 1;
2090 for (const char* a = haystack + 1; b < needle_end; a++, b++)
2091 if (ImToUpper(*a) != ImToUpper(*b))
2092 break;
2093 if (b == needle_end)
2094 return haystack;
2095 }
2096 haystack++;
2097 }
2098 return NULL;
2099}
2100
2101// 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.
2102void ImStrTrimBlanks(char* buf)
2103{
2104 char* p = buf;
2105 while (p[0] == ' ' || p[0] == '\t') // Leading blanks
2106 p++;
2107 char* p_start = p;
2108 while (*p != 0) // Find end of string
2109 p++;
2110 while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks
2111 p--;
2112 if (p_start != buf) // Copy memory if we had leading blanks
2113 memmove(buf, p_start, p - p_start);
2114 buf[p - p_start] = 0; // Zero terminate
2115}
2116
2117const char* ImStrSkipBlank(const char* str)
2118{
2119 while (str[0] == ' ' || str[0] == '\t')
2120 str++;
2121 return str;
2122}
2123
2124// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
2125// 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.
2126// B) When buf==NULL vsnprintf() will return the output size.
2127#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2128
2129// We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
2130// You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2131// and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
2132// designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
2133#ifdef IMGUI_USE_STB_SPRINTF
2134#ifndef IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION
2135#define STB_SPRINTF_IMPLEMENTATION
2136#endif
2137#ifdef IMGUI_STB_SPRINTF_FILENAME
2138#include IMGUI_STB_SPRINTF_FILENAME
2139#else
2140#include "stb_sprintf.h"
2141#endif
2142#endif // #ifdef IMGUI_USE_STB_SPRINTF
2143
2144#if defined(_MSC_VER) && !defined(vsnprintf)
2145#define vsnprintf _vsnprintf
2146#endif
2147
2148int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
2149{
2150 va_list args;
2151 va_start(args, fmt);
2152#ifdef IMGUI_USE_STB_SPRINTF
2153 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
2154#else
2155 int w = vsnprintf(buf, buf_size, fmt, args);
2156#endif
2157 va_end(args);
2158 if (buf == NULL)
2159 return w;
2160 if (w == -1 || w >= (int)buf_size)
2161 w = (int)buf_size - 1;
2162 buf[w] = 0;
2163 return w;
2164}
2165
2166int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
2167{
2168#ifdef IMGUI_USE_STB_SPRINTF
2169 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
2170#else
2171 int w = vsnprintf(buf, buf_size, fmt, args);
2172#endif
2173 if (buf == NULL)
2174 return w;
2175 if (w == -1 || w >= (int)buf_size)
2176 w = (int)buf_size - 1;
2177 buf[w] = 0;
2178 return w;
2179}
2180#endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2181
2182void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...)
2183{
2184 va_list args;
2185 va_start(args, fmt);
2186 ImFormatStringToTempBufferV(out_buf, out_buf_end, fmt, args);
2187 va_end(args);
2188}
2189
2190// FIXME: Should rework API toward allowing multiple in-flight temp buffers (easier and safer for caller)
2191// by making the caller acquire a temp buffer token, with either explicit or destructor release, e.g.
2192// ImGuiTempBufferToken token;
2193// ImFormatStringToTempBuffer(token, ...);
2194void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args)
2195{
2196 ImGuiContext& g = *GImGui;
2197 if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0)
2198 {
2199 const char* buf = va_arg(args, const char*); // Skip formatting when using "%s"
2200 if (buf == NULL)
2201 buf = "(null)";
2202 *out_buf = buf;
2203 if (out_buf_end) { *out_buf_end = buf + strlen(buf); }
2204 }
2205 else if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 's' && fmt[4] == 0)
2206 {
2207 int buf_len = va_arg(args, int); // Skip formatting when using "%.*s"
2208 const char* buf = va_arg(args, const char*);
2209 if (buf == NULL)
2210 {
2211 buf = "(null)";
2212 buf_len = ImMin(buf_len, 6);
2213 }
2214 *out_buf = buf;
2215 *out_buf_end = buf + buf_len; // Disallow not passing 'out_buf_end' here. User is expected to use it.
2216 }
2217 else
2218 {
2219 int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args);
2220 *out_buf = g.TempBuffer.Data;
2221 if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; }
2222 }
2223}
2224
2225// CRC32 needs a 1KB lookup table (not cache friendly)
2226// Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
2227// - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
2228static const ImU32 GCrc32LookupTable[256] =
2229{
2230 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
2231 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
2232 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
2233 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
2234 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
2235 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
2236 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
2237 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
2238 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
2239 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
2240 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
2241 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
2242 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
2243 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
2244 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
2245 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
2246};
2247
2248// Known size hash
2249// It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
2250// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
2251ImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed)
2252{
2253 ImU32 crc = ~seed;
2254 const unsigned char* data = (const unsigned char*)data_p;
2255 const ImU32* crc32_lut = GCrc32LookupTable;
2256 while (data_size-- != 0)
2257 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
2258 return ~crc;
2259}
2260
2261// Zero-terminated string hash, with support for ### to reset back to seed value
2262// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
2263// Because this syntax is rarely used we are optimizing for the common case.
2264// - If we reach ### in the string we discard the hash so far and reset to the seed.
2265// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
2266// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
2267ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)
2268{
2269 seed = ~seed;
2270 ImU32 crc = seed;
2271 const unsigned char* data = (const unsigned char*)data_p;
2272 const ImU32* crc32_lut = GCrc32LookupTable;
2273 if (data_size != 0)
2274 {
2275 while (data_size-- != 0)
2276 {
2277 unsigned char c = *data++;
2278 if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
2279 crc = seed;
2280 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
2281 }
2282 }
2283 else
2284 {
2285 while (unsigned char c = *data++)
2286 {
2287 if (c == '#' && data[0] == '#' && data[1] == '#')
2288 crc = seed;
2289 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
2290 }
2291 }
2292 return ~crc;
2293}
2294
2295//-----------------------------------------------------------------------------
2296// [SECTION] MISC HELPERS/UTILITIES (File functions)
2297//-----------------------------------------------------------------------------
2298
2299// Default file functions
2300#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
2301
2302ImFileHandle ImFileOpen(const char* filename, const char* mode)
2303{
2304#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
2305 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
2306 // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
2307 const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
2308 const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
2309
2310 // Use stack buffer if possible, otherwise heap buffer. Sizes include zero terminator.
2311 // We don't rely on current ImGuiContext as this is implied to be a helper function which doesn't depend on it (see #7314).
2312 wchar_t local_temp_stack[FILENAME_MAX];
2313 ImVector<wchar_t> local_temp_heap;
2314 if (filename_wsize + mode_wsize > IM_ARRAYSIZE(local_temp_stack))
2315 local_temp_heap.resize(filename_wsize + mode_wsize);
2316 wchar_t* filename_wbuf = local_temp_heap.Data ? local_temp_heap.Data : local_temp_stack;
2317 wchar_t* mode_wbuf = filename_wbuf + filename_wsize;
2318 ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_wbuf, filename_wsize);
2319 ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_wbuf, mode_wsize);
2320 return ::_wfopen(filename_wbuf, mode_wbuf);
2321#else
2322 return fopen(filename, mode);
2323#endif
2324}
2325
2326// 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.
2327bool ImFileClose(ImFileHandle f) { return fclose(f) == 0; }
2328ImU64 ImFileGetSize(ImFileHandle f) { long off = 0, sz = 0; return ((off = ftell(f)) != -1 && !fseek(f, 0, SEEK_END) && (sz = ftell(f)) != -1 && !fseek(f, off, SEEK_SET)) ? (ImU64)sz : (ImU64)-1; }
2329ImU64 ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fread(data, (size_t)sz, (size_t)count, f); }
2330ImU64 ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fwrite(data, (size_t)sz, (size_t)count, f); }
2331#endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
2332
2333// Helper: Load file content into memory
2334// Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
2335// This can't really be used with "rt" because fseek size won't match read size.
2336void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
2337{
2338 IM_ASSERT(filename && mode);
2339 if (out_file_size)
2340 *out_file_size = 0;
2341
2342 ImFileHandle f;
2343 if ((f = ImFileOpen(filename, mode)) == NULL)
2344 return NULL;
2345
2346 size_t file_size = (size_t)ImFileGetSize(f);
2347 if (file_size == (size_t)-1)
2348 {
2349 ImFileClose(f);
2350 return NULL;
2351 }
2352
2353 void* file_data = IM_ALLOC(file_size + padding_bytes);
2354 if (file_data == NULL)
2355 {
2356 ImFileClose(f);
2357 return NULL;
2358 }
2359 if (ImFileRead(file_data, 1, file_size, f) != file_size)
2360 {
2361 ImFileClose(f);
2362 IM_FREE(file_data);
2363 return NULL;
2364 }
2365 if (padding_bytes > 0)
2366 memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
2367
2368 ImFileClose(f);
2369 if (out_file_size)
2370 *out_file_size = file_size;
2371
2372 return file_data;
2373}
2374
2375//-----------------------------------------------------------------------------
2376// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
2377//-----------------------------------------------------------------------------
2378
2379IM_MSVC_RUNTIME_CHECKS_OFF
2380
2381// Convert UTF-8 to 32-bit character, process single character input.
2382// A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).
2383// We handle UTF-8 decoding error by skipping forward.
2384int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
2385{
2386 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 };
2387 static const int masks[] = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 };
2388 static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 };
2389 static const int shiftc[] = { 0, 18, 12, 6, 0 };
2390 static const int shifte[] = { 0, 6, 4, 2, 0 };
2391 int len = lengths[*(const unsigned char*)in_text >> 3];
2392 int wanted = len + (len ? 0 : 1);
2393
2394 if (in_text_end == NULL)
2395 in_text_end = in_text + wanted; // Max length, nulls will be taken into account.
2396
2397 // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here,
2398 // so it is fast even with excessive branching.
2399 unsigned char s[4];
2400 s[0] = in_text + 0 < in_text_end ? in_text[0] : 0;
2401 s[1] = in_text + 1 < in_text_end ? in_text[1] : 0;
2402 s[2] = in_text + 2 < in_text_end ? in_text[2] : 0;
2403 s[3] = in_text + 3 < in_text_end ? in_text[3] : 0;
2404
2405 // Assume a four-byte character and load four bytes. Unused bits are shifted out.
2406 *out_char = (uint32_t)(s[0] & masks[len]) << 18;
2407 *out_char |= (uint32_t)(s[1] & 0x3f) << 12;
2408 *out_char |= (uint32_t)(s[2] & 0x3f) << 6;
2409 *out_char |= (uint32_t)(s[3] & 0x3f) << 0;
2410 *out_char >>= shiftc[len];
2411
2412 // Accumulate the various error conditions.
2413 int e = 0;
2414 e = (*out_char < mins[len]) << 6; // non-canonical encoding
2415 e |= ((*out_char >> 11) == 0x1b) << 7; // surrogate half?
2416 e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8; // out of range?
2417 e |= (s[1] & 0xc0) >> 2;
2418 e |= (s[2] & 0xc0) >> 4;
2419 e |= (s[3] ) >> 6;
2420 e ^= 0x2a; // top two bits of each tail byte correct?
2421 e >>= shifte[len];
2422
2423 if (e)
2424 {
2425 // No bytes are consumed when *in_text == 0 || in_text == in_text_end.
2426 // One byte is consumed in case of invalid first byte of in_text.
2427 // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes.
2428 // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s.
2429 wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]);
2430 *out_char = IM_UNICODE_CODEPOINT_INVALID;
2431 }
2432
2433 return wanted;
2434}
2435
2436int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
2437{
2438 ImWchar* buf_out = buf;
2439 ImWchar* buf_end = buf + buf_size;
2440 while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
2441 {
2442 unsigned int c;
2443 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
2444 *buf_out++ = (ImWchar)c;
2445 }
2446 *buf_out = 0;
2447 if (in_text_remaining)
2448 *in_text_remaining = in_text;
2449 return (int)(buf_out - buf);
2450}
2451
2452int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
2453{
2454 int char_count = 0;
2455 while ((!in_text_end || in_text < in_text_end) && *in_text)
2456 {
2457 unsigned int c;
2458 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
2459 char_count++;
2460 }
2461 return char_count;
2462}
2463
2464// Based on stb_to_utf8() from github.com/nothings/stb/
2465static inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c)
2466{
2467 if (c < 0x80)
2468 {
2469 buf[0] = (char)c;
2470 return 1;
2471 }
2472 if (c < 0x800)
2473 {
2474 if (buf_size < 2) return 0;
2475 buf[0] = (char)(0xc0 + (c >> 6));
2476 buf[1] = (char)(0x80 + (c & 0x3f));
2477 return 2;
2478 }
2479 if (c < 0x10000)
2480 {
2481 if (buf_size < 3) return 0;
2482 buf[0] = (char)(0xe0 + (c >> 12));
2483 buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
2484 buf[2] = (char)(0x80 + ((c ) & 0x3f));
2485 return 3;
2486 }
2487 if (c <= 0x10FFFF)
2488 {
2489 if (buf_size < 4) return 0;
2490 buf[0] = (char)(0xf0 + (c >> 18));
2491 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
2492 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
2493 buf[3] = (char)(0x80 + ((c ) & 0x3f));
2494 return 4;
2495 }
2496 // Invalid code point, the max unicode is 0x10FFFF
2497 return 0;
2498}
2499
2500const char* ImTextCharToUtf8(char out_buf[5], unsigned int c)
2501{
2502 int count = ImTextCharToUtf8_inline(out_buf, 5, c);
2503 out_buf[count] = 0;
2504 return out_buf;
2505}
2506
2507// Not optimal but we very rarely use this function.
2508int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
2509{
2510 unsigned int unused = 0;
2511 return ImTextCharFromUtf8(&unused, in_text, in_text_end);
2512}
2513
2514static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
2515{
2516 if (c < 0x80) return 1;
2517 if (c < 0x800) return 2;
2518 if (c < 0x10000) return 3;
2519 if (c <= 0x10FFFF) return 4;
2520 return 3;
2521}
2522
2523int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
2524{
2525 char* buf_p = out_buf;
2526 const char* buf_end = out_buf + out_buf_size;
2527 while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
2528 {
2529 unsigned int c = (unsigned int)(*in_text++);
2530 if (c < 0x80)
2531 *buf_p++ = (char)c;
2532 else
2533 buf_p += ImTextCharToUtf8_inline(buf_p, (int)(buf_end - buf_p - 1), c);
2534 }
2535 *buf_p = 0;
2536 return (int)(buf_p - out_buf);
2537}
2538
2539int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
2540{
2541 int bytes_count = 0;
2542 while ((!in_text_end || in_text < in_text_end) && *in_text)
2543 {
2544 unsigned int c = (unsigned int)(*in_text++);
2545 if (c < 0x80)
2546 bytes_count++;
2547 else
2548 bytes_count += ImTextCountUtf8BytesFromChar(c);
2549 }
2550 return bytes_count;
2551}
2552
2553const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr)
2554{
2555 while (in_text_curr > in_text_start)
2556 {
2557 in_text_curr--;
2558 if ((*in_text_curr & 0xC0) != 0x80)
2559 return in_text_curr;
2560 }
2561 return in_text_start;
2562}
2563
2564int ImTextCountLines(const char* in_text, const char* in_text_end)
2565{
2566 if (in_text_end == NULL)
2567 in_text_end = in_text + strlen(in_text); // FIXME-OPT: Not optimal approach, discourage use for now.
2568 int count = 0;
2569 while (in_text < in_text_end)
2570 {
2571 const char* line_end = (const char*)memchr(in_text, '\n', in_text_end - in_text);
2572 in_text = line_end ? line_end + 1 : in_text_end;
2573 count++;
2574 }
2575 return count;
2576}
2577
2578IM_MSVC_RUNTIME_CHECKS_RESTORE
2579
2580//-----------------------------------------------------------------------------
2581// [SECTION] MISC HELPERS/UTILITIES (Color functions)
2582// Note: The Convert functions are early design which are not consistent with other API.
2583//-----------------------------------------------------------------------------
2584
2585IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
2586{
2587 float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
2588 int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
2589 int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
2590 int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
2591 return IM_COL32(r, g, b, 0xFF);
2592}
2593
2594ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
2595{
2596 float s = 1.0f / 255.0f;
2597 return ImVec4(
2598 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
2599 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
2600 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
2601 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
2602}
2603
2604ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
2605{
2606 ImU32 out;
2607 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
2608 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
2609 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
2610 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
2611 return out;
2612}
2613
2614// Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
2615// Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
2616void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
2617{
2618 float K = 0.f;
2619 if (g < b)
2620 {
2621 ImSwap(g, b);
2622 K = -1.f;
2623 }
2624 if (r < g)
2625 {
2626 ImSwap(r, g);
2627 K = -2.f / 6.f - K;
2628 }
2629
2630 const float chroma = r - (g < b ? g : b);
2631 out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
2632 out_s = chroma / (r + 1e-20f);
2633 out_v = r;
2634}
2635
2636// Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
2637// also http://en.wikipedia.org/wiki/HSL_and_HSV
2638void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
2639{
2640 if (s == 0.0f)
2641 {
2642 // gray
2643 out_r = out_g = out_b = v;
2644 return;
2645 }
2646
2647 h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
2648 int i = (int)h;
2649 float f = h - (float)i;
2650 float p = v * (1.0f - s);
2651 float q = v * (1.0f - s * f);
2652 float t = v * (1.0f - s * (1.0f - f));
2653
2654 switch (i)
2655 {
2656 case 0: out_r = v; out_g = t; out_b = p; break;
2657 case 1: out_r = q; out_g = v; out_b = p; break;
2658 case 2: out_r = p; out_g = v; out_b = t; break;
2659 case 3: out_r = p; out_g = q; out_b = v; break;
2660 case 4: out_r = t; out_g = p; out_b = v; break;
2661 case 5: default: out_r = v; out_g = p; out_b = q; break;
2662 }
2663}
2664
2665//-----------------------------------------------------------------------------
2666// [SECTION] ImGuiStorage
2667// Helper: Key->value storage
2668//-----------------------------------------------------------------------------
2669
2670// std::lower_bound but without the bullshit
2671ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key)
2672{
2673 ImGuiStoragePair* in_p = in_begin;
2674 for (size_t count = (size_t)(in_end - in_p); count > 0; )
2675 {
2676 size_t count2 = count >> 1;
2677 ImGuiStoragePair* mid = in_p + count2;
2678 if (mid->key < key)
2679 {
2680 in_p = ++mid;
2681 count -= count2 + 1;
2682 }
2683 else
2684 {
2685 count = count2;
2686 }
2687 }
2688 return in_p;
2689}
2690
2691IM_MSVC_RUNTIME_CHECKS_OFF
2692static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs)
2693{
2694 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
2695 ImGuiID lhs_v = ((const ImGuiStoragePair*)lhs)->key;
2696 ImGuiID rhs_v = ((const ImGuiStoragePair*)rhs)->key;
2697 return (lhs_v > rhs_v ? +1 : lhs_v < rhs_v ? -1 : 0);
2698}
2699
2700// For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
2701void ImGuiStorage::BuildSortByKey()
2702{
2703 ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), PairComparerByID);
2704}
2705
2706int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
2707{
2708 ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
2709 if (it == Data.Data + Data.Size || it->key != key)
2710 return default_val;
2711 return it->val_i;
2712}
2713
2714bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
2715{
2716 return GetInt(key, default_val ? 1 : 0) != 0;
2717}
2718
2719float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
2720{
2721 ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
2722 if (it == Data.Data + Data.Size || it->key != key)
2723 return default_val;
2724 return it->val_f;
2725}
2726
2727void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
2728{
2729 ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
2730 if (it == Data.Data + Data.Size || it->key != key)
2731 return NULL;
2732 return it->val_p;
2733}
2734
2735// 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.
2736int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
2737{
2738 ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2739 if (it == Data.Data + Data.Size || it->key != key)
2740 it = Data.insert(it, ImGuiStoragePair(key, default_val));
2741 return &it->val_i;
2742}
2743
2744bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
2745{
2746 return (bool*)GetIntRef(key, default_val ? 1 : 0);
2747}
2748
2749float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
2750{
2751 ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2752 if (it == Data.Data + Data.Size || it->key != key)
2753 it = Data.insert(it, ImGuiStoragePair(key, default_val));
2754 return &it->val_f;
2755}
2756
2757void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
2758{
2759 ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2760 if (it == Data.Data + Data.Size || it->key != key)
2761 it = Data.insert(it, ImGuiStoragePair(key, default_val));
2762 return &it->val_p;
2763}
2764
2765// 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)
2766void ImGuiStorage::SetInt(ImGuiID key, int val)
2767{
2768 ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2769 if (it == Data.Data + Data.Size || it->key != key)
2770 Data.insert(it, ImGuiStoragePair(key, val));
2771 else
2772 it->val_i = val;
2773}
2774
2775void ImGuiStorage::SetBool(ImGuiID key, bool val)
2776{
2777 SetInt(key, val ? 1 : 0);
2778}
2779
2780void ImGuiStorage::SetFloat(ImGuiID key, float val)
2781{
2782 ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2783 if (it == Data.Data + Data.Size || it->key != key)
2784 Data.insert(it, ImGuiStoragePair(key, val));
2785 else
2786 it->val_f = val;
2787}
2788
2789void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
2790{
2791 ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2792 if (it == Data.Data + Data.Size || it->key != key)
2793 Data.insert(it, ImGuiStoragePair(key, val));
2794 else
2795 it->val_p = val;
2796}
2797
2798void ImGuiStorage::SetAllInt(int v)
2799{
2800 for (int i = 0; i < Data.Size; i++)
2801 Data[i].val_i = v;
2802}
2803IM_MSVC_RUNTIME_CHECKS_RESTORE
2804
2805//-----------------------------------------------------------------------------
2806// [SECTION] ImGuiTextFilter
2807//-----------------------------------------------------------------------------
2808
2809// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
2810ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077
2811{
2812 InputBuf[0] = 0;
2813 CountGrep = 0;
2814 if (default_filter)
2815 {
2816 ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
2817 Build();
2818 }
2819}
2820
2821bool ImGuiTextFilter::Draw(const char* label, float width)
2822{
2823 if (width != 0.0f)
2824 ImGui::SetNextItemWidth(width);
2825 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
2826 if (value_changed)
2827 Build();
2828 return value_changed;
2829}
2830
2831void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
2832{
2833 out->resize(0);
2834 const char* wb = b;
2835 const char* we = wb;
2836 while (we < e)
2837 {
2838 if (*we == separator)
2839 {
2840 out->push_back(ImGuiTextRange(wb, we));
2841 wb = we + 1;
2842 }
2843 we++;
2844 }
2845 if (wb != we)
2846 out->push_back(ImGuiTextRange(wb, we));
2847}
2848
2849void ImGuiTextFilter::Build()
2850{
2851 Filters.resize(0);
2852 ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf));
2853 input_range.split(',', &Filters);
2854
2855 CountGrep = 0;
2856 for (ImGuiTextRange& f : Filters)
2857 {
2858 while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2859 f.b++;
2860 while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2861 f.e--;
2862 if (f.empty())
2863 continue;
2864 if (f.b[0] != '-')
2865 CountGrep += 1;
2866 }
2867}
2868
2869bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2870{
2871 if (Filters.Size == 0)
2872 return true;
2873
2874 if (text == NULL)
2875 text = text_end = "";
2876
2877 for (const ImGuiTextRange& f : Filters)
2878 {
2879 if (f.b == f.e)
2880 continue;
2881 if (f.b[0] == '-')
2882 {
2883 // Subtract
2884 if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
2885 return false;
2886 }
2887 else
2888 {
2889 // Grep
2890 if (ImStristr(text, text_end, f.b, f.e) != NULL)
2891 return true;
2892 }
2893 }
2894
2895 // Implicit * grep
2896 if (CountGrep == 0)
2897 return true;
2898
2899 return false;
2900}
2901
2902//-----------------------------------------------------------------------------
2903// [SECTION] ImGuiTextBuffer, ImGuiTextIndex
2904//-----------------------------------------------------------------------------
2905
2906// On some platform vsnprintf() takes va_list by reference and modifies it.
2907// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2908#ifndef va_copy
2909#if defined(__GNUC__) || defined(__clang__)
2910#define va_copy(dest, src) __builtin_va_copy(dest, src)
2911#else
2912#define va_copy(dest, src) (dest = src)
2913#endif
2914#endif
2915
2916char ImGuiTextBuffer::EmptyString[1] = { 0 };
2917
2918void ImGuiTextBuffer::append(const char* str, const char* str_end)
2919{
2920 int len = str_end ? (int)(str_end - str) : (int)strlen(str);
2921
2922 // Add zero-terminator the first time
2923 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2924 const int needed_sz = write_off + len;
2925 if (write_off + len >= Buf.Capacity)
2926 {
2927 int new_capacity = Buf.Capacity * 2;
2928 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2929 }
2930
2931 Buf.resize(needed_sz);
2932 memcpy(&Buf[write_off - 1], str, (size_t)len);
2933 Buf[write_off - 1 + len] = 0;
2934}
2935
2936void ImGuiTextBuffer::appendf(const char* fmt, ...)
2937{
2938 va_list args;
2939 va_start(args, fmt);
2940 appendfv(fmt, args);
2941 va_end(args);
2942}
2943
2944// Helper: Text buffer for logging/accumulating text
2945void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2946{
2947 va_list args_copy;
2948 va_copy(args_copy, args);
2949
2950 int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2951 if (len <= 0)
2952 {
2953 va_end(args_copy);
2954 return;
2955 }
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(needed_sz > new_capacity ? needed_sz : new_capacity);
2964 }
2965
2966 Buf.resize(needed_sz);
2967 ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2968 va_end(args_copy);
2969}
2970
2971void ImGuiTextIndex::append(const char* base, int old_size, int new_size)
2972{
2973 IM_ASSERT(old_size >= 0 && new_size >= old_size && new_size >= EndOffset);
2974 if (old_size == new_size)
2975 return;
2976 if (EndOffset == 0 || base[EndOffset - 1] == '\n')
2977 LineOffsets.push_back(EndOffset);
2978 const char* base_end = base + new_size;
2979 for (const char* p = base + old_size; (p = (const char*)memchr(p, '\n', base_end - p)) != 0; )
2980 if (++p < base_end) // Don't push a trailing offset on last \n
2981 LineOffsets.push_back((int)(intptr_t)(p - base));
2982 EndOffset = ImMax(EndOffset, new_size);
2983}
2984
2985//-----------------------------------------------------------------------------
2986// [SECTION] ImGuiListClipper
2987//-----------------------------------------------------------------------------
2988
2989// FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.
2990// The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.
2991static bool GetSkipItemForListClipping()
2992{
2993 ImGuiContext& g = *GImGui;
2994 return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);
2995}
2996
2997static void ImGuiListClipper_SortAndFuseRanges(ImVector<ImGuiListClipperRange>& ranges, int offset = 0)
2998{
2999 if (ranges.Size - offset <= 1)
3000 return;
3001
3002 // Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries)
3003 for (int sort_end = ranges.Size - offset - 1; sort_end > 0; --sort_end)
3004 for (int i = offset; i < sort_end + offset; ++i)
3005 if (ranges[i].Min > ranges[i + 1].Min)
3006 ImSwap(ranges[i], ranges[i + 1]);
3007
3008 // Now fuse ranges together as much as possible.
3009 for (int i = 1 + offset; i < ranges.Size; i++)
3010 {
3011 IM_ASSERT(!ranges[i].PosToIndexConvert && !ranges[i - 1].PosToIndexConvert);
3012 if (ranges[i - 1].Max < ranges[i].Min)
3013 continue;
3014 ranges[i - 1].Min = ImMin(ranges[i - 1].Min, ranges[i].Min);
3015 ranges[i - 1].Max = ImMax(ranges[i - 1].Max, ranges[i].Max);
3016 ranges.erase(ranges.Data + i);
3017 i--;
3018 }
3019}
3020
3021static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_height)
3022{
3023 // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
3024 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
3025 // 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?
3026 ImGuiContext& g = *GImGui;
3027 ImGuiWindow* window = g.CurrentWindow;
3028 float off_y = pos_y - window->DC.CursorPos.y;
3029 window->DC.CursorPos.y = pos_y;
3030 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y - g.Style.ItemSpacing.y);
3031 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.
3032 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.
3033 if (ImGuiOldColumns* columns = window->DC.CurrentColumns)
3034 columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
3035 if (ImGuiTable* table = g.CurrentTable)
3036 {
3037 if (table->IsInsideRow)
3038 ImGui::TableEndRow(table);
3039 table->RowPosY2 = window->DC.CursorPos.y;
3040 const int row_increase = (int)((off_y / line_height) + 0.5f);
3041 //table->CurrentRow += row_increase; // Can't do without fixing TableEndRow()
3042 table->RowBgColorCounter += row_increase;
3043 }
3044}
3045
3046ImGuiListClipper::ImGuiListClipper()
3047{
3048 memset(this, 0, sizeof(*this));
3049}
3050
3051ImGuiListClipper::~ImGuiListClipper()
3052{
3053 End();
3054}
3055
3056void ImGuiListClipper::Begin(int items_count, float items_height)
3057{
3058 if (Ctx == NULL)
3059 Ctx = ImGui::GetCurrentContext();
3060
3061 ImGuiContext& g = *Ctx;
3062 ImGuiWindow* window = g.CurrentWindow;
3063 IMGUI_DEBUG_LOG_CLIPPER("Clipper: Begin(%d,%.2f) in '%s'\n", items_count, items_height, window->Name);
3064
3065 if (ImGuiTable* table = g.CurrentTable)
3066 if (table->IsInsideRow)
3067 ImGui::TableEndRow(table);
3068
3069 StartPosY = window->DC.CursorPos.y;
3070 ItemsHeight = items_height;
3071 ItemsCount = items_count;
3072 DisplayStart = -1;
3073 DisplayEnd = 0;
3074
3075 // Acquire temporary buffer
3076 if (++g.ClipperTempDataStacked > g.ClipperTempData.Size)
3077 g.ClipperTempData.resize(g.ClipperTempDataStacked, ImGuiListClipperData());
3078 ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
3079 data->Reset(this);
3080 data->LossynessOffset = window->DC.CursorStartPosLossyness.y;
3081 TempData = data;
3082 StartSeekOffsetY = data->LossynessOffset;
3083}
3084
3085void ImGuiListClipper::End()
3086{
3087 if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData)
3088 {
3089 // 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.
3090 ImGuiContext& g = *Ctx;
3091 IMGUI_DEBUG_LOG_CLIPPER("Clipper: End() in '%s'\n", g.CurrentWindow->Name);
3092 if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0)
3093 SeekCursorForItem(ItemsCount);
3094
3095 // Restore temporary buffer and fix back pointers which may be invalidated when nesting
3096 IM_ASSERT(data->ListClipper == this);
3097 data->StepNo = data->Ranges.Size;
3098 if (--g.ClipperTempDataStacked > 0)
3099 {
3100 data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
3101 data->ListClipper->TempData = data;
3102 }
3103 TempData = NULL;
3104 }
3105 ItemsCount = -1;
3106}
3107
3108void ImGuiListClipper::IncludeItemsByIndex(int item_begin, int item_end)
3109{
3110 ImGuiListClipperData* data = (ImGuiListClipperData*)TempData;
3111 IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet.
3112 IM_ASSERT(item_begin <= item_end);
3113 if (item_begin < item_end)
3114 data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_begin, item_end));
3115}
3116
3117// This is already called while stepping.
3118// 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.
3119void ImGuiListClipper::SeekCursorForItem(int item_n)
3120{
3121 // - Perform the add and multiply with double to allow seeking through larger ranges.
3122 // - StartPosY starts from ItemsFrozen, by adding SeekOffsetY we generally cancel that out (SeekOffsetY == LossynessOffset - ItemsFrozen * ItemsHeight).
3123 // - 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.
3124 float pos_y = (float)((double)StartPosY + StartSeekOffsetY + (double)item_n * ItemsHeight);
3125 ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, ItemsHeight);
3126}
3127
3128static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
3129{
3130 ImGuiContext& g = *clipper->Ctx;
3131 ImGuiWindow* window = g.CurrentWindow;
3132 ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData;
3133 IM_ASSERT(data != NULL && "Called ImGuiListClipper::Step() too many times, or before ImGuiListClipper::Begin() ?");
3134
3135 ImGuiTable* table = g.CurrentTable;
3136 if (table && table->IsInsideRow)
3137 ImGui::TableEndRow(table);
3138
3139 // No items
3140 if (clipper->ItemsCount == 0 || GetSkipItemForListClipping())
3141 return false;
3142
3143 // While we are in frozen row state, keep displaying items one by one, unclipped
3144 // FIXME: Could be stored as a table-agnostic state.
3145 if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows)
3146 {
3147 clipper->DisplayStart = data->ItemsFrozen;
3148 clipper->DisplayEnd = ImMin(data->ItemsFrozen + 1, clipper->ItemsCount);
3149 if (clipper->DisplayStart < clipper->DisplayEnd)
3150 data->ItemsFrozen++;
3151 return true;
3152 }
3153
3154 // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
3155 bool calc_clipping = false;
3156 if (data->StepNo == 0)
3157 {
3158 clipper->StartPosY = window->DC.CursorPos.y;
3159 if (clipper->ItemsHeight <= 0.0f)
3160 {
3161 // Submit the first item (or range) so we can measure its height (generally the first range is 0..1)
3162 data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1));
3163 clipper->DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen);
3164 clipper->DisplayEnd = ImMin(data->Ranges[0].Max, clipper->ItemsCount);
3165 data->StepNo = 1;
3166 return true;
3167 }
3168 calc_clipping = true; // If on the first step with known item height, calculate clipping.
3169 }
3170
3171 // Step 1: Let the clipper infer height from first range
3172 if (clipper->ItemsHeight <= 0.0f)
3173 {
3174 IM_ASSERT(data->StepNo == 1);
3175 if (table)
3176 IM_ASSERT(table->RowPosY1 == clipper->StartPosY && table->RowPosY2 == window->DC.CursorPos.y);
3177
3178 clipper->ItemsHeight = (window->DC.CursorPos.y - clipper->StartPosY) / (float)(clipper->DisplayEnd - clipper->DisplayStart);
3179 bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(clipper->StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y);
3180 if (affected_by_floating_point_precision)
3181 clipper->ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries.
3182 if (clipper->ItemsHeight == 0.0f && clipper->ItemsCount == INT_MAX) // Accept that no item have been submitted if in indeterminate mode.
3183 return false;
3184 IM_ASSERT(clipper->ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
3185 calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards.
3186 }
3187
3188 // Step 0 or 1: Calculate the actual ranges of visible elements.
3189 const int already_submitted = clipper->DisplayEnd;
3190 if (calc_clipping)
3191 {
3192 // Record seek offset, this is so ImGuiListClipper::Seek() can be called after ImGuiListClipperData is done
3193 clipper->StartSeekOffsetY = (double)data->LossynessOffset - data->ItemsFrozen * (double)clipper->ItemsHeight;
3194
3195 if (g.LogEnabled)
3196 {
3197 // If logging is active, do not perform any clipping
3198 data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, clipper->ItemsCount));
3199 }
3200 else
3201 {
3202 // Add range selected to be included for navigation
3203 const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
3204 if (is_nav_request)
3205 data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0));
3206 if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1)
3207 data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount));
3208
3209 // Add focused/active item
3210 ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]);
3211 if (g.NavId != 0 && window->NavLastIds[0] == g.NavId)
3212 data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0));
3213
3214 // Add visible range
3215 float min_y = window->ClipRect.Min.y;
3216 float max_y = window->ClipRect.Max.y;
3217
3218 // Add box selection range
3219 ImGuiBoxSelectState* bs = &g.BoxSelectState;
3220 if (bs->IsActive && bs->Window == window)
3221 {
3222 // FIXME: Selectable() use of half-ItemSpacing isn't consistent in matter of layout, as ItemAdd(bb) stray above ItemSize()'s CursorPos.
3223 // RangeSelect's BoxSelect relies on comparing overlap of previous and current rectangle and is sensitive to that.
3224 // As a workaround we currently half ItemSpacing worth on each side.
3225 min_y -= g.Style.ItemSpacing.y;
3226 max_y += g.Style.ItemSpacing.y;
3227
3228 // Box-select on 2D area requires different clipping.
3229 if (bs->UnclipMode)
3230 data->Ranges.push_back(ImGuiListClipperRange::FromPositions(bs->UnclipRect.Min.y, bs->UnclipRect.Max.y, 0, 0));
3231 }
3232
3233 const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0;
3234 const int off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0;
3235 data->Ranges.push_back(ImGuiListClipperRange::FromPositions(min_y, max_y, off_min, off_max));
3236 }
3237
3238 // Convert position ranges to item index ranges
3239 // - 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.
3240 // - Due to how Selectable extra padding they tend to be "unaligned" with exact unit in the item list,
3241 // which with the flooring/ceiling tend to lead to 2 items instead of one being submitted.
3242 for (ImGuiListClipperRange& range : data->Ranges)
3243 if (range.PosToIndexConvert)
3244 {
3245 int m1 = (int)(((double)range.Min - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight);
3246 int m2 = (int)((((double)range.Max - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight) + 0.999999f);
3247 range.Min = ImClamp(already_submitted + m1 + range.PosToIndexOffsetMin, already_submitted, clipper->ItemsCount - 1);
3248 range.Max = ImClamp(already_submitted + m2 + range.PosToIndexOffsetMax, range.Min + 1, clipper->ItemsCount);
3249 range.PosToIndexConvert = false;
3250 }
3251 ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo);
3252 }
3253
3254 // Step 0+ (if item height is given in advance) or 1+: Display the next range in line.
3255 while (data->StepNo < data->Ranges.Size)
3256 {
3257 clipper->DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted);
3258 clipper->DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, clipper->ItemsCount);
3259 if (clipper->DisplayStart > already_submitted) //-V1051
3260 clipper->SeekCursorForItem(clipper->DisplayStart);
3261 data->StepNo++;
3262 if (clipper->DisplayStart == clipper->DisplayEnd && data->StepNo < data->Ranges.Size)
3263 continue;
3264 return true;
3265 }
3266
3267 // After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),
3268 // Advance the cursor to the end of the list and then returns 'false' to end the loop.
3269 if (clipper->ItemsCount < INT_MAX)
3270 clipper->SeekCursorForItem(clipper->ItemsCount);
3271
3272 return false;
3273}
3274
3275bool ImGuiListClipper::Step()
3276{
3277 ImGuiContext& g = *Ctx;
3278 bool need_items_height = (ItemsHeight <= 0.0f);
3279 bool ret = ImGuiListClipper_StepInternal(this);
3280 if (ret && (DisplayStart == DisplayEnd))
3281 ret = false;
3282 if (g.CurrentTable && g.CurrentTable->IsUnfrozenRows == false)
3283 IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): inside frozen table row.\n");
3284 if (need_items_height && ItemsHeight > 0.0f)
3285 IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): computed ItemsHeight: %.2f.\n", ItemsHeight);
3286 if (ret)
3287 {
3288 IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): display %d to %d.\n", DisplayStart, DisplayEnd);
3289 }
3290 else
3291 {
3292 IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): End.\n");
3293 End();
3294 }
3295 return ret;
3296}
3297
3298//-----------------------------------------------------------------------------
3299// [SECTION] STYLING
3300//-----------------------------------------------------------------------------
3301
3302ImGuiStyle& ImGui::GetStyle()
3303{
3304 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3305 return GImGui->Style;
3306}
3307
3308ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
3309{
3310 ImGuiStyle& style = GImGui->Style;
3311 ImVec4 c = style.Colors[idx];
3312 c.w *= style.Alpha * alpha_mul;
3313 return ColorConvertFloat4ToU32(c);
3314}
3315
3316ImU32 ImGui::GetColorU32(const ImVec4& col)
3317{
3318 ImGuiStyle& style = GImGui->Style;
3319 ImVec4 c = col;
3320 c.w *= style.Alpha;
3321 return ColorConvertFloat4ToU32(c);
3322}
3323
3324const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
3325{
3326 ImGuiStyle& style = GImGui->Style;
3327 return style.Colors[idx];
3328}
3329
3330ImU32 ImGui::GetColorU32(ImU32 col, float alpha_mul)
3331{
3332 ImGuiStyle& style = GImGui->Style;
3333 alpha_mul *= style.Alpha;
3334 if (alpha_mul >= 1.0f)
3335 return col;
3336 ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
3337 a = (ImU32)(a * alpha_mul); // We don't need to clamp 0..255 because alpha is in 0..1 range.
3338 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
3339}
3340
3341// 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
3342void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
3343{
3344 ImGuiContext& g = *GImGui;
3345 ImGuiColorMod backup;
3346 backup.Col = idx;
3347 backup.BackupValue = g.Style.Colors[idx];
3348 g.ColorStack.push_back(backup);
3349 if (g.DebugFlashStyleColorIdx != idx)
3350 g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
3351}
3352
3353void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
3354{
3355 ImGuiContext& g = *GImGui;
3356 ImGuiColorMod backup;
3357 backup.Col = idx;
3358 backup.BackupValue = g.Style.Colors[idx];
3359 g.ColorStack.push_back(backup);
3360 if (g.DebugFlashStyleColorIdx != idx)
3361 g.Style.Colors[idx] = col;
3362}
3363
3364void ImGui::PopStyleColor(int count)
3365{
3366 ImGuiContext& g = *GImGui;
3367 if (g.ColorStack.Size < count)
3368 {
3369 IM_ASSERT_USER_ERROR(0, "Calling PopStyleColor() too many times!");
3370 count = g.ColorStack.Size;
3371 }
3372 while (count > 0)
3373 {
3374 ImGuiColorMod& backup = g.ColorStack.back();
3375 g.Style.Colors[backup.Col] = backup.BackupValue;
3376 g.ColorStack.pop_back();
3377 count--;
3378 }
3379}
3380
3381static const ImGuiCol GWindowDockStyleColors[ImGuiWindowDockStyleCol_COUNT] =
3382{
3383 ImGuiCol_Text, ImGuiCol_TabHovered, ImGuiCol_Tab, ImGuiCol_TabSelected, ImGuiCol_TabSelectedOverline, ImGuiCol_TabDimmed, ImGuiCol_TabDimmedSelected, ImGuiCol_TabDimmedSelectedOverline,
3384};
3385
3386static const ImGuiDataVarInfo GStyleVarInfo[] =
3387{
3388 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
3389 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha
3390 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
3391 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
3392 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
3393 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
3394 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
3395 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
3396 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
3397 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
3398 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
3399 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
3400 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
3401 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
3402 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
3403 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
3404 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
3405 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding
3406 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
3407 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
3408 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
3409 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
3410 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
3411 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBorderSize) }, // ImGuiStyleVar_TabBorderSize
3412 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) }, // ImGuiStyleVar_TabBarBorderSize
3413 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBarOverlineSize) }, // ImGuiStyleVar_TabBarOverlineSize
3414 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersAngle)}, // ImGuiStyleVar_TableAngledHeadersAngle
3415 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign
3416 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
3417 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
3418 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)}, // ImGuiStyleVar_SeparatorTextBorderSize
3419 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign
3420 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) }, // ImGuiStyleVar_SeparatorTextPadding
3421 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, DockingSeparatorSize) }, // ImGuiStyleVar_DockingSeparatorSize
3422};
3423
3424const ImGuiDataVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx)
3425{
3426 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
3427 IM_STATIC_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
3428 return &GStyleVarInfo[idx];
3429}
3430
3431void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
3432{
3433 ImGuiContext& g = *GImGui;
3434 const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);
3435 if (var_info->Type != ImGuiDataType_Float || var_info->Count != 1)
3436 {
3437 IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3438 return;
3439 }
3440 float* pvar = (float*)var_info->GetVarPtr(&g.Style);
3441 g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3442 *pvar = val;
3443}
3444
3445void ImGui::PushStyleVarX(ImGuiStyleVar idx, float val_x)
3446{
3447 ImGuiContext& g = *GImGui;
3448 const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);
3449 if (var_info->Type != ImGuiDataType_Float || var_info->Count != 2)
3450 {
3451 IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3452 return;
3453 }
3454 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
3455 g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3456 pvar->x = val_x;
3457}
3458
3459void ImGui::PushStyleVarY(ImGuiStyleVar idx, float val_y)
3460{
3461 ImGuiContext& g = *GImGui;
3462 const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);
3463 if (var_info->Type != ImGuiDataType_Float || var_info->Count != 2)
3464 {
3465 IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3466 return;
3467 }
3468 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
3469 g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3470 pvar->y = val_y;
3471}
3472
3473void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
3474{
3475 ImGuiContext& g = *GImGui;
3476 const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);
3477 if (var_info->Type != ImGuiDataType_Float || var_info->Count != 2)
3478 {
3479 IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3480 return;
3481 }
3482 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
3483 g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3484 *pvar = val;
3485}
3486
3487void ImGui::PopStyleVar(int count)
3488{
3489 ImGuiContext& g = *GImGui;
3490 if (g.StyleVarStack.Size < count)
3491 {
3492 IM_ASSERT_USER_ERROR(0, "Calling PopStyleVar() too many times!");
3493 count = g.StyleVarStack.Size;
3494 }
3495 while (count > 0)
3496 {
3497 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
3498 ImGuiStyleMod& backup = g.StyleVarStack.back();
3499 const ImGuiDataVarInfo* info = GetStyleVarInfo(backup.VarIdx);
3500 void* data = info->GetVarPtr(&g.Style);
3501 if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; }
3502 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
3503 g.StyleVarStack.pop_back();
3504 count--;
3505 }
3506}
3507
3508const char* ImGui::GetStyleColorName(ImGuiCol idx)
3509{
3510 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
3511 switch (idx)
3512 {
3513 case ImGuiCol_Text: return "Text";
3514 case ImGuiCol_TextDisabled: return "TextDisabled";
3515 case ImGuiCol_WindowBg: return "WindowBg";
3516 case ImGuiCol_ChildBg: return "ChildBg";
3517 case ImGuiCol_PopupBg: return "PopupBg";
3518 case ImGuiCol_Border: return "Border";
3519 case ImGuiCol_BorderShadow: return "BorderShadow";
3520 case ImGuiCol_FrameBg: return "FrameBg";
3521 case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
3522 case ImGuiCol_FrameBgActive: return "FrameBgActive";
3523 case ImGuiCol_TitleBg: return "TitleBg";
3524 case ImGuiCol_TitleBgActive: return "TitleBgActive";
3525 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
3526 case ImGuiCol_MenuBarBg: return "MenuBarBg";
3527 case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
3528 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
3529 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
3530 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
3531 case ImGuiCol_CheckMark: return "CheckMark";
3532 case ImGuiCol_SliderGrab: return "SliderGrab";
3533 case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
3534 case ImGuiCol_Button: return "Button";
3535 case ImGuiCol_ButtonHovered: return "ButtonHovered";
3536 case ImGuiCol_ButtonActive: return "ButtonActive";
3537 case ImGuiCol_Header: return "Header";
3538 case ImGuiCol_HeaderHovered: return "HeaderHovered";
3539 case ImGuiCol_HeaderActive: return "HeaderActive";
3540 case ImGuiCol_Separator: return "Separator";
3541 case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
3542 case ImGuiCol_SeparatorActive: return "SeparatorActive";
3543 case ImGuiCol_ResizeGrip: return "ResizeGrip";
3544 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
3545 case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
3546 case ImGuiCol_TabHovered: return "TabHovered";
3547 case ImGuiCol_Tab: return "Tab";
3548 case ImGuiCol_TabSelected: return "TabSelected";
3549 case ImGuiCol_TabSelectedOverline: return "TabSelectedOverline";
3550 case ImGuiCol_TabDimmed: return "TabDimmed";
3551 case ImGuiCol_TabDimmedSelected: return "TabDimmedSelected";
3552 case ImGuiCol_TabDimmedSelectedOverline: return "TabDimmedSelectedOverline";
3553 case ImGuiCol_DockingPreview: return "DockingPreview";
3554 case ImGuiCol_DockingEmptyBg: return "DockingEmptyBg";
3555 case ImGuiCol_PlotLines: return "PlotLines";
3556 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
3557 case ImGuiCol_PlotHistogram: return "PlotHistogram";
3558 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
3559 case ImGuiCol_TableHeaderBg: return "TableHeaderBg";
3560 case ImGuiCol_TableBorderStrong: return "TableBorderStrong";
3561 case ImGuiCol_TableBorderLight: return "TableBorderLight";
3562 case ImGuiCol_TableRowBg: return "TableRowBg";
3563 case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt";
3564 case ImGuiCol_TextLink: return "TextLink";
3565 case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
3566 case ImGuiCol_DragDropTarget: return "DragDropTarget";
3567 case ImGuiCol_NavCursor: return "NavCursor";
3568 case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
3569 case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
3570 case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
3571 }
3572 IM_ASSERT(0);
3573 return "Unknown";
3574}
3575
3576
3577//-----------------------------------------------------------------------------
3578// [SECTION] RENDER HELPERS
3579// Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
3580// we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
3581// Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
3582//-----------------------------------------------------------------------------
3583
3584const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
3585{
3586 const char* text_display_end = text;
3587 if (!text_end)
3588 text_end = (const char*)-1;
3589
3590 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
3591 text_display_end++;
3592 return text_display_end;
3593}
3594
3595// Internal ImGui functions to render text
3596// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
3597void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
3598{
3599 ImGuiContext& g = *GImGui;
3600 ImGuiWindow* window = g.CurrentWindow;
3601
3602 // Hide anything after a '##' string
3603 const char* text_display_end;
3604 if (hide_text_after_hash)
3605 {
3606 text_display_end = FindRenderedTextEnd(text, text_end);
3607 }
3608 else
3609 {
3610 if (!text_end)
3611 text_end = text + strlen(text); // FIXME-OPT
3612 text_display_end = text_end;
3613 }
3614
3615 if (text != text_display_end)
3616 {
3617 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
3618 if (g.LogEnabled)
3619 LogRenderedText(&pos, text, text_display_end);
3620 }
3621}
3622
3623void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
3624{
3625 ImGuiContext& g = *GImGui;
3626 ImGuiWindow* window = g.CurrentWindow;
3627
3628 if (!text_end)
3629 text_end = text + strlen(text); // FIXME-OPT
3630
3631 if (text != text_end)
3632 {
3633 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
3634 if (g.LogEnabled)
3635 LogRenderedText(&pos, text, text_end);
3636 }
3637}
3638
3639// Default clip_rect uses (pos_min,pos_max)
3640// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
3641// FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especally for text above draw_list->DrawList.
3642// Effectively as this is called from widget doing their own coarse clipping it's not very valuable presently. Next time function will take
3643// better advantage of the render function taking size into account for coarse clipping.
3644void 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)
3645{
3646 // Perform CPU side clipping for single clipped element to avoid using scissor state
3647 ImVec2 pos = pos_min;
3648 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
3649
3650 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
3651 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
3652 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
3653 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
3654 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
3655
3656 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
3657 if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
3658 if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
3659
3660 // Render
3661 if (need_clipping)
3662 {
3663 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
3664 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
3665 }
3666 else
3667 {
3668 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
3669 }
3670}
3671
3672void 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)
3673{
3674 // Hide anything after a '##' string
3675 const char* text_display_end = FindRenderedTextEnd(text, text_end);
3676 const int text_len = (int)(text_display_end - text);
3677 if (text_len == 0)
3678 return;
3679
3680 ImGuiContext& g = *GImGui;
3681 ImGuiWindow* window = g.CurrentWindow;
3682 RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
3683 if (g.LogEnabled)
3684 LogRenderedText(&pos_min, text, text_display_end);
3685}
3686
3687// Another overly complex function until we reorganize everything into a nice all-in-one helper.
3688// 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.
3689// 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.
3690void 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)
3691{
3692 ImGuiContext& g = *GImGui;
3693 if (text_end_full == NULL)
3694 text_end_full = FindRenderedTextEnd(text);
3695 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
3696
3697 //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));
3698 //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));
3699 //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
3700 // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
3701 if (text_size.x > pos_max.x - pos_min.x)
3702 {
3703 // Hello wo...
3704 // | | |
3705 // min max ellipsis_max
3706 // <-> this is generally some padding value
3707
3708 const ImFont* font = draw_list->_Data->Font;
3709 const float font_size = draw_list->_Data->FontSize;
3710 const float font_scale = draw_list->_Data->FontScale;
3711 const char* text_end_ellipsis = NULL;
3712 const float ellipsis_width = font->EllipsisWidth * font_scale;
3713
3714 // We can now claim the space between pos_max.x and ellipsis_max.x
3715 const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_width) - pos_min.x, 1.0f);
3716 float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
3717 if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
3718 {
3719 // Always display at least 1 character if there's no room for character + ellipsis
3720 text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
3721 text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
3722 }
3723 while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
3724 {
3725 // 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)
3726 text_end_ellipsis--;
3727 text_size_clipped_x -= font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text_end_ellipsis, text_end_ellipsis + 1).x; // Ascii blanks are always 1 byte
3728 }
3729
3730 // Render text, render ellipsis
3731 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
3732 ImVec2 ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y));
3733 if (ellipsis_pos.x + ellipsis_width <= ellipsis_max_x)
3734 for (int i = 0; i < font->EllipsisCharCount; i++, ellipsis_pos.x += font->EllipsisCharStep * font_scale)
3735 font->RenderChar(draw_list, font_size, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar);
3736 }
3737 else
3738 {
3739 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
3740 }
3741
3742 if (g.LogEnabled)
3743 LogRenderedText(&pos_min, text, text_end_full);
3744}
3745
3746// Render a rectangle shaped with optional rounding and borders
3747void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders, float rounding)
3748{
3749 ImGuiContext& g = *GImGui;
3750 ImGuiWindow* window = g.CurrentWindow;
3751 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
3752 const float border_size = g.Style.FrameBorderSize;
3753 if (borders && border_size > 0.0f)
3754 {
3755 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
3756 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
3757 }
3758}
3759
3760void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
3761{
3762 ImGuiContext& g = *GImGui;
3763 ImGuiWindow* window = g.CurrentWindow;
3764 const float border_size = g.Style.FrameBorderSize;
3765 if (border_size > 0.0f)
3766 {
3767 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
3768 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
3769 }
3770}
3771
3772void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags)
3773{
3774 ImGuiContext& g = *GImGui;
3775 if (id != g.NavId)
3776 return;
3777 if (!g.NavCursorVisible && !(flags & ImGuiNavRenderCursorFlags_AlwaysDraw))
3778 return;
3779 if (id == g.LastItemData.ID && (g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))
3780 return;
3781 ImGuiWindow* window = g.CurrentWindow;
3782 if (window->DC.NavHideHighlightOneFrame)
3783 return;
3784
3785 float rounding = (flags & ImGuiNavRenderCursorFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
3786 ImRect display_rect = bb;
3787 display_rect.ClipWith(window->ClipRect);
3788 const float thickness = 2.0f;
3789 if (flags & ImGuiNavRenderCursorFlags_Compact)
3790 {
3791 window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness);
3792 }
3793 else
3794 {
3795 const float distance = 3.0f + thickness * 0.5f;
3796 display_rect.Expand(ImVec2(distance, distance));
3797 bool fully_visible = window->ClipRect.Contains(display_rect);
3798 if (!fully_visible)
3799 window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
3800 window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness);
3801 if (!fully_visible)
3802 window->DrawList->PopClipRect();
3803 }
3804}
3805
3806void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow)
3807{
3808 ImGuiContext& g = *GImGui;
3809 if (mouse_cursor <= ImGuiMouseCursor_None || mouse_cursor >= ImGuiMouseCursor_COUNT) // We intentionally accept out of bound values.
3810 mouse_cursor = ImGuiMouseCursor_Arrow;
3811 ImFontAtlas* font_atlas = g.DrawListSharedData.Font->ContainerAtlas;
3812 for (ImGuiViewportP* viewport : g.Viewports)
3813 {
3814 // We scale cursor with current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor.
3815 ImVec2 offset, size, uv[4];
3816 if (!font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2]))
3817 continue;
3818 const ImVec2 pos = base_pos - offset;
3819 const float scale = base_scale * viewport->DpiScale;
3820 if (!viewport->GetMainRect().Overlaps(ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale)))
3821 continue;
3822 ImDrawList* draw_list = GetForegroundDrawList(viewport);
3823 ImTextureID tex_id = font_atlas->TexID;
3824 draw_list->PushTextureID(tex_id);
3825 draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow);
3826 draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow);
3827 draw_list->AddImage(tex_id, pos, pos + size * scale, uv[2], uv[3], col_border);
3828 draw_list->AddImage(tex_id, pos, pos + size * scale, uv[0], uv[1], col_fill);
3829 draw_list->PopTextureID();
3830 }
3831}
3832
3833//-----------------------------------------------------------------------------
3834// [SECTION] INITIALIZATION, SHUTDOWN
3835//-----------------------------------------------------------------------------
3836
3837// Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3838// 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
3839ImGuiContext* ImGui::GetCurrentContext()
3840{
3841 return GImGui;
3842}
3843
3844void ImGui::SetCurrentContext(ImGuiContext* ctx)
3845{
3846#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3847 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3848#else
3849 GImGui = ctx;
3850#endif
3851}
3852
3853void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data)
3854{
3855 GImAllocatorAllocFunc = alloc_func;
3856 GImAllocatorFreeFunc = free_func;
3857 GImAllocatorUserData = user_data;
3858}
3859
3860// This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space)
3861void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data)
3862{
3863 *p_alloc_func = GImAllocatorAllocFunc;
3864 *p_free_func = GImAllocatorFreeFunc;
3865 *p_user_data = GImAllocatorUserData;
3866}
3867
3868ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3869{
3870 ImGuiContext* prev_ctx = GetCurrentContext();
3871 ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3872 SetCurrentContext(ctx);
3873 Initialize();
3874 if (prev_ctx != NULL)
3875 SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one.
3876 return ctx;
3877}
3878
3879void ImGui::DestroyContext(ImGuiContext* ctx)
3880{
3881 ImGuiContext* prev_ctx = GetCurrentContext();
3882 if (ctx == NULL) //-V1051
3883 ctx = prev_ctx;
3884 SetCurrentContext(ctx);
3885 Shutdown();
3886 SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL);
3887 IM_DELETE(ctx);
3888}
3889
3890// IMPORTANT: interactive elements requires a fixed ###xxx suffix, it must be same in ALL languages to allow for automation.
3891static const ImGuiLocEntry GLocalizationEntriesEnUS[] =
3892{
3893 { ImGuiLocKey_VersionStr, "Dear ImGui " IMGUI_VERSION " (" IM_STRINGIFY(IMGUI_VERSION_NUM) ")" },
3894 { ImGuiLocKey_TableSizeOne, "Size column to fit###SizeOne" },
3895 { ImGuiLocKey_TableSizeAllFit, "Size all columns to fit###SizeAll" },
3896 { ImGuiLocKey_TableSizeAllDefault, "Size all columns to default###SizeAll" },
3897 { ImGuiLocKey_TableResetOrder, "Reset order###ResetOrder" },
3898 { ImGuiLocKey_WindowingMainMenuBar, "(Main menu bar)" },
3899 { ImGuiLocKey_WindowingPopup, "(Popup)" },
3900 { ImGuiLocKey_WindowingUntitled, "(Untitled)" },
3901 { ImGuiLocKey_OpenLink_s, "Open '%s'" },
3902 { ImGuiLocKey_CopyLink, "Copy Link###CopyLink" },
3903 { ImGuiLocKey_DockingHideTabBar, "Hide tab bar###HideTabBar" },
3904 { ImGuiLocKey_DockingHoldShiftToDock, "Hold SHIFT to enable Docking window." },
3905 { ImGuiLocKey_DockingDragToUndockOrMoveNode,"Click and drag to move or undock whole node." },
3906};
3907
3908ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
3909{
3910 IO.Ctx = this;
3911 InputTextState.Ctx = this;
3912
3913 Initialized = false;
3914 ConfigFlagsCurrFrame = ConfigFlagsLastFrame = ImGuiConfigFlags_None;
3915 FontAtlasOwnedByContext = shared_font_atlas ? false : true;
3916 Font = NULL;
3917 FontSize = FontBaseSize = FontScale = CurrentDpiScale = 0.0f;
3918 IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();
3919 Time = 0.0f;
3920 FrameCount = 0;
3921 FrameCountEnded = FrameCountPlatformEnded = FrameCountRendered = -1;
3922 WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false;
3923 GcCompactAll = false;
3924 TestEngineHookItems = false;
3925 TestEngine = NULL;
3926 memset(ContextName, 0, sizeof(ContextName));
3927
3928 InputEventsNextMouseSource = ImGuiMouseSource_Mouse;
3929 InputEventsNextEventId = 1;
3930
3931 WindowsActiveCount = 0;
3932 CurrentWindow = NULL;
3933 HoveredWindow = NULL;
3934 HoveredWindowUnderMovingWindow = NULL;
3935 HoveredWindowBeforeClear = NULL;
3936 MovingWindow = NULL;
3937 WheelingWindow = NULL;
3938 WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1;
3939 WheelingWindowReleaseTimer = 0.0f;
3940
3941 DebugDrawIdConflicts = 0;
3942 DebugHookIdInfo = 0;
3943 HoveredId = HoveredIdPreviousFrame = 0;
3944 HoveredIdPreviousFrameItemCount = 0;
3945 HoveredIdAllowOverlap = false;
3946 HoveredIdIsDisabled = false;
3947 HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f;
3948 ItemUnclipByLog = false;
3949 ActiveId = 0;
3950 ActiveIdIsAlive = 0;
3951 ActiveIdTimer = 0.0f;
3952 ActiveIdIsJustActivated = false;
3953 ActiveIdAllowOverlap = false;
3954 ActiveIdNoClearOnFocusLoss = false;
3955 ActiveIdHasBeenPressedBefore = false;
3956 ActiveIdHasBeenEditedBefore = false;
3957 ActiveIdHasBeenEditedThisFrame = false;
3958 ActiveIdFromShortcut = false;
3959 ActiveIdClickOffset = ImVec2(-1, -1);
3960 ActiveIdWindow = NULL;
3961 ActiveIdSource = ImGuiInputSource_None;
3962 ActiveIdMouseButton = -1;
3963 ActiveIdPreviousFrame = 0;
3964 ActiveIdPreviousFrameIsAlive = false;
3965 ActiveIdPreviousFrameHasBeenEditedBefore = false;
3966 ActiveIdPreviousFrameWindow = NULL;
3967 LastActiveId = 0;
3968 LastActiveIdTimer = 0.0f;
3969
3970 LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0;
3971
3972 ActiveIdUsingNavDirMask = 0x00;
3973 ActiveIdUsingAllKeyboardKeys = false;
3974
3975 CurrentFocusScopeId = 0;
3976 CurrentItemFlags = ImGuiItemFlags_None;
3977 DebugShowGroupRects = false;
3978
3979 CurrentViewport = NULL;
3980 MouseViewport = MouseLastHoveredViewport = NULL;
3981 PlatformLastFocusedViewportId = 0;
3982 ViewportCreatedCount = PlatformWindowsCreatedCount = 0;
3983 ViewportFocusedStampCount = 0;
3984
3985 NavCursorVisible = false;
3986 NavHighlightItemUnderNav = false;
3987 NavMousePosDirty = false;
3988 NavIdIsAlive = false;
3989 NavId = 0;
3990 NavWindow = NULL;
3991 NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0;
3992 NavLayer = ImGuiNavLayer_Main;
3993 NavNextActivateId = 0;
3994 NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;
3995 NavHighlightActivatedId = 0;
3996 NavHighlightActivatedTimer = 0.0f;
3997 NavInputSource = ImGuiInputSource_Keyboard;
3998 NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
3999 NavCursorHideFrames = 0;
4000
4001 NavAnyRequest = false;
4002 NavInitRequest = false;
4003 NavInitRequestFromMove = false;
4004 NavMoveSubmitted = false;
4005 NavMoveScoringItems = false;
4006 NavMoveForwardToNextFrame = false;
4007 NavMoveFlags = ImGuiNavMoveFlags_None;
4008 NavMoveScrollFlags = ImGuiScrollFlags_None;
4009 NavMoveKeyMods = ImGuiMod_None;
4010 NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None;
4011 NavScoringDebugCount = 0;
4012 NavTabbingDir = 0;
4013 NavTabbingCounter = 0;
4014
4015 NavJustMovedFromFocusScopeId = NavJustMovedToId = NavJustMovedToFocusScopeId = 0;
4016 NavJustMovedToKeyMods = ImGuiMod_None;
4017 NavJustMovedToIsTabbing = false;
4018 NavJustMovedToHasSelectionData = false;
4019
4020 // All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac...
4021 // FIXME: Because this value is stored, it annoyingly interfere with toggling io.ConfigMacOSXBehaviors updating this..
4022 ConfigNavWindowingKeyNext = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiKey_Tab);
4023 ConfigNavWindowingKeyPrev = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab);
4024 NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL;
4025 NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;
4026 NavWindowingToggleLayer = false;
4027 NavWindowingToggleKey = ImGuiKey_None;
4028
4029 DimBgRatio = 0.0f;
4030
4031 DragDropActive = DragDropWithinSource = DragDropWithinTarget = false;
4032 DragDropSourceFlags = ImGuiDragDropFlags_None;
4033 DragDropSourceFrameCount = -1;
4034 DragDropMouseButton = -1;
4035 DragDropTargetId = 0;
4036 DragDropAcceptFlags = ImGuiDragDropFlags_None;
4037 DragDropAcceptIdCurrRectSurface = 0.0f;
4038 DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0;
4039 DragDropAcceptFrameCount = -1;
4040 DragDropHoldJustPressedId = 0;
4041 memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal));
4042
4043 ClipperTempDataStacked = 0;
4044
4045 CurrentTable = NULL;
4046 TablesTempDataStacked = 0;
4047 CurrentTabBar = NULL;
4048 CurrentMultiSelect = NULL;
4049 MultiSelectTempDataStacked = 0;
4050
4051 HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0;
4052 HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f;
4053
4054 MouseCursor = ImGuiMouseCursor_Arrow;
4055 MouseStationaryTimer = 0.0f;
4056
4057 TempInputId = 0;
4058 memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue));
4059 BeginMenuDepth = BeginComboDepth = 0;
4060 ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_;
4061 ColorEditCurrentID = ColorEditSavedID = 0;
4062 ColorEditSavedHue = ColorEditSavedSat = 0.0f;
4063 ColorEditSavedColor = 0;
4064 WindowResizeRelativeMode = false;
4065 ScrollbarSeekMode = 0;
4066 ScrollbarClickDeltaToGrabCenter = 0.0f;
4067 SliderGrabClickOffset = 0.0f;
4068 SliderCurrentAccum = 0.0f;
4069 SliderCurrentAccumDirty = false;
4070 DragCurrentAccumDirty = false;
4071 DragCurrentAccum = 0.0f;
4072 DragSpeedDefaultRatio = 1.0f / 100.0f;
4073 DisabledAlphaBackup = 0.0f;
4074 DisabledStackSize = 0;
4075 TooltipOverrideCount = 0;
4076 TooltipPreviousWindow = NULL;
4077
4078 PlatformImeData.InputPos = ImVec2(0.0f, 0.0f);
4079 PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission
4080 PlatformImeViewport = 0;
4081
4082 DockNodeWindowMenuHandler = NULL;
4083
4084 SettingsLoaded = false;
4085 SettingsDirtyTimer = 0.0f;
4086 HookIdNext = 0;
4087
4088 memset(LocalizationTable, 0, sizeof(LocalizationTable));
4089
4090 LogEnabled = false;
4091 LogType = ImGuiLogType_None;
4092 LogNextPrefix = LogNextSuffix = NULL;
4093 LogFile = NULL;
4094 LogLinePosY = FLT_MAX;
4095 LogLineFirstItem = false;
4096 LogDepthRef = 0;
4097 LogDepthToExpand = LogDepthToExpandDefault = 2;
4098
4099 ErrorCallback = NULL;
4100 ErrorCallbackUserData = NULL;
4101 ErrorFirst = true;
4102 ErrorCountCurrentFrame = 0;
4103 StackSizesInBeginForCurrentWindow = NULL;
4104
4105 DebugDrawIdConflictsCount = 0;
4106 DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY;
4107 DebugLocateId = 0;
4108 DebugLogSkippedErrors = 0;
4109 DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
4110 DebugLogAutoDisableFrames = 0;
4111 DebugLocateFrames = 0;
4112 DebugBeginReturnValueCullDepth = -1;
4113 DebugItemPickerActive = false;
4114 DebugItemPickerMouseButton = ImGuiMouseButton_Left;
4115 DebugItemPickerBreakId = 0;
4116 DebugFlashStyleColorTime = 0.0f;
4117 DebugFlashStyleColorIdx = ImGuiCol_COUNT;
4118 DebugHoveredDockNode = NULL;
4119
4120 // Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations
4121 DebugBreakInWindow = 0;
4122 DebugBreakInTable = 0;
4123 DebugBreakInLocateId = false;
4124 DebugBreakKeyChord = ImGuiKey_Pause;
4125 DebugBreakInShortcutRouting = ImGuiKey_None;
4126
4127 memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame));
4128 FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0;
4129 FramerateSecPerFrameAccum = 0.0f;
4130 WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1;
4131 memset(TempKeychordName, 0, sizeof(TempKeychordName));
4132}
4133
4134void ImGui::Initialize()
4135{
4136 ImGuiContext& g = *GImGui;
4137 IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
4138
4139 // Add .ini handle for ImGuiWindow and ImGuiTable types
4140 {
4141 ImGuiSettingsHandler ini_handler;
4142 ini_handler.TypeName = "Window";
4143 ini_handler.TypeHash = ImHashStr("Window");
4144 ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
4145 ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
4146 ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
4147 ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
4148 ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
4149 AddSettingsHandler(&ini_handler);
4150 }
4151 TableSettingsAddSettingsHandler();
4152
4153 // Setup default localization table
4154 LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_ARRAYSIZE(GLocalizationEntriesEnUS));
4155
4156 // Setup default ImGuiPlatformIO clipboard/IME handlers.
4157 g.PlatformIO.Platform_GetClipboardTextFn = Platform_GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
4158 g.PlatformIO.Platform_SetClipboardTextFn = Platform_SetClipboardTextFn_DefaultImpl;
4159 g.PlatformIO.Platform_OpenInShellFn = Platform_OpenInShellFn_DefaultImpl;
4160 g.PlatformIO.Platform_SetImeDataFn = Platform_SetImeDataFn_DefaultImpl;
4161
4162 // Create default viewport
4163 ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
4164 viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID;
4165 viewport->Idx = 0;
4166 viewport->PlatformWindowCreated = true;
4167 viewport->Flags = ImGuiViewportFlags_OwnedByApp;
4168 g.Viewports.push_back(viewport);
4169 g.TempBuffer.resize(1024 * 3 + 1, 0);
4170 g.ViewportCreatedCount++;
4171 g.PlatformIO.Viewports.push_back(g.Viewports[0]);
4172
4173 // Build KeysMayBeCharInput[] lookup table (1 bool per named key)
4174 for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
4175 if ((key >= ImGuiKey_0 && key <= ImGuiKey_9) || (key >= ImGuiKey_A && key <= ImGuiKey_Z) || (key >= ImGuiKey_Keypad0 && key <= ImGuiKey_Keypad9)
4176 || key == ImGuiKey_Tab || key == ImGuiKey_Space || key == ImGuiKey_Apostrophe || key == ImGuiKey_Comma || key == ImGuiKey_Minus || key == ImGuiKey_Period
4177 || key == ImGuiKey_Slash || key == ImGuiKey_Semicolon || key == ImGuiKey_Equal || key == ImGuiKey_LeftBracket || key == ImGuiKey_RightBracket || key == ImGuiKey_GraveAccent
4178 || key == ImGuiKey_KeypadDecimal || key == ImGuiKey_KeypadDivide || key == ImGuiKey_KeypadMultiply || key == ImGuiKey_KeypadSubtract || key == ImGuiKey_KeypadAdd || key == ImGuiKey_KeypadEqual)
4179 g.KeysMayBeCharInput.SetBit(key);
4180
4181#ifdef IMGUI_HAS_DOCK
4182 // Initialize Docking
4183 DockContextInitialize(&g);
4184#endif
4185
4186 g.Initialized = true;
4187}
4188
4189// This function is merely here to free heap allocations.
4190void ImGui::Shutdown()
4191{
4192 ImGuiContext& g = *GImGui;
4193 IM_ASSERT_USER_ERROR(g.IO.BackendPlatformUserData == NULL, "Forgot to shutdown Platform backend?");
4194 IM_ASSERT_USER_ERROR(g.IO.BackendRendererUserData == NULL, "Forgot to shutdown Renderer backend?");
4195
4196 // 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)
4197 if (g.IO.Fonts && g.FontAtlasOwnedByContext)
4198 {
4199 g.IO.Fonts->Locked = false;
4200 IM_DELETE(g.IO.Fonts);
4201 }
4202 g.IO.Fonts = NULL;
4203 g.DrawListSharedData.TempBuffer.clear();
4204
4205 // Cleanup of other data are conditional on actually having initialized Dear ImGui.
4206 if (!g.Initialized)
4207 return;
4208
4209 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
4210 if (g.SettingsLoaded && g.IO.IniFilename != NULL)
4211 SaveIniSettingsToDisk(g.IO.IniFilename);
4212
4213 // Destroy platform windows
4214 DestroyPlatformWindows();
4215
4216 // Shutdown extensions
4217 DockContextShutdown(&g);
4218
4219 CallContextHooks(&g, ImGuiContextHookType_Shutdown);
4220
4221 // Clear everything else
4222 g.Windows.clear_delete();
4223 g.WindowsFocusOrder.clear();
4224 g.WindowsTempSortBuffer.clear();
4225 g.CurrentWindow = NULL;
4226 g.CurrentWindowStack.clear();
4227 g.WindowsById.Clear();
4228 g.NavWindow = NULL;
4229 g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
4230 g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
4231 g.MovingWindow = NULL;
4232
4233 g.KeysRoutingTable.Clear();
4234
4235 g.ColorStack.clear();
4236 g.StyleVarStack.clear();
4237 g.FontStack.clear();
4238 g.OpenPopupStack.clear();
4239 g.BeginPopupStack.clear();
4240 g.TreeNodeStack.clear();
4241
4242 g.CurrentViewport = g.MouseViewport = g.MouseLastHoveredViewport = NULL;
4243 g.Viewports.clear_delete();
4244
4245 g.TabBars.Clear();
4246 g.CurrentTabBarStack.clear();
4247 g.ShrinkWidthBuffer.clear();
4248
4249 g.ClipperTempData.clear_destruct();
4250
4251 g.Tables.Clear();
4252 g.TablesTempData.clear_destruct();
4253 g.DrawChannelsTempMergeBuffer.clear();
4254
4255 g.MultiSelectStorage.Clear();
4256 g.MultiSelectTempData.clear_destruct();
4257
4258 g.ClipboardHandlerData.clear();
4259 g.MenusIdSubmittedThisFrame.clear();
4260 g.InputTextState.ClearFreeMemory();
4261 g.InputTextDeactivatedState.ClearFreeMemory();
4262
4263 g.SettingsWindows.clear();
4264 g.SettingsHandlers.clear();
4265
4266 if (g.LogFile)
4267 {
4268#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
4269 if (g.LogFile != stdout)
4270#endif
4271 ImFileClose(g.LogFile);
4272 g.LogFile = NULL;
4273 }
4274 g.LogBuffer.clear();
4275 g.DebugLogBuf.clear();
4276 g.DebugLogIndex.clear();
4277
4278 g.Initialized = false;
4279}
4280
4281// No specific ordering/dependency support, will see as needed
4282ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook)
4283{
4284 ImGuiContext& g = *ctx;
4285 IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_);
4286 g.Hooks.push_back(*hook);
4287 g.Hooks.back().HookId = ++g.HookIdNext;
4288 return g.HookIdNext;
4289}
4290
4291// Deferred removal, avoiding issue with changing vector while iterating it
4292void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id)
4293{
4294 ImGuiContext& g = *ctx;
4295 IM_ASSERT(hook_id != 0);
4296 for (ImGuiContextHook& hook : g.Hooks)
4297 if (hook.HookId == hook_id)
4298 hook.Type = ImGuiContextHookType_PendingRemoval_;
4299}
4300
4301// Call context hooks (used by e.g. test engine)
4302// We assume a small number of hooks so all stored in same array
4303void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)
4304{
4305 ImGuiContext& g = *ctx;
4306 for (ImGuiContextHook& hook : g.Hooks)
4307 if (hook.Type == hook_type)
4308 hook.Callback(&g, &hook);
4309}
4310
4311
4312//-----------------------------------------------------------------------------
4313// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
4314//-----------------------------------------------------------------------------
4315
4316// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
4317ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NULL)
4318{
4319 memset(this, 0, sizeof(*this));
4320 Ctx = ctx;
4321 Name = ImStrdup(name);
4322 NameBufLen = (int)strlen(name) + 1;
4323 ID = ImHashStr(name);
4324 IDStack.push_back(ID);
4325 ViewportAllowPlatformMonitorExtend = -1;
4326 ViewportPos = ImVec2(FLT_MAX, FLT_MAX);
4327 MoveId = GetID("#MOVE");
4328 TabId = GetID("#TAB");
4329 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
4330 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
4331 AutoFitFramesX = AutoFitFramesY = -1;
4332 AutoPosLastDirection = ImGuiDir_None;
4333 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = SetWindowDockAllowFlags = 0;
4334 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
4335 LastFrameActive = -1;
4336 LastFrameJustFocused = -1;
4337 LastTimeActive = -1.0f;
4338 FontWindowScale = FontDpiScale = 1.0f;
4339 SettingsOffset = -1;
4340 DockOrder = -1;
4341 DrawList = &DrawListInst;
4342 DrawList->_Data = &Ctx->DrawListSharedData;
4343 DrawList->_OwnerName = Name;
4344 NavPreferredScoringPosRel[0] = NavPreferredScoringPosRel[1] = ImVec2(FLT_MAX, FLT_MAX);
4345 IM_PLACEMENT_NEW(&WindowClass) ImGuiWindowClass();
4346}
4347
4348ImGuiWindow::~ImGuiWindow()
4349{
4350 IM_ASSERT(DrawList == &DrawListInst);
4351 IM_DELETE(Name);
4352 ColumnsStorage.clear_destruct();
4353}
4354
4355static void SetCurrentWindow(ImGuiWindow* window)
4356{
4357 ImGuiContext& g = *GImGui;
4358 g.CurrentWindow = window;
4359 g.StackSizesInBeginForCurrentWindow = g.CurrentWindow ? &g.CurrentWindowStack.back().StackSizesInBegin : NULL;
4360 g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
4361 if (window)
4362 {
4363 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
4364 g.FontScale = g.DrawListSharedData.FontScale = g.FontSize / g.Font->FontSize;
4365 ImGui::NavUpdateCurrentWindowIsScrollPushableX();
4366 }
4367}
4368
4369void ImGui::GcCompactTransientMiscBuffers()
4370{
4371 ImGuiContext& g = *GImGui;
4372 g.ItemFlagsStack.clear();
4373 g.GroupStack.clear();
4374 g.MultiSelectTempDataStacked = 0;
4375 g.MultiSelectTempData.clear_destruct();
4376 TableGcCompactSettings();
4377}
4378
4379// Free up/compact internal window buffers, we can use this when a window becomes unused.
4380// Not freed:
4381// - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)
4382// This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
4383void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
4384{
4385 window->MemoryCompacted = true;
4386 window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
4387 window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
4388 window->IDStack.clear();
4389 window->DrawList->_ClearFreeMemory();
4390 window->DC.ChildWindows.clear();
4391 window->DC.ItemWidthStack.clear();
4392 window->DC.TextWrapPosStack.clear();
4393}
4394
4395void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
4396{
4397 // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
4398 // The other buffers tends to amortize much faster.
4399 window->MemoryCompacted = false;
4400 window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
4401 window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
4402 window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
4403}
4404
4405void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
4406{
4407 ImGuiContext& g = *GImGui;
4408
4409 // Clear previous active id
4410 if (g.ActiveId != 0)
4411 {
4412 // While most behaved code would make an effort to not steal active id during window move/drag operations,
4413 // we at least need to be resilient to it. Canceling the move is rather aggressive and users of 'master' branch
4414 // may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that.
4415 if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId)
4416 {
4417 IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() cancel MovingWindow\n");
4418 g.MovingWindow = NULL;
4419 }
4420
4421 // This could be written in a more general way (e.g associate a hook to ActiveId),
4422 // but since this is currently quite an exception we'll leave it as is.
4423 // One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveId()
4424 if (g.InputTextState.ID == g.ActiveId)
4425 InputTextDeactivateHook(g.ActiveId);
4426 }
4427
4428 // Set active id
4429 g.ActiveIdIsJustActivated = (g.ActiveId != id);
4430 if (g.ActiveIdIsJustActivated)
4431 {
4432 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 : "");
4433 g.ActiveIdTimer = 0.0f;
4434 g.ActiveIdHasBeenPressedBefore = false;
4435 g.ActiveIdHasBeenEditedBefore = false;
4436 g.ActiveIdMouseButton = -1;
4437 if (id != 0)
4438 {
4439 g.LastActiveId = id;
4440 g.LastActiveIdTimer = 0.0f;
4441 }
4442 }
4443 g.ActiveId = id;
4444 g.ActiveIdAllowOverlap = false;
4445 g.ActiveIdNoClearOnFocusLoss = false;
4446 g.ActiveIdWindow = window;
4447 g.ActiveIdHasBeenEditedThisFrame = false;
4448 g.ActiveIdFromShortcut = false;
4449 if (id)
4450 {
4451 g.ActiveIdIsAlive = id;
4452 g.ActiveIdSource = (g.NavActivateId == id || g.NavJustMovedToId == id) ? g.NavInputSource : ImGuiInputSource_Mouse;
4453 IM_ASSERT(g.ActiveIdSource != ImGuiInputSource_None);
4454 }
4455
4456 // Clear declaration of inputs claimed by the widget
4457 // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
4458 g.ActiveIdUsingNavDirMask = 0x00;
4459 g.ActiveIdUsingAllKeyboardKeys = false;
4460}
4461
4462void ImGui::ClearActiveID()
4463{
4464 SetActiveID(0, NULL); // g.ActiveId = 0;
4465}
4466
4467void ImGui::SetHoveredID(ImGuiID id)
4468{
4469 ImGuiContext& g = *GImGui;
4470 g.HoveredId = id;
4471 g.HoveredIdAllowOverlap = false;
4472 if (id != 0 && g.HoveredIdPreviousFrame != id)
4473 g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
4474}
4475
4476ImGuiID ImGui::GetHoveredID()
4477{
4478 ImGuiContext& g = *GImGui;
4479 return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
4480}
4481
4482void ImGui::MarkItemEdited(ImGuiID id)
4483{
4484 // This marking is to be able to provide info for IsItemDeactivatedAfterEdit().
4485 // 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.
4486 ImGuiContext& g = *GImGui;
4487 if (g.LastItemData.ItemFlags & ImGuiItemFlags_NoMarkEdited)
4488 return;
4489 if (g.ActiveId == id || g.ActiveId == 0)
4490 {
4491 g.ActiveIdHasBeenEditedThisFrame = true;
4492 g.ActiveIdHasBeenEditedBefore = true;
4493 }
4494
4495 // We accept a MarkItemEdited() on drag and drop targets (see https://github.com/ocornut/imgui/issues/1875#issuecomment-978243343)
4496 // We accept 'ActiveIdPreviousFrame == id' for InputText() returning an edit after it has been taken ActiveId away (#4714)
4497 IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id || (g.CurrentMultiSelect != NULL && g.BoxSelectState.IsActive));
4498
4499 //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
4500 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
4501}
4502
4503bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
4504{
4505 // An active popup disable hovering on other windows (apart from its own children)
4506 // FIXME-OPT: This could be cached/stored within the window.
4507 ImGuiContext& g = *GImGui;
4508 if (g.NavWindow)
4509 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindowDockTree)
4510 if (focused_root_window->WasActive && focused_root_window != window->RootWindowDockTree)
4511 {
4512 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
4513 // NB: The 'else' is important because Modal windows are also Popups.
4514 bool want_inhibit = false;
4515 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
4516 want_inhibit = true;
4517 else if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
4518 want_inhibit = true;
4519
4520 // Inhibit hover unless the window is within the stack of our modal/popup
4521 if (want_inhibit)
4522 if (!IsWindowWithinBeginStackOf(window->RootWindow, focused_root_window))
4523 return false;
4524 }
4525
4526 // Filter by viewport
4527 if (window->Viewport != g.MouseViewport)
4528 if (g.MovingWindow == NULL || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree)
4529 return false;
4530
4531 return true;
4532}
4533
4534static inline float CalcDelayFromHoveredFlags(ImGuiHoveredFlags flags)
4535{
4536 ImGuiContext& g = *GImGui;
4537 if (flags & ImGuiHoveredFlags_DelayNormal)
4538 return g.Style.HoverDelayNormal;
4539 if (flags & ImGuiHoveredFlags_DelayShort)
4540 return g.Style.HoverDelayShort;
4541 return 0.0f;
4542}
4543
4544static ImGuiHoveredFlags ApplyHoverFlagsForTooltip(ImGuiHoveredFlags user_flags, ImGuiHoveredFlags shared_flags)
4545{
4546 // Allow instance flags to override shared flags
4547 if (user_flags & (ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal))
4548 shared_flags &= ~(ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal);
4549 return user_flags | shared_flags;
4550}
4551
4552// This is roughly matching the behavior of internal-facing ItemHoverable()
4553// - 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()
4554// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
4555bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
4556{
4557 ImGuiContext& g = *GImGui;
4558 ImGuiWindow* window = g.CurrentWindow;
4559 IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0, "Invalid flags for IsItemHovered()!");
4560
4561 if (g.NavHighlightItemUnderNav && g.NavCursorVisible && !(flags & ImGuiHoveredFlags_NoNavOverride))
4562 {
4563 if (!IsItemFocused())
4564 return false;
4565 if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
4566 return false;
4567
4568 if (flags & ImGuiHoveredFlags_ForTooltip)
4569 flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipNav);
4570 }
4571 else
4572 {
4573 // Test for bounding box overlap, as updated as ItemAdd()
4574 ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags;
4575 if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))
4576 return false;
4577
4578 if (flags & ImGuiHoveredFlags_ForTooltip)
4579 flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);
4580
4581 // Done with rectangle culling so we can perform heavier checks now
4582 // Test if we are hovering the right window (our window could be behind another window)
4583 // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)
4584 // [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
4585 // 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
4586 // the test that has been running for a long while.
4587 if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0)
4588 if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByWindow) == 0)
4589 return false;
4590
4591 // Test if another item is active (e.g. being dragged)
4592 const ImGuiID id = g.LastItemData.ID;
4593 if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0)
4594 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
4595 if (g.ActiveId != window->MoveId && g.ActiveId != window->TabId)
4596 return false;
4597
4598 // Test if interactions on this window are blocked by an active popup or modal.
4599 // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
4600 if (!IsWindowContentHoverable(window, flags) && !(g.LastItemData.ItemFlags & ImGuiItemFlags_NoWindowHoverableCheck))
4601 return false;
4602
4603 // Test if the item is disabled
4604 if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
4605 return false;
4606
4607 // Special handling for calling after Begin() which represent the title bar or tab.
4608 // When the window is skipped/collapsed (SkipItems==true) that last item (always ->MoveId submitted by Begin)
4609 // will never be overwritten so we need to detect the case.
4610 if (id == window->MoveId && window->WriteAccessed)
4611 return false;
4612
4613 // Test if using AllowOverlap and overlapped
4614 if ((g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap) && id != 0)
4615 if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByItem) == 0)
4616 if (g.HoveredIdPreviousFrame != g.LastItemData.ID)
4617 return false;
4618 }
4619
4620 // Handle hover delay
4621 // (some ideas: https://www.nngroup.com/articles/timing-exposing-content)
4622 const float delay = CalcDelayFromHoveredFlags(flags);
4623 if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary))
4624 {
4625 ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromPos(g.LastItemData.Rect.Min);
4626 if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id))
4627 g.HoverItemDelayTimer = 0.0f;
4628 g.HoverItemDelayId = hover_delay_id;
4629
4630 // When changing hovered item we requires a bit of stationary delay before activating hover timer,
4631 // but once unlocked on a given item we also moving.
4632 //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); }
4633 if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverItemUnlockedStationaryId != hover_delay_id)
4634 return false;
4635
4636 if (g.HoverItemDelayTimer < delay)
4637 return false;
4638 }
4639
4640 return true;
4641}
4642
4643// Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
4644// (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call)
4645// FIXME-LEGACY: the 'ImGuiItemFlags item_flags' parameter was added on 2023-06-28.
4646// If you used this in your legacy/custom widgets code:
4647// - Commonly: if your ItemHoverable() call comes after an ItemAdd() call: pass 'item_flags = g.LastItemData.ItemFlags'.
4648// - Rare: otherwise you may pass 'item_flags = 0' (ImGuiItemFlags_None) unless you want to benefit from special behavior handled by ItemHoverable.
4649bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags)
4650{
4651 ImGuiContext& g = *GImGui;
4652 ImGuiWindow* window = g.CurrentWindow;
4653
4654 // Detect ID conflicts
4655#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4656 if (id != 0 && g.HoveredIdPreviousFrame == id && (item_flags & ImGuiItemFlags_AllowDuplicateId) == 0)
4657 {
4658 g.HoveredIdPreviousFrameItemCount++;
4659 if (g.DebugDrawIdConflicts == id)
4660 window->DrawList->AddRect(bb.Min - ImVec2(1,1), bb.Max + ImVec2(1,1), IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f);
4661 }
4662#endif
4663
4664 if (g.HoveredWindow != window)
4665 return false;
4666 if (!IsMouseHoveringRect(bb.Min, bb.Max))
4667 return false;
4668
4669 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
4670 return false;
4671 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
4672 if (!g.ActiveIdFromShortcut)
4673 return false;
4674
4675 // Done with rectangle culling so we can perform heavier checks now.
4676 if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
4677 {
4678 g.HoveredIdIsDisabled = true;
4679 return false;
4680 }
4681
4682 // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
4683 // hover test in widgets code. We could also decide to split this function is two.
4684 if (id != 0)
4685 {
4686 // Drag source doesn't report as hovered
4687 if (g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover))
4688 return false;
4689
4690 SetHoveredID(id);
4691
4692 // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match.
4693 // This allows using patterns where a later submitted widget overlaps a previous one. Generally perceived as a front-to-back hit-test.
4694 if (item_flags & ImGuiItemFlags_AllowOverlap)
4695 {
4696 g.HoveredIdAllowOverlap = true;
4697 if (g.HoveredIdPreviousFrame != id)
4698 return false;
4699 }
4700
4701 // Display shortcut (only works with mouse)
4702 // (ImGuiItemStatusFlags_HasShortcut in LastItemData denotes we want a tooltip)
4703 if (id == g.LastItemData.ID && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasShortcut) && g.ActiveId != id)
4704 if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal))
4705 SetTooltip("%s", GetKeyChordName(g.LastItemData.Shortcut));
4706 }
4707
4708 // When disabled we'll return false but still set HoveredId
4709 if (item_flags & ImGuiItemFlags_Disabled)
4710 {
4711 // Release active id if turning disabled
4712 if (g.ActiveId == id && id != 0)
4713 ClearActiveID();
4714 g.HoveredIdIsDisabled = true;
4715 return false;
4716 }
4717
4718#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4719 if (id != 0)
4720 {
4721 // [DEBUG] Item Picker tool!
4722 // We perform the check here because reaching is path is rare (1~ time a frame),
4723 // making the cost of this tool near-zero! We could get better call-stack and support picking non-hovered
4724 // items if we performed the test in ItemAdd(), but that would incur a bigger runtime cost.
4725 if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
4726 GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
4727 if (g.DebugItemPickerBreakId == id)
4728 IM_DEBUG_BREAK();
4729 }
4730#endif
4731
4732 if (g.NavHighlightItemUnderNav && (item_flags & ImGuiItemFlags_NoNavDisableMouseHover) == 0)
4733 return false;
4734
4735 return true;
4736}
4737
4738// FIXME: This is inlined/duplicated in ItemAdd()
4739// FIXME: The id != 0 path is not used by our codebase, may get rid of it?
4740bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id)
4741{
4742 ImGuiContext& g = *GImGui;
4743 ImGuiWindow* window = g.CurrentWindow;
4744 if (!bb.Overlaps(window->ClipRect))
4745 if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
4746 if (!g.ItemUnclipByLog)
4747 return true;
4748 return false;
4749}
4750
4751// This is also inlined in ItemAdd()
4752// Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set g.LastItemData.DisplayRect.
4753void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags item_flags, const ImRect& item_rect)
4754{
4755 ImGuiContext& g = *GImGui;
4756 g.LastItemData.ID = item_id;
4757 g.LastItemData.ItemFlags = in_flags;
4758 g.LastItemData.StatusFlags = item_flags;
4759 g.LastItemData.Rect = g.LastItemData.NavRect = item_rect;
4760}
4761
4762float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
4763{
4764 if (wrap_pos_x < 0.0f)
4765 return 0.0f;
4766
4767 ImGuiContext& g = *GImGui;
4768 ImGuiWindow* window = g.CurrentWindow;
4769 if (wrap_pos_x == 0.0f)
4770 {
4771 // We could decide to setup a default wrapping max point for auto-resizing windows,
4772 // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
4773 //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
4774 // wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
4775 //else
4776 wrap_pos_x = window->WorkRect.Max.x;
4777 }
4778 else if (wrap_pos_x > 0.0f)
4779 {
4780 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
4781 }
4782
4783 return ImMax(wrap_pos_x - pos.x, 1.0f);
4784}
4785
4786// IM_ALLOC() == ImGui::MemAlloc()
4787void* ImGui::MemAlloc(size_t size)
4788{
4789 void* ptr = (*GImAllocatorAllocFunc)(size, GImAllocatorUserData);
4790#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4791 if (ImGuiContext* ctx = GImGui)
4792 DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, size);
4793#endif
4794 return ptr;
4795}
4796
4797// IM_FREE() == ImGui::MemFree()
4798void ImGui::MemFree(void* ptr)
4799{
4800#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4801 if (ptr != NULL)
4802 if (ImGuiContext* ctx = GImGui)
4803 DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, (size_t)-1);
4804#endif
4805 return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData);
4806}
4807
4808// 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"
4809void ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size)
4810{
4811 ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[info->LastEntriesIdx];
4812 IM_UNUSED(ptr);
4813 if (entry->FrameCount != frame_count)
4814 {
4815 info->LastEntriesIdx = (info->LastEntriesIdx + 1) % IM_ARRAYSIZE(info->LastEntriesBuf);
4816 entry = &info->LastEntriesBuf[info->LastEntriesIdx];
4817 entry->FrameCount = frame_count;
4818 entry->AllocCount = entry->FreeCount = 0;
4819 }
4820 if (size != (size_t)-1)
4821 {
4822 entry->AllocCount++;
4823 info->TotalAllocCount++;
4824 //printf("[%05d] MemAlloc(%d) -> 0x%p\n", frame_count, size, ptr);
4825 }
4826 else
4827 {
4828 entry->FreeCount++;
4829 info->TotalFreeCount++;
4830 //printf("[%05d] MemFree(0x%p)\n", frame_count, ptr);
4831 }
4832}
4833
4834const char* ImGui::GetClipboardText()
4835{
4836 ImGuiContext& g = *GImGui;
4837 return g.PlatformIO.Platform_GetClipboardTextFn ? g.PlatformIO.Platform_GetClipboardTextFn(&g) : "";
4838}
4839
4840void ImGui::SetClipboardText(const char* text)
4841{
4842 ImGuiContext& g = *GImGui;
4843 if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)
4844 g.PlatformIO.Platform_SetClipboardTextFn(&g, text);
4845}
4846
4847const char* ImGui::GetVersion()
4848{
4849 return IMGUI_VERSION;
4850}
4851
4852ImGuiIO& ImGui::GetIO()
4853{
4854 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
4855 return GImGui->IO;
4856}
4857
4858// This variant exists to facilitate backends experimenting with multi-threaded parallel context. (#8069, #6293, #5856)
4859ImGuiIO& ImGui::GetIOEx(ImGuiContext* ctx)
4860{
4861 IM_ASSERT(ctx != NULL);
4862 return ctx->IO;
4863}
4864
4865ImGuiPlatformIO& ImGui::GetPlatformIO()
4866{
4867 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext()?");
4868 return GImGui->PlatformIO;
4869}
4870
4871// Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
4872ImDrawData* ImGui::GetDrawData()
4873{
4874 ImGuiContext& g = *GImGui;
4875 ImGuiViewportP* viewport = g.Viewports[0];
4876 return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL;
4877}
4878
4879double ImGui::GetTime()
4880{
4881 return GImGui->Time;
4882}
4883
4884int ImGui::GetFrameCount()
4885{
4886 return GImGui->FrameCount;
4887}
4888
4889static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)
4890{
4891 // Create the draw list on demand, because they are not frequently used for all viewports
4892 ImGuiContext& g = *GImGui;
4893 IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->BgFgDrawLists));
4894 ImDrawList* draw_list = viewport->BgFgDrawLists[drawlist_no];
4895 if (draw_list == NULL)
4896 {
4897 draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);
4898 draw_list->_OwnerName = drawlist_name;
4899 viewport->BgFgDrawLists[drawlist_no] = draw_list;
4900 }
4901
4902 // Our ImDrawList system requires that there is always a command
4903 if (viewport->BgFgDrawListsLastFrame[drawlist_no] != g.FrameCount)
4904 {
4905 draw_list->_ResetForNewFrame();
4906 draw_list->PushTextureID(g.IO.Fonts->TexID);
4907 draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false);
4908 viewport->BgFgDrawListsLastFrame[drawlist_no] = g.FrameCount;
4909 }
4910 return draw_list;
4911}
4912
4913ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)
4914{
4915 if (viewport == NULL)
4916 viewport = GImGui->CurrentWindow->Viewport;
4917 return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 0, "##Background");
4918}
4919
4920ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
4921{
4922 if (viewport == NULL)
4923 viewport = GImGui->CurrentWindow->Viewport;
4924 return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 1, "##Foreground");
4925}
4926
4927ImDrawListSharedData* ImGui::GetDrawListSharedData()
4928{
4929 return &GImGui->DrawListSharedData;
4930}
4931
4932void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
4933{
4934 // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
4935 // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
4936 // This is because we want ActiveId to be set even when the window is not permitted to move.
4937 ImGuiContext& g = *GImGui;
4938 FocusWindow(window);
4939 SetActiveID(window->MoveId, window);
4940 if (g.IO.ConfigNavCursorVisibleAuto)
4941 g.NavCursorVisible = false;
4942 g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindowDockTree->Pos;
4943 g.ActiveIdNoClearOnFocusLoss = true;
4944 SetActiveIdUsingAllKeyboardKeys();
4945
4946 bool can_move_window = true;
4947 if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoMove))
4948 can_move_window = false;
4949 if (ImGuiDockNode* node = window->DockNodeAsHost)
4950 if (node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove))
4951 can_move_window = false;
4952 if (can_move_window)
4953 g.MovingWindow = window;
4954}
4955
4956// We use 'undock == false' when dragging from title bar to allow moving groups of floating nodes without undocking them.
4957void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock)
4958{
4959 ImGuiContext& g = *GImGui;
4960 bool can_undock_node = false;
4961 if (undock && node != NULL && node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove) == 0 && (node->MergedFlags & ImGuiDockNodeFlags_NoUndocking) == 0)
4962 {
4963 // Can undock if:
4964 // - part of a hierarchy with more than one visible node (if only one is visible, we'll just move the root window)
4965 // - part of a dockspace node hierarchy: so we can undock the last single visible node too (trivia: undocking from a fixed/central node will create a new node and copy windows)
4966 ImGuiDockNode* root_node = DockNodeGetRootNode(node);
4967 if (root_node->OnlyNodeWithWindows != node || root_node->CentralNode != NULL) // -V1051 PVS-Studio thinks node should be root_node and is wrong about that.
4968 can_undock_node = true;
4969 }
4970
4971 const bool clicked = IsMouseClicked(0);
4972 const bool dragging = IsMouseDragging(0);
4973 if (can_undock_node && dragging)
4974 DockContextQueueUndockNode(&g, node); // Will lead to DockNodeStartMouseMovingWindow() -> StartMouseMovingWindow() being called next frame
4975 else if (!can_undock_node && (clicked || dragging) && g.MovingWindow != window)
4976 StartMouseMovingWindow(window);
4977}
4978
4979// Handle mouse moving window
4980// Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
4981// FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId.
4982// This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
4983// but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
4984void ImGui::UpdateMouseMovingWindowNewFrame()
4985{
4986 ImGuiContext& g = *GImGui;
4987 if (g.MovingWindow != NULL)
4988 {
4989 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
4990 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
4991 KeepAliveID(g.ActiveId);
4992 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindowDockTree);
4993 ImGuiWindow* moving_window = g.MovingWindow->RootWindowDockTree;
4994
4995 // When a window stop being submitted while being dragged, it may will its viewport until next Begin()
4996 const bool window_disappared = (!moving_window->WasActive && !moving_window->Active);
4997 if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos) && !window_disappared)
4998 {
4999 ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
5000 if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
5001 {
5002 SetWindowPos(moving_window, pos, ImGuiCond_Always);
5003 if (moving_window->Viewport && moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window.
5004 {
5005 moving_window->Viewport->Pos = pos;
5006 moving_window->Viewport->UpdateWorkRect();
5007 }
5008 }
5009 FocusWindow(g.MovingWindow);
5010 }
5011 else
5012 {
5013 if (!window_disappared)
5014 {
5015 // Try to merge the window back into the main viewport.
5016 // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports)
5017 if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
5018 UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport);
5019
5020 // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button.
5021 if (moving_window->Viewport && !IsDragDropPayloadBeingAccepted())
5022 g.MouseViewport = moving_window->Viewport;
5023
5024 // Clear the NoInput window flag set by the Viewport system
5025 if (moving_window->Viewport)
5026 moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs;
5027 }
5028
5029 g.MovingWindow = NULL;
5030 ClearActiveID();
5031 }
5032 }
5033 else
5034 {
5035 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
5036 if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
5037 {
5038 KeepAliveID(g.ActiveId);
5039 if (!g.IO.MouseDown[0])
5040 ClearActiveID();
5041 }
5042 }
5043}
5044
5045// Initiate focusing and moving window when clicking on empty space or title bar.
5046// Initiate focusing window when clicking on a disabled item.
5047// Handle left-click and right-click focus.
5048void ImGui::UpdateMouseMovingWindowEndFrame()
5049{
5050 ImGuiContext& g = *GImGui;
5051 if (g.ActiveId != 0 || (g.HoveredId != 0 && !g.HoveredIdIsDisabled))
5052 return;
5053
5054 // Unless we just made a window/popup appear
5055 if (g.NavWindow && g.NavWindow->Appearing)
5056 return;
5057
5058 // Click on empty space to focus window and start moving
5059 // (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!)
5060 if (g.IO.MouseClicked[0])
5061 {
5062 // Handle the edge case of a popup being closed while clicking in its empty space.
5063 // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
5064 ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
5065 const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel);
5066
5067 if (root_window != NULL && !is_closed_popup)
5068 {
5069 StartMouseMovingWindow(g.HoveredWindow); //-V595
5070
5071 // Cancel moving if clicked outside of title bar
5072 if (g.IO.ConfigWindowsMoveFromTitleBarOnly)
5073 if (!(root_window->Flags & ImGuiWindowFlags_NoTitleBar) || root_window->DockIsActive)
5074 if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
5075 g.MovingWindow = NULL;
5076
5077 // Cancel moving if clicked over an item which was disabled or inhibited by popups
5078 // (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)
5079 if (g.HoveredIdIsDisabled)
5080 g.MovingWindow = NULL;
5081 }
5082 else if (root_window == NULL && g.NavWindow != NULL)
5083 {
5084 // Clicking on void disable focus
5085 FocusWindow(NULL, ImGuiFocusRequestFlags_UnlessBelowModal);
5086 }
5087 }
5088
5089 // With right mouse button we close popups without changing focus based on where the mouse is aimed
5090 // Instead, focus will be restored to the window under the bottom-most closed popup.
5091 // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
5092 if (g.IO.MouseClicked[1] && g.HoveredId == 0)
5093 {
5094 // Find the top-most window between HoveredWindow and the top-most Modal Window.
5095 // This is where we can trim the popup stack.
5096 ImGuiWindow* modal = GetTopMostPopupModal();
5097 bool hovered_window_above_modal = g.HoveredWindow && (modal == NULL || IsWindowAbove(g.HoveredWindow, modal));
5098 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
5099 }
5100}
5101
5102// This is called during NewFrame()->UpdateViewportsNewFrame() only.
5103// Need to keep in sync with SetWindowPos()
5104static void TranslateWindow(ImGuiWindow* window, const ImVec2& delta)
5105{
5106 window->Pos += delta;
5107 window->ClipRect.Translate(delta);
5108 window->OuterRectClipped.Translate(delta);
5109 window->InnerRect.Translate(delta);
5110 window->DC.CursorPos += delta;
5111 window->DC.CursorStartPos += delta;
5112 window->DC.CursorMaxPos += delta;
5113 window->DC.IdealMaxPos += delta;
5114}
5115
5116static void ScaleWindow(ImGuiWindow* window, float scale)
5117{
5118 ImVec2 origin = window->Viewport->Pos;
5119 window->Pos = ImFloor((window->Pos - origin) * scale + origin);
5120 window->Size = ImTrunc(window->Size * scale);
5121 window->SizeFull = ImTrunc(window->SizeFull * scale);
5122 window->ContentSize = ImTrunc(window->ContentSize * scale);
5123}
5124
5125static bool IsWindowActiveAndVisible(ImGuiWindow* window)
5126{
5127 return (window->Active) && (!window->Hidden);
5128}
5129
5130// 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)
5131void ImGui::UpdateHoveredWindowAndCaptureFlags()
5132{
5133 ImGuiContext& g = *GImGui;
5134 ImGuiIO& io = g.IO;
5135
5136 // FIXME-DPI: This storage was added on 2021/03/31 for test engine, but if we want to multiply WINDOWS_HOVER_PADDING
5137 // by DpiScale, we need to make this window-agnostic anyhow, maybe need storing inside ImGuiWindow.
5138 g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING));
5139
5140 // Find the window hovered by mouse:
5141 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
5142 // - 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.
5143 // - 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.
5144 bool clear_hovered_windows = false;
5145 FindHoveredWindowEx(g.IO.MousePos, false, &g.HoveredWindow, &g.HoveredWindowUnderMovingWindow);
5146 IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseViewport);
5147 g.HoveredWindowBeforeClear = g.HoveredWindow;
5148
5149 // Modal windows prevents mouse from hovering behind them.
5150 ImGuiWindow* modal_window = GetTopMostPopupModal();
5151 if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window)) // FIXME-MERGE: RootWindowDockTree ?
5152 clear_hovered_windows = true;
5153
5154 // Disabled mouse hovering (we don't currently clear MousePos, we could)
5155 if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
5156 clear_hovered_windows = true;
5157
5158 // We track click ownership. When clicked outside of a window the click is owned by the application and
5159 // won't report hovering nor request capture even while dragging over our windows afterward.
5160 const bool has_open_popup = (g.OpenPopupStack.Size > 0);
5161 const bool has_open_modal = (modal_window != NULL);
5162 int mouse_earliest_down = -1;
5163 bool mouse_any_down = false;
5164 for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
5165 {
5166 if (io.MouseClicked[i])
5167 {
5168 io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup;
5169 io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal;
5170 }
5171 mouse_any_down |= io.MouseDown[i];
5172 if (io.MouseDown[i] || io.MouseReleased[i]) // Increase release frame for our evaluation of earliest button (#1392)
5173 if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down])
5174 mouse_earliest_down = i;
5175 }
5176 const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down];
5177 const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down];
5178
5179 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
5180 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
5181 const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
5182 if (!mouse_avail && !mouse_dragging_extern_payload)
5183 clear_hovered_windows = true;
5184
5185 if (clear_hovered_windows)
5186 g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
5187
5188 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app)
5189 // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag
5190 if (g.WantCaptureMouseNextFrame != -1)
5191 {
5192 io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0);
5193 }
5194 else
5195 {
5196 io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup;
5197 io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal;
5198 }
5199
5200 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)
5201 io.WantCaptureKeyboard = false;
5202 if ((io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) == 0)
5203 {
5204 if ((g.ActiveId != 0) || (modal_window != NULL))
5205 io.WantCaptureKeyboard = true;
5206 else if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && io.ConfigNavCaptureKeyboard)
5207 io.WantCaptureKeyboard = true;
5208 }
5209 if (g.WantCaptureKeyboardNextFrame != -1) // Manual override
5210 io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
5211
5212 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
5213 io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
5214}
5215
5216// Calling SetupDrawListSharedData() is followed by SetCurrentFont() which sets up the remaining data.
5217static void SetupDrawListSharedData()
5218{
5219 ImGuiContext& g = *GImGui;
5220 ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
5221 for (ImGuiViewportP* viewport : g.Viewports)
5222 virtual_space.Add(viewport->GetMainRect());
5223 g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();
5224 g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
5225 g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError);
5226 g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
5227 if (g.Style.AntiAliasedLines)
5228 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
5229 if (g.Style.AntiAliasedLinesUseTex && !(g.IO.Fonts->Flags & ImFontAtlasFlags_NoBakedLines))
5230 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
5231 if (g.Style.AntiAliasedFill)
5232 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
5233 if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
5234 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
5235}
5236
5237void ImGui::NewFrame()
5238{
5239 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
5240 ImGuiContext& g = *GImGui;
5241
5242 // Remove pending delete hooks before frame start.
5243 // This deferred removal avoid issues of removal while iterating the hook vector
5244 for (int n = g.Hooks.Size - 1; n >= 0; n--)
5245 if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_)
5246 g.Hooks.erase(&g.Hooks[n]);
5247
5248 CallContextHooks(&g, ImGuiContextHookType_NewFramePre);
5249
5250 // Check and assert for various common IO and Configuration mistakes
5251 g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame;
5252 ErrorCheckNewFrameSanityChecks();
5253 g.ConfigFlagsCurrFrame = g.IO.ConfigFlags;
5254
5255 // Load settings on first frame, save settings when modified (after a delay)
5256 UpdateSettings();
5257
5258 g.Time += g.IO.DeltaTime;
5259 g.WithinFrameScope = true;
5260 g.FrameCount += 1;
5261 g.TooltipOverrideCount = 0;
5262 g.WindowsActiveCount = 0;
5263 g.MenusIdSubmittedThisFrame.resize(0);
5264
5265 // Calculate frame-rate for the user, as a purely luxurious feature
5266 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
5267 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
5268 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
5269 g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame));
5270 g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX;
5271
5272 // Process input queue (trickle as many events as possible), turn events into writes to IO structure
5273 g.InputEventsTrail.resize(0);
5274 UpdateInputEvents(g.IO.ConfigInputTrickleEventQueue);
5275
5276 // Update viewports (after processing input queue, so io.MouseHoveredViewport is set)
5277 UpdateViewportsNewFrame();
5278
5279 // Setup current font and draw list shared data
5280 // FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal!
5281 g.IO.Fonts->Locked = true;
5282 SetupDrawListSharedData();
5283 SetCurrentFont(GetDefaultFont());
5284 IM_ASSERT(g.Font->IsLoaded());
5285
5286 // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
5287 for (ImGuiViewportP* viewport : g.Viewports)
5288 {
5289 viewport->DrawData = NULL;
5290 viewport->DrawDataP.Valid = false;
5291 }
5292
5293 // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
5294 if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
5295 KeepAliveID(g.DragDropPayload.SourceId);
5296
5297 // [DEBUG]
5298 if (!g.IO.ConfigDebugHighlightIdConflicts || !g.IO.KeyCtrl) // Count is locked while holding CTRL
5299 g.DebugDrawIdConflicts = 0;
5300 if (g.IO.ConfigDebugHighlightIdConflicts && g.HoveredIdPreviousFrameItemCount > 1)
5301 g.DebugDrawIdConflicts = g.HoveredIdPreviousFrame;
5302
5303 // Update HoveredId data
5304 if (!g.HoveredIdPreviousFrame)
5305 g.HoveredIdTimer = 0.0f;
5306 if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
5307 g.HoveredIdNotActiveTimer = 0.0f;
5308 if (g.HoveredId)
5309 g.HoveredIdTimer += g.IO.DeltaTime;
5310 if (g.HoveredId && g.ActiveId != g.HoveredId)
5311 g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
5312 g.HoveredIdPreviousFrame = g.HoveredId;
5313 g.HoveredIdPreviousFrameItemCount = 0;
5314 g.HoveredId = 0;
5315 g.HoveredIdAllowOverlap = false;
5316 g.HoveredIdIsDisabled = false;
5317
5318 // Clear ActiveID if the item is not alive anymore.
5319 // In 1.87, the common most call to KeepAliveID() was moved from GetID() to ItemAdd().
5320 // As a result, custom widget using ButtonBehavior() _without_ ItemAdd() need to call KeepAliveID() themselves.
5321 if (g.ActiveId != 0 && g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId)
5322 {
5323 IMGUI_DEBUG_LOG_ACTIVEID("NewFrame(): ClearActiveID() because it isn't marked alive anymore!\n");
5324 ClearActiveID();
5325 }
5326
5327 // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
5328 if (g.ActiveId)
5329 g.ActiveIdTimer += g.IO.DeltaTime;
5330 g.LastActiveIdTimer += g.IO.DeltaTime;
5331 g.ActiveIdPreviousFrame = g.ActiveId;
5332 g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
5333 g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
5334 g.ActiveIdIsAlive = 0;
5335 g.ActiveIdHasBeenEditedThisFrame = false;
5336 g.ActiveIdPreviousFrameIsAlive = false;
5337 g.ActiveIdIsJustActivated = false;
5338 if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)
5339 g.TempInputId = 0;
5340 if (g.ActiveId == 0)
5341 {
5342 g.ActiveIdUsingNavDirMask = 0x00;
5343 g.ActiveIdUsingAllKeyboardKeys = false;
5344 }
5345
5346 // Record when we have been stationary as this state is preserved while over same item.
5347 // FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values.
5348 // To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function.
5349 if (g.HoverItemDelayId != 0 && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
5350 g.HoverItemUnlockedStationaryId = g.HoverItemDelayId;
5351 else if (g.HoverItemDelayId == 0)
5352 g.HoverItemUnlockedStationaryId = 0;
5353 if (g.HoveredWindow != NULL && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
5354 g.HoverWindowUnlockedStationaryId = g.HoveredWindow->ID;
5355 else if (g.HoveredWindow == NULL)
5356 g.HoverWindowUnlockedStationaryId = 0;
5357
5358 // Update hover delay for IsItemHovered() with delays and tooltips
5359 g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId;
5360 if (g.HoverItemDelayId != 0)
5361 {
5362 g.HoverItemDelayTimer += g.IO.DeltaTime;
5363 g.HoverItemDelayClearTimer = 0.0f;
5364 g.HoverItemDelayId = 0;
5365 }
5366 else if (g.HoverItemDelayTimer > 0.0f)
5367 {
5368 // This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps
5369 // We could expose 0.25f as style.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle.
5370 g.HoverItemDelayClearTimer += g.IO.DeltaTime;
5371 if (g.HoverItemDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate
5372 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.
5373 }
5374
5375 // Drag and drop
5376 g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
5377 g.DragDropAcceptIdCurr = 0;
5378 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
5379 g.DragDropWithinSource = false;
5380 g.DragDropWithinTarget = false;
5381 g.DragDropHoldJustPressedId = 0;
5382 g.TooltipPreviousWindow = NULL;
5383
5384 // Close popups on focus lost (currently wip/opt-in)
5385 //if (g.IO.AppFocusLost)
5386 // ClosePopupsExceptModals();
5387
5388 // Update keyboard input state
5389 UpdateKeyboardInputs();
5390
5391 //IM_ASSERT(g.IO.KeyCtrl == IsKeyDown(ImGuiKey_LeftCtrl) || IsKeyDown(ImGuiKey_RightCtrl));
5392 //IM_ASSERT(g.IO.KeyShift == IsKeyDown(ImGuiKey_LeftShift) || IsKeyDown(ImGuiKey_RightShift));
5393 //IM_ASSERT(g.IO.KeyAlt == IsKeyDown(ImGuiKey_LeftAlt) || IsKeyDown(ImGuiKey_RightAlt));
5394 //IM_ASSERT(g.IO.KeySuper == IsKeyDown(ImGuiKey_LeftSuper) || IsKeyDown(ImGuiKey_RightSuper));
5395
5396 // Update keyboard/gamepad navigation
5397 NavUpdate();
5398
5399 // Update mouse input state
5400 UpdateMouseInputs();
5401
5402 // Undocking
5403 // (needs to be before UpdateMouseMovingWindowNewFrame so the window is already offset and following the mouse on the detaching frame)
5404 DockContextNewFrameUpdateUndocking(&g);
5405
5406 // Mark all windows as not visible and compact unused memory.
5407 IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);
5408 const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
5409 for (ImGuiWindow* window : g.Windows)
5410 {
5411 window->WasActive = window->Active;
5412 window->Active = false;
5413 window->WriteAccessed = false;
5414 window->BeginCountPreviousFrame = window->BeginCount;
5415 window->BeginCount = 0;
5416
5417 // Garbage collect transient buffers of recently unused windows
5418 if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
5419 GcCompactTransientWindowBuffers(window);
5420 }
5421
5422 // Find hovered window
5423 // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
5424 // (currently needs to be done after the WasActive=Active loop and FindHoveredWindowEx uses ->Active)
5425 UpdateHoveredWindowAndCaptureFlags();
5426
5427 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
5428 UpdateMouseMovingWindowNewFrame();
5429
5430 // Background darkening/whitening
5431 if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
5432 g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
5433 else
5434 g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
5435
5436 g.MouseCursor = ImGuiMouseCursor_Arrow;
5437 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
5438
5439 // Platform IME data: reset for the frame
5440 g.PlatformImeDataPrev = g.PlatformImeData;
5441 g.PlatformImeData.WantVisible = false;
5442
5443 // Mouse wheel scrolling, scale
5444 UpdateMouseWheel();
5445
5446 // Garbage collect transient buffers of recently unused tables
5447 for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
5448 if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
5449 TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));
5450 for (ImGuiTableTempData& table_temp_data : g.TablesTempData)
5451 if (table_temp_data.LastTimeActive >= 0.0f && table_temp_data.LastTimeActive < memory_compact_start_time)
5452 TableGcCompactTransientBuffers(&table_temp_data);
5453 if (g.GcCompactAll)
5454 GcCompactTransientMiscBuffers();
5455 g.GcCompactAll = false;
5456
5457 // Closing the focused window restore focus to the first active root window in descending z-order
5458 if (g.NavWindow && !g.NavWindow->WasActive)
5459 FocusTopMostWindowUnderOne(NULL, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild);
5460
5461 // No window should be open at the beginning of the frame.
5462 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
5463 g.CurrentWindowStack.resize(0);
5464 g.BeginPopupStack.resize(0);
5465 g.ItemFlagsStack.resize(0);
5466 g.ItemFlagsStack.push_back(ImGuiItemFlags_AutoClosePopups); // Default flags
5467 g.CurrentItemFlags = g.ItemFlagsStack.back();
5468 g.GroupStack.resize(0);
5469
5470 // Docking
5471 DockContextNewFrameUpdateDocking(&g);
5472
5473 // [DEBUG] Update debug features
5474#ifndef IMGUI_DISABLE_DEBUG_TOOLS
5475 UpdateDebugToolItemPicker();
5476 UpdateDebugToolStackQueries();
5477 UpdateDebugToolFlashStyleColor();
5478 if (g.DebugLocateFrames > 0 && --g.DebugLocateFrames == 0)
5479 {
5480 g.DebugLocateId = 0;
5481 g.DebugBreakInLocateId = false;
5482 }
5483 if (g.DebugLogAutoDisableFrames > 0 && --g.DebugLogAutoDisableFrames == 0)
5484 {
5485 DebugLog("(Debug Log: Auto-disabled some ImGuiDebugLogFlags after 2 frames)\n");
5486 g.DebugLogFlags &= ~g.DebugLogAutoDisableFlags;
5487 g.DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
5488 }
5489#endif
5490
5491 // Create implicit/fallback window - which we will only render it if the user has added something to it.
5492 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
5493 // This fallback is particularly important as it prevents ImGui:: calls from crashing.
5494 g.WithinFrameScopeWithImplicitWindow = true;
5495 SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
5496 Begin("Debug##Default");
5497 IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
5498
5499 // Store stack sizes
5500 g.ErrorCountCurrentFrame = 0;
5501 ErrorRecoveryStoreState(&g.StackSizesInNewFrame);
5502
5503 // [DEBUG] When io.ConfigDebugBeginReturnValue is set, we make Begin()/BeginChild() return false at different level of the window-stack,
5504 // allowing to validate correct Begin/End behavior in user code.
5505#ifndef IMGUI_DISABLE_DEBUG_TOOLS
5506 if (g.IO.ConfigDebugBeginReturnValueLoop)
5507 g.DebugBeginReturnValueCullDepth = (g.DebugBeginReturnValueCullDepth == -1) ? 0 : ((g.DebugBeginReturnValueCullDepth + ((g.FrameCount % 4) == 0 ? 1 : 0)) % 10);
5508 else
5509 g.DebugBeginReturnValueCullDepth = -1;
5510#endif
5511
5512 CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
5513}
5514
5515// FIXME: Add a more explicit sort order in the window structure.
5516static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
5517{
5518 const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
5519 const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
5520 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
5521 return d;
5522 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
5523 return d;
5524 return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
5525}
5526
5527static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
5528{
5529 out_sorted_windows->push_back(window);
5530 if (window->Active)
5531 {
5532 int count = window->DC.ChildWindows.Size;
5533 ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
5534 for (int i = 0; i < count; i++)
5535 {
5536 ImGuiWindow* child = window->DC.ChildWindows[i];
5537 if (child->Active)
5538 AddWindowToSortBuffer(out_sorted_windows, child);
5539 }
5540 }
5541}
5542
5543static void AddWindowToDrawData(ImGuiWindow* window, int layer)
5544{
5545 ImGuiContext& g = *GImGui;
5546 ImGuiViewportP* viewport = window->Viewport;
5547 IM_ASSERT(viewport != NULL);
5548 g.IO.MetricsRenderWindows++;
5549 if (window->DrawList->_Splitter._Count > 1)
5550 window->DrawList->ChannelsMerge(); // Merge if user forgot to merge back. Also required in Docking branch for ImGuiWindowFlags_DockNodeHost windows.
5551 ImGui::AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[layer], window->DrawList);
5552 for (ImGuiWindow* child : window->DC.ChildWindows)
5553 if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active
5554 AddWindowToDrawData(child, layer);
5555}
5556
5557static inline int GetWindowDisplayLayer(ImGuiWindow* window)
5558{
5559 return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
5560}
5561
5562// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
5563static inline void AddRootWindowToDrawData(ImGuiWindow* window)
5564{
5565 AddWindowToDrawData(window, GetWindowDisplayLayer(window));
5566}
5567
5568static void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder)
5569{
5570 int n = builder->Layers[0]->Size;
5571 int full_size = n;
5572 for (int i = 1; i < IM_ARRAYSIZE(builder->Layers); i++)
5573 full_size += builder->Layers[i]->Size;
5574 builder->Layers[0]->resize(full_size);
5575 for (int layer_n = 1; layer_n < IM_ARRAYSIZE(builder->Layers); layer_n++)
5576 {
5577 ImVector<ImDrawList*>* layer = builder->Layers[layer_n];
5578 if (layer->empty())
5579 continue;
5580 memcpy(builder->Layers[0]->Data + n, layer->Data, layer->Size * sizeof(ImDrawList*));
5581 n += layer->Size;
5582 layer->resize(0);
5583 }
5584}
5585
5586static void InitViewportDrawData(ImGuiViewportP* viewport)
5587{
5588 ImGuiIO& io = ImGui::GetIO();
5589 ImDrawData* draw_data = &viewport->DrawDataP;
5590
5591 viewport->DrawData = draw_data; // Make publicly accessible
5592 viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists;
5593 viewport->DrawDataBuilder.Layers[1] = &viewport->DrawDataBuilder.LayerData1;
5594 viewport->DrawDataBuilder.Layers[0]->resize(0);
5595 viewport->DrawDataBuilder.Layers[1]->resize(0);
5596
5597 // When minimized, we report draw_data->DisplaySize as zero to be consistent with non-viewport mode,
5598 // and to allow applications/backends to easily skip rendering.
5599 // FIXME: Note that we however do NOT attempt to report "zero drawlist / vertices" into the ImDrawData structure.
5600 // This is because the work has been done already, and its wasted! We should fix that and add optimizations for
5601 // it earlier in the pipeline, rather than pretend to hide the data at the end of the pipeline.
5602 const bool is_minimized = (viewport->Flags & ImGuiViewportFlags_IsMinimized) != 0;
5603
5604 draw_data->Valid = true;
5605 draw_data->CmdListsCount = 0;
5606 draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
5607 draw_data->DisplayPos = viewport->Pos;
5608 draw_data->DisplaySize = is_minimized ? ImVec2(0.0f, 0.0f) : viewport->Size;
5609 draw_data->FramebufferScale = io.DisplayFramebufferScale; // FIXME-VIEWPORT: This may vary on a per-monitor/viewport basis?
5610 draw_data->OwnerViewport = viewport;
5611}
5612
5613// Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
5614// - When using this function it is sane to ensure that float are perfectly rounded to integer values,
5615// so that e.g. (int)(max.x-min.x) in user's render produce correct result.
5616// - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
5617// some frequently called functions which to modify both channels and clipping simultaneously tend to use the
5618// more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.
5619// - This is analoguous to PushFont()/PopFont() in the sense that are a mixing a global stack and a window stack,
5620// which in the case of ClipRect is not so problematic but tends to be more restrictive for fonts.
5621void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
5622{
5623 ImGuiWindow* window = GetCurrentWindow();
5624 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
5625 window->ClipRect = window->DrawList->_ClipRectStack.back();
5626}
5627
5628void ImGui::PopClipRect()
5629{
5630 ImGuiWindow* window = GetCurrentWindow();
5631 window->DrawList->PopClipRect();
5632 window->ClipRect = window->DrawList->_ClipRectStack.back();
5633}
5634
5635static ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window)
5636{
5637 for (int n = window->DC.ChildWindows.Size - 1; n >= 0; n--)
5638 if (IsWindowActiveAndVisible(window->DC.ChildWindows[n]))
5639 return FindFrontMostVisibleChildWindow(window->DC.ChildWindows[n]);
5640 return window;
5641}
5642
5643static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col)
5644{
5645 if ((col & IM_COL32_A_MASK) == 0)
5646 return;
5647
5648 ImGuiViewportP* viewport = window->Viewport;
5649 ImRect viewport_rect = viewport->GetMainRect();
5650
5651 // Draw behind window by moving the draw command at the FRONT of the draw list
5652 {
5653 // Draw list have been trimmed already, hence the explicit recreation of a draw command if missing.
5654 // 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.
5655 ImDrawList* draw_list = window->RootWindowDockTree->DrawList;
5656 draw_list->ChannelsMerge();
5657 if (draw_list->CmdBuffer.Size == 0)
5658 draw_list->AddDrawCmd();
5659 draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // FIXME: Need to stricty ensure ImDrawCmd are not merged (ElemCount==6 checks below will verify that)
5660 draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col);
5661 ImDrawCmd cmd = draw_list->CmdBuffer.back();
5662 IM_ASSERT(cmd.ElemCount == 6);
5663 draw_list->CmdBuffer.pop_back();
5664 draw_list->CmdBuffer.push_front(cmd);
5665 draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command.
5666 draw_list->PopClipRect();
5667 }
5668
5669 // Draw over sibling docking nodes in a same docking tree
5670 if (window->RootWindow->DockIsActive)
5671 {
5672 ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindowDockTree)->DrawList;
5673 draw_list->ChannelsMerge();
5674 if (draw_list->CmdBuffer.Size == 0)
5675 draw_list->AddDrawCmd();
5676 draw_list->PushClipRect(viewport_rect.Min, viewport_rect.Max, false);
5677 RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), col, 0.0f);// window->RootWindowDockTree->WindowRounding);
5678 draw_list->PopClipRect();
5679 }
5680}
5681
5682ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window)
5683{
5684 ImGuiContext& g = *GImGui;
5685 ImGuiWindow* bottom_most_visible_window = parent_window;
5686 for (int i = FindWindowDisplayIndex(parent_window); i >= 0; i--)
5687 {
5688 ImGuiWindow* window = g.Windows[i];
5689 if (window->Flags & ImGuiWindowFlags_ChildWindow)
5690 continue;
5691 if (!IsWindowWithinBeginStackOf(window, parent_window))
5692 break;
5693 if (IsWindowActiveAndVisible(window) && GetWindowDisplayLayer(window) <= GetWindowDisplayLayer(parent_window))
5694 bottom_most_visible_window = window;
5695 }
5696 return bottom_most_visible_window;
5697}
5698
5699// Important: AddWindowToDrawData() has not been called yet, meaning DockNodeHost windows needs a DrawList->ChannelsMerge() before usage.
5700// We call ChannelsMerge() lazily here at it is faster that doing a full iteration of g.Windows[] prior to calling RenderDimmedBackgrounds().
5701static void ImGui::RenderDimmedBackgrounds()
5702{
5703 ImGuiContext& g = *GImGui;
5704 ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal();
5705 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
5706 return;
5707 const bool dim_bg_for_modal = (modal_window != NULL);
5708 const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active);
5709 if (!dim_bg_for_modal && !dim_bg_for_window_list)
5710 return;
5711
5712 ImGuiViewport* viewports_already_dimmed[2] = { NULL, NULL };
5713 if (dim_bg_for_modal)
5714 {
5715 // Draw dimming behind modal or a begin stack child, whichever comes first in draw order.
5716 ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window);
5717 RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(modal_window->DC.ModalDimBgColor, g.DimBgRatio));
5718 viewports_already_dimmed[0] = modal_window->Viewport;
5719 }
5720 else if (dim_bg_for_window_list)
5721 {
5722 // Draw dimming behind CTRL+Tab target window and behind CTRL+Tab UI window
5723 RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio));
5724 if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->Viewport && g.NavWindowingListWindow->Viewport != g.NavWindowingTargetAnim->Viewport)
5725 RenderDimmedBackgroundBehindWindow(g.NavWindowingListWindow, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio));
5726 viewports_already_dimmed[0] = g.NavWindowingTargetAnim->Viewport;
5727 viewports_already_dimmed[1] = g.NavWindowingListWindow ? g.NavWindowingListWindow->Viewport : NULL;
5728
5729 // Draw border around CTRL+Tab target window
5730 ImGuiWindow* window = g.NavWindowingTargetAnim;
5731 ImGuiViewport* viewport = window->Viewport;
5732 float distance = g.FontSize;
5733 ImRect bb = window->Rect();
5734 bb.Expand(distance);
5735 if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y)
5736 bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward
5737 window->DrawList->ChannelsMerge();
5738 if (window->DrawList->CmdBuffer.Size == 0)
5739 window->DrawList->AddDrawCmd();
5740 window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size);
5741 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f);
5742 window->DrawList->PopClipRect();
5743 }
5744
5745 // Draw dimming background on _other_ viewports than the ones our windows are in
5746 for (ImGuiViewportP* viewport : g.Viewports)
5747 {
5748 if (viewport == viewports_already_dimmed[0] || viewport == viewports_already_dimmed[1])
5749 continue;
5750 if (modal_window && viewport->Window && IsWindowAbove(viewport->Window, modal_window))
5751 continue;
5752 ImDrawList* draw_list = GetForegroundDrawList(viewport);
5753 const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
5754 draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col);
5755 }
5756}
5757
5758// 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.
5759void ImGui::EndFrame()
5760{
5761 ImGuiContext& g = *GImGui;
5762 IM_ASSERT(g.Initialized);
5763
5764 // Don't process EndFrame() multiple times.
5765 if (g.FrameCountEnded == g.FrameCount)
5766 return;
5767 IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
5768
5769 CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
5770
5771 // [EXPERIMENTAL] Recover from errors
5772 if (g.IO.ConfigErrorRecovery)
5773 ErrorRecoveryTryToRecoverState(&g.StackSizesInNewFrame);
5774 ErrorCheckEndFrameSanityChecks();
5775 ErrorCheckEndFrameFinalizeErrorTooltip();
5776
5777 // Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
5778 ImGuiPlatformImeData* ime_data = &g.PlatformImeData;
5779 if (g.PlatformIO.Platform_SetImeDataFn != NULL && memcmp(ime_data, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0)
5780 {
5781 ImGuiViewport* viewport = FindViewportByID(g.PlatformImeViewport);
5782 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);
5783 if (viewport == NULL)
5784 viewport = GetMainViewport();
5785 g.PlatformIO.Platform_SetImeDataFn(&g, viewport, ime_data);
5786 }
5787
5788 // Hide implicit/fallback "Debug" window if it hasn't been used
5789 g.WithinFrameScopeWithImplicitWindow = false;
5790 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
5791 g.CurrentWindow->Active = false;
5792 End();
5793
5794 // Update navigation: CTRL+Tab, wrap-around requests
5795 NavEndFrame();
5796
5797 // Update docking
5798 DockContextEndFrame(&g);
5799
5800 SetCurrentViewport(NULL, NULL);
5801
5802 // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
5803 if (g.DragDropActive)
5804 {
5805 bool is_delivered = g.DragDropPayload.Delivery;
5806 bool is_elapsed = (g.DragDropSourceFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_PayloadAutoExpire) || g.DragDropMouseButton == -1 || !IsMouseDown(g.DragDropMouseButton));
5807 if (is_delivered || is_elapsed)
5808 ClearDragDrop();
5809 }
5810
5811 // Drag and Drop: Fallback for missing source tooltip. This is not ideal but better than nothing.
5812 // If you want to handle source item disappearing: instead of submitting your description tooltip
5813 // in the BeginDragDropSource() block of the dragged item, you can submit them from a safe single spot
5814 // (e.g. end of your item loop, or before EndFrame) by reading payload data.
5815 // In the typical case, the contents of drag tooltip should be possible to infer solely from payload data.
5816 if (g.DragDropActive && g.DragDropSourceFrameCount + 1 < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
5817 {
5818 g.DragDropWithinSource = true;
5819 SetTooltip("...");
5820 g.DragDropWithinSource = false;
5821 }
5822
5823 // End frame
5824 g.WithinFrameScope = false;
5825 g.FrameCountEnded = g.FrameCount;
5826
5827 // Initiate moving window + handle left-click and right-click focus
5828 UpdateMouseMovingWindowEndFrame();
5829
5830 // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)
5831 UpdateViewportsEndFrame();
5832
5833 // Sort the window list so that all child windows are after their parent
5834 // We cannot do that on FocusWindow() because children may not exist yet
5835 g.WindowsTempSortBuffer.resize(0);
5836 g.WindowsTempSortBuffer.reserve(g.Windows.Size);
5837 for (ImGuiWindow* window : g.Windows)
5838 {
5839 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
5840 continue;
5841 AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
5842 }
5843
5844 // 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.
5845 IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
5846 g.Windows.swap(g.WindowsTempSortBuffer);
5847 g.IO.MetricsActiveWindows = g.WindowsActiveCount;
5848
5849 // Unlock font atlas
5850 g.IO.Fonts->Locked = false;
5851
5852 // Clear Input data for next frame
5853 g.IO.MousePosPrev = g.IO.MousePos;
5854 g.IO.AppFocusLost = false;
5855 g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
5856 g.IO.InputQueueCharacters.resize(0);
5857
5858 CallContextHooks(&g, ImGuiContextHookType_EndFramePost);
5859}
5860
5861// Prepare the data for rendering so you can call GetDrawData()
5862// (As with anything within the ImGui:: namspace this doesn't touch your GPU or graphics API at all:
5863// it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend)
5864void ImGui::Render()
5865{
5866 ImGuiContext& g = *GImGui;
5867 IM_ASSERT(g.Initialized);
5868
5869 if (g.FrameCountEnded != g.FrameCount)
5870 EndFrame();
5871 if (g.FrameCountRendered == g.FrameCount)
5872 return;
5873 g.FrameCountRendered = g.FrameCount;
5874
5875 g.IO.MetricsRenderWindows = 0;
5876 CallContextHooks(&g, ImGuiContextHookType_RenderPre);
5877
5878 // Add background ImDrawList (for each active viewport)
5879 for (ImGuiViewportP* viewport : g.Viewports)
5880 {
5881 InitViewportDrawData(viewport);
5882 if (viewport->BgFgDrawLists[0] != NULL)
5883 AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));
5884 }
5885
5886 // Draw modal/window whitening backgrounds
5887 RenderDimmedBackgrounds();
5888
5889 // Add ImDrawList to render
5890 ImGuiWindow* windows_to_render_top_most[2];
5891 windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindowDockTree : NULL;
5892 windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
5893 for (ImGuiWindow* window : g.Windows)
5894 {
5895 IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'"
5896 if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
5897 AddRootWindowToDrawData(window);
5898 }
5899 for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
5900 if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
5901 AddRootWindowToDrawData(windows_to_render_top_most[n]);
5902
5903 // Draw software mouse cursor if requested by io.MouseDrawCursor flag
5904 if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None)
5905 RenderMouseCursor(g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
5906
5907 // Setup ImDrawData structures for end-user
5908 g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;
5909 for (ImGuiViewportP* viewport : g.Viewports)
5910 {
5911 FlattenDrawDataIntoSingleLayer(&viewport->DrawDataBuilder);
5912
5913 // Add foreground ImDrawList (for each active viewport)
5914 if (viewport->BgFgDrawLists[1] != NULL)
5915 AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));
5916
5917 // We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch).
5918 ImDrawData* draw_data = &viewport->DrawDataP;
5919 IM_ASSERT(draw_data->CmdLists.Size == draw_data->CmdListsCount);
5920 for (ImDrawList* draw_list : draw_data->CmdLists)
5921 draw_list->_PopUnusedDrawCmd();
5922
5923 g.IO.MetricsRenderVertices += draw_data->TotalVtxCount;
5924 g.IO.MetricsRenderIndices += draw_data->TotalIdxCount;
5925 }
5926
5927 CallContextHooks(&g, ImGuiContextHookType_RenderPost);
5928}
5929
5930// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
5931// CalcTextSize("") should return ImVec2(0.0f, g.FontSize)
5932ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
5933{
5934 ImGuiContext& g = *GImGui;
5935
5936 const char* text_display_end;
5937 if (hide_text_after_double_hash)
5938 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
5939 else
5940 text_display_end = text_end;
5941
5942 ImFont* font = g.Font;
5943 const float font_size = g.FontSize;
5944 if (text == text_display_end)
5945 return ImVec2(0.0f, font_size);
5946 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
5947
5948 // Round
5949 // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
5950 // FIXME: Investigate using ceilf or e.g.
5951 // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
5952 // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html
5953 text_size.x = IM_TRUNC(text_size.x + 0.99999f);
5954
5955 return text_size;
5956}
5957
5958// Find window given position, search front-to-back
5959// - Typically write output back to g.HoveredWindow and g.HoveredWindowUnderMovingWindow.
5960// - FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
5961// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
5962// called, aka before the next Begin(). Moving window isn't affected.
5963// - The 'find_first_and_in_any_viewport = true' mode is only used by TestEngine. It is simpler to maintain here.
5964void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window)
5965{
5966 ImGuiContext& g = *GImGui;
5967 ImGuiWindow* hovered_window = NULL;
5968 ImGuiWindow* hovered_window_under_moving_window = NULL;
5969
5970 // Special handling for the window being moved: Ignore the mouse viewport check (because it may reset/lose its viewport during the undocking frame)
5971 ImGuiViewportP* backup_moving_window_viewport = NULL;
5972 if (find_first_and_in_any_viewport == false && g.MovingWindow)
5973 {
5974 backup_moving_window_viewport = g.MovingWindow->Viewport;
5975 g.MovingWindow->Viewport = g.MouseViewport;
5976 if (!(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
5977 hovered_window = g.MovingWindow;
5978 }
5979
5980 ImVec2 padding_regular = g.Style.TouchExtraPadding;
5981 ImVec2 padding_for_resize = g.IO.ConfigWindowsResizeFromEdges ? g.WindowsHoverPadding : padding_regular;
5982 for (int i = g.Windows.Size - 1; i >= 0; i--)
5983 {
5984 ImGuiWindow* window = g.Windows[i];
5985 IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
5986 if (!window->WasActive || window->Hidden)
5987 continue;
5988 if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
5989 continue;
5990 IM_ASSERT(window->Viewport);
5991 if (window->Viewport != g.MouseViewport)
5992 continue;
5993
5994 // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
5995 ImVec2 hit_padding = (window->Flags & (ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) ? padding_regular : padding_for_resize;
5996 if (!window->OuterRectClipped.ContainsWithPad(pos, hit_padding))
5997 continue;
5998
5999 // Support for one rectangular hole in any given window
6000 // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
6001 if (window->HitTestHoleSize.x != 0)
6002 {
6003 ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
6004 ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
6005 if (ImRect(hole_pos, hole_pos + hole_size).Contains(pos))
6006 continue;
6007 }
6008
6009 if (find_first_and_in_any_viewport)
6010 {
6011 hovered_window = window;
6012 break;
6013 }
6014 else
6015 {
6016 if (hovered_window == NULL)
6017 hovered_window = window;
6018 IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
6019 if (hovered_window_under_moving_window == NULL && (!g.MovingWindow || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree))
6020 hovered_window_under_moving_window = window;
6021 if (hovered_window && hovered_window_under_moving_window)
6022 break;
6023 }
6024 }
6025
6026 *out_hovered_window = hovered_window;
6027 if (out_hovered_window_under_moving_window != NULL)
6028 *out_hovered_window_under_moving_window = hovered_window_under_moving_window;
6029 if (find_first_and_in_any_viewport == false && g.MovingWindow)
6030 g.MovingWindow->Viewport = backup_moving_window_viewport;
6031}
6032
6033bool ImGui::IsItemActive()
6034{
6035 ImGuiContext& g = *GImGui;
6036 if (g.ActiveId)
6037 return g.ActiveId == g.LastItemData.ID;
6038 return false;
6039}
6040
6041bool ImGui::IsItemActivated()
6042{
6043 ImGuiContext& g = *GImGui;
6044 if (g.ActiveId)
6045 if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID)
6046 return true;
6047 return false;
6048}
6049
6050bool ImGui::IsItemDeactivated()
6051{
6052 ImGuiContext& g = *GImGui;
6053 if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated)
6054 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
6055 return (g.ActiveIdPreviousFrame == g.LastItemData.ID && g.ActiveIdPreviousFrame != 0 && g.ActiveId != g.LastItemData.ID);
6056}
6057
6058bool ImGui::IsItemDeactivatedAfterEdit()
6059{
6060 ImGuiContext& g = *GImGui;
6061 return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
6062}
6063
6064// == GetItemID() == GetFocusID()
6065bool ImGui::IsItemFocused()
6066{
6067 ImGuiContext& g = *GImGui;
6068 if (g.NavId != g.LastItemData.ID || g.NavId == 0)
6069 return false;
6070
6071 // Special handling for the dummy item after Begin() which represent the title bar or tab.
6072 // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
6073 ImGuiWindow* window = g.CurrentWindow;
6074 if (g.LastItemData.ID == window->ID && window->WriteAccessed)
6075 return false;
6076
6077 return true;
6078}
6079
6080// Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()!
6081// Most widgets have specific reactions based on mouse-up/down state, mouse position etc.
6082bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
6083{
6084 return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
6085}
6086
6087bool ImGui::IsItemToggledOpen()
6088{
6089 ImGuiContext& g = *GImGui;
6090 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
6091}
6092
6093// Call after a Selectable() or TreeNode() involved in multi-selection.
6094// Useful if you need the per-item information before reaching EndMultiSelect(), e.g. for rendering purpose.
6095// This is only meant to be called inside a BeginMultiSelect()/EndMultiSelect() block.
6096// (Outside of multi-select, it would be misleading/ambiguous to report this signal, as widgets
6097// return e.g. a pressed event and user code is in charge of altering selection in ways we cannot predict.)
6098bool ImGui::IsItemToggledSelection()
6099{
6100 ImGuiContext& g = *GImGui;
6101 IM_ASSERT(g.CurrentMultiSelect != NULL); // Can only be used inside a BeginMultiSelect()/EndMultiSelect()
6102 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
6103}
6104
6105// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
6106// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
6107// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
6108bool ImGui::IsAnyItemHovered()
6109{
6110 ImGuiContext& g = *GImGui;
6111 return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
6112}
6113
6114bool ImGui::IsAnyItemActive()
6115{
6116 ImGuiContext& g = *GImGui;
6117 return g.ActiveId != 0;
6118}
6119
6120bool ImGui::IsAnyItemFocused()
6121{
6122 ImGuiContext& g = *GImGui;
6123 return g.NavId != 0 && g.NavCursorVisible;
6124}
6125
6126bool ImGui::IsItemVisible()
6127{
6128 ImGuiContext& g = *GImGui;
6129 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) != 0;
6130}
6131
6132bool ImGui::IsItemEdited()
6133{
6134 ImGuiContext& g = *GImGui;
6135 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0;
6136}
6137
6138// Allow next item to be overlapped by subsequent items.
6139// This works by requiring HoveredId to match for two subsequent frames,
6140// so if a following items overwrite it our interactions will naturally be disabled.
6141void ImGui::SetNextItemAllowOverlap()
6142{
6143 ImGuiContext& g = *GImGui;
6144 g.NextItemData.ItemFlags |= ImGuiItemFlags_AllowOverlap;
6145}
6146
6147#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
6148// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
6149// FIXME-LEGACY: Use SetNextItemAllowOverlap() *before* your item instead.
6150void ImGui::SetItemAllowOverlap()
6151{
6152 ImGuiContext& g = *GImGui;
6153 ImGuiID id = g.LastItemData.ID;
6154 if (g.HoveredId == id)
6155 g.HoveredIdAllowOverlap = true;
6156 if (g.ActiveId == id) // Before we made this obsolete, most calls to SetItemAllowOverlap() used to avoid this path by testing g.ActiveId != id.
6157 g.ActiveIdAllowOverlap = true;
6158}
6159#endif
6160
6161// This is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations.
6162// FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version if needed?
6163void ImGui::SetActiveIdUsingAllKeyboardKeys()
6164{
6165 ImGuiContext& g = *GImGui;
6166 IM_ASSERT(g.ActiveId != 0);
6167 g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_COUNT) - 1;
6168 g.ActiveIdUsingAllKeyboardKeys = true;
6169 NavMoveRequestCancel();
6170}
6171
6172ImGuiID ImGui::GetItemID()
6173{
6174 ImGuiContext& g = *GImGui;
6175 return g.LastItemData.ID;
6176}
6177
6178ImVec2 ImGui::GetItemRectMin()
6179{
6180 ImGuiContext& g = *GImGui;
6181 return g.LastItemData.Rect.Min;
6182}
6183
6184ImVec2 ImGui::GetItemRectMax()
6185{
6186 ImGuiContext& g = *GImGui;
6187 return g.LastItemData.Rect.Max;
6188}
6189
6190ImVec2 ImGui::GetItemRectSize()
6191{
6192 ImGuiContext& g = *GImGui;
6193 return g.LastItemData.Rect.GetSize();
6194}
6195
6196// Prior to v1.90 2023/10/16, the BeginChild() function took a 'bool border = false' parameter instead of 'ImGuiChildFlags child_flags = 0'.
6197// ImGuiChildFlags_Borders is defined as always == 1 in order to allow old code passing 'true'. Read comments in imgui.h for details!
6198bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
6199{
6200 ImGuiID id = GetCurrentWindow()->GetID(str_id);
6201 return BeginChildEx(str_id, id, size_arg, child_flags, window_flags);
6202}
6203
6204bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
6205{
6206 return BeginChildEx(NULL, id, size_arg, child_flags, window_flags);
6207}
6208
6209bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
6210{
6211 ImGuiContext& g = *GImGui;
6212 ImGuiWindow* parent_window = g.CurrentWindow;
6213 IM_ASSERT(id != 0);
6214
6215 // Sanity check as it is likely that some user will accidentally pass ImGuiWindowFlags into the ImGuiChildFlags argument.
6216 const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_FrameStyle | ImGuiChildFlags_NavFlattened;
6217 IM_UNUSED(ImGuiChildFlags_SupportedMask_);
6218 IM_ASSERT((child_flags & ~ImGuiChildFlags_SupportedMask_) == 0 && "Illegal ImGuiChildFlags value. Did you pass ImGuiWindowFlags values instead of ImGuiChildFlags?");
6219 IM_ASSERT((window_flags & ImGuiWindowFlags_AlwaysAutoResize) == 0 && "Cannot specify ImGuiWindowFlags_AlwaysAutoResize for BeginChild(). Use ImGuiChildFlags_AlwaysAutoResize!");
6220 if (child_flags & ImGuiChildFlags_AlwaysAutoResize)
6221 {
6222 IM_ASSERT((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0 && "Cannot use ImGuiChildFlags_ResizeX or ImGuiChildFlags_ResizeY with ImGuiChildFlags_AlwaysAutoResize!");
6223 IM_ASSERT((child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) != 0 && "Must use ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY with ImGuiChildFlags_AlwaysAutoResize!");
6224 }
6225#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
6226 if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding)
6227 child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding;
6228 if (window_flags & ImGuiWindowFlags_NavFlattened)
6229 child_flags |= ImGuiChildFlags_NavFlattened;
6230#endif
6231 if (child_flags & ImGuiChildFlags_AutoResizeX)
6232 child_flags &= ~ImGuiChildFlags_ResizeX;
6233 if (child_flags & ImGuiChildFlags_AutoResizeY)
6234 child_flags &= ~ImGuiChildFlags_ResizeY;
6235
6236 // Set window flags
6237 window_flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking;
6238 window_flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
6239 if (child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize))
6240 window_flags |= ImGuiWindowFlags_AlwaysAutoResize;
6241 if ((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0)
6242 window_flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
6243
6244 // Special framed style
6245 if (child_flags & ImGuiChildFlags_FrameStyle)
6246 {
6247 PushStyleColor(ImGuiCol_ChildBg, g.Style.Colors[ImGuiCol_FrameBg]);
6248 PushStyleVar(ImGuiStyleVar_ChildRounding, g.Style.FrameRounding);
6249 PushStyleVar(ImGuiStyleVar_ChildBorderSize, g.Style.FrameBorderSize);
6250 PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.FramePadding);
6251 child_flags |= ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding;
6252 window_flags |= ImGuiWindowFlags_NoMove;
6253 }
6254
6255 // Forward size
6256 // Important: Begin() has special processing to switch condition to ImGuiCond_FirstUseEver for a given axis when ImGuiChildFlags_ResizeXXX is set.
6257 // (the alternative would to store conditional flags per axis, which is possible but more code)
6258 const ImVec2 size_avail = GetContentRegionAvail();
6259 const ImVec2 size_default((child_flags & ImGuiChildFlags_AutoResizeX) ? 0.0f : size_avail.x, (child_flags & ImGuiChildFlags_AutoResizeY) ? 0.0f : size_avail.y);
6260 ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y);
6261
6262 // A SetNextWindowSize() call always has priority (#8020)
6263 // (since the code in Begin() never supported SizeVal==0.0f aka auto-resize via SetNextWindowSize() call, we don't support it here for now)
6264 // FIXME: We only support ImGuiCond_Always in this path. Supporting other paths would requires to obtain window pointer.
6265 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) != 0 && (g.NextWindowData.SizeCond & ImGuiCond_Always) != 0)
6266 {
6267 if (g.NextWindowData.SizeVal.x > 0.0f)
6268 {
6269 size.x = g.NextWindowData.SizeVal.x;
6270 child_flags &= ~ImGuiChildFlags_ResizeX;
6271 }
6272 if (g.NextWindowData.SizeVal.y > 0.0f)
6273 {
6274 size.y = g.NextWindowData.SizeVal.y;
6275 child_flags &= ~ImGuiChildFlags_ResizeY;
6276 }
6277 }
6278 SetNextWindowSize(size);
6279
6280 // Forward child flags
6281 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasChildFlags;
6282 g.NextWindowData.ChildFlags = child_flags;
6283
6284 // 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.
6285 // 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.
6286 // e.g. "ParentName###ParentIdentifier/ChildName###ChildIdentifier" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it.
6287 const char* temp_window_name;
6288 /*if (name && parent_window->IDStack.back() == parent_window->ID)
6289 ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s", parent_window->Name, name); // May omit ID if in root of ID stack
6290 else*/
6291 if (name)
6292 ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s_%08X", parent_window->Name, name, id);
6293 else
6294 ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%08X", parent_window->Name, id);
6295
6296 // Set style
6297 const float backup_border_size = g.Style.ChildBorderSize;
6298 if ((child_flags & ImGuiChildFlags_Borders) == 0)
6299 g.Style.ChildBorderSize = 0.0f;
6300
6301 // Begin into window
6302 const bool ret = Begin(temp_window_name, NULL, window_flags);
6303
6304 // Restore style
6305 g.Style.ChildBorderSize = backup_border_size;
6306 if (child_flags & ImGuiChildFlags_FrameStyle)
6307 {
6308 PopStyleVar(3);
6309 PopStyleColor();
6310 }
6311
6312 ImGuiWindow* child_window = g.CurrentWindow;
6313 child_window->ChildId = id;
6314
6315 // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
6316 // While this is not really documented/defined, it seems that the expected thing to do.
6317 if (child_window->BeginCount == 1)
6318 parent_window->DC.CursorPos = child_window->Pos;
6319
6320 // Process navigation-in immediately so NavInit can run on first frame
6321 // Can enter a child if (A) it has navigable items or (B) it can be scrolled.
6322 const ImGuiID temp_id_for_activation = ImHashStr("##Child", 0, id);
6323 if (g.ActiveId == temp_id_for_activation)
6324 ClearActiveID();
6325 if (g.NavActivateId == id && !(child_flags & ImGuiChildFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY))
6326 {
6327 FocusWindow(child_window);
6328 NavInitWindow(child_window, false);
6329 SetActiveID(temp_id_for_activation, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
6330 g.ActiveIdSource = g.NavInputSource;
6331 }
6332 return ret;
6333}
6334
6335void ImGui::EndChild()
6336{
6337 ImGuiContext& g = *GImGui;
6338 ImGuiWindow* child_window = g.CurrentWindow;
6339
6340 IM_ASSERT(g.WithinEndChild == false);
6341 IM_ASSERT(child_window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls
6342
6343 g.WithinEndChild = true;
6344 ImVec2 child_size = child_window->Size;
6345 End();
6346 if (child_window->BeginCount == 1)
6347 {
6348 ImGuiWindow* parent_window = g.CurrentWindow;
6349 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + child_size);
6350 ItemSize(child_size);
6351 const bool nav_flattened = (child_window->ChildFlags & ImGuiChildFlags_NavFlattened) != 0;
6352 if ((child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY) && !nav_flattened)
6353 {
6354 ItemAdd(bb, child_window->ChildId);
6355 RenderNavCursor(bb, child_window->ChildId);
6356
6357 // 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)
6358 if (child_window->DC.NavLayersActiveMask == 0 && child_window == g.NavWindow)
6359 RenderNavCursor(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavRenderCursorFlags_Compact);
6360 }
6361 else
6362 {
6363 // Not navigable into
6364 // - This is a bit of a fringe use case, mostly useful for undecorated, non-scrolling contents childs, or empty childs.
6365 // - We could later decide to not apply this path if ImGuiChildFlags_FrameStyle or ImGuiChildFlags_Borders is set.
6366 ItemAdd(bb, child_window->ChildId, NULL, ImGuiItemFlags_NoNav);
6367
6368 // But when flattened we directly reach items, adjust active layer mask accordingly
6369 if (nav_flattened)
6370 parent_window->DC.NavLayersActiveMaskNext |= child_window->DC.NavLayersActiveMaskNext;
6371 }
6372 if (g.HoveredWindow == child_window)
6373 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
6374 }
6375 g.WithinEndChild = false;
6376 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
6377}
6378
6379static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
6380{
6381 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
6382 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
6383 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
6384 window->SetWindowDockAllowFlags = enabled ? (window->SetWindowDockAllowFlags | flags) : (window->SetWindowDockAllowFlags & ~flags);
6385}
6386
6387ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
6388{
6389 ImGuiContext& g = *GImGui;
6390 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
6391}
6392
6393ImGuiWindow* ImGui::FindWindowByName(const char* name)
6394{
6395 ImGuiID id = ImHashStr(name);
6396 return FindWindowByID(id);
6397}
6398
6399static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
6400{
6401 const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
6402 window->ViewportPos = main_viewport->Pos;
6403 if (settings->ViewportId)
6404 {
6405 window->ViewportId = settings->ViewportId;
6406 window->ViewportPos = ImVec2(settings->ViewportPos.x, settings->ViewportPos.y);
6407 }
6408 window->Pos = ImTrunc(ImVec2(settings->Pos.x + window->ViewportPos.x, settings->Pos.y + window->ViewportPos.y));
6409 if (settings->Size.x > 0 && settings->Size.y > 0)
6410 window->Size = window->SizeFull = ImTrunc(ImVec2(settings->Size.x, settings->Size.y));
6411 window->Collapsed = settings->Collapsed;
6412 window->DockId = settings->DockId;
6413 window->DockOrder = settings->DockOrder;
6414}
6415
6416static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags)
6417{
6418 ImGuiContext& g = *GImGui;
6419
6420 const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0 && ((new_flags & ImGuiWindowFlags_Popup) == 0 || (new_flags & ImGuiWindowFlags_ChildMenu) != 0);
6421 const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild;
6422 if ((just_created || child_flag_changed) && !new_is_explicit_child)
6423 {
6424 IM_ASSERT(!g.WindowsFocusOrder.contains(window));
6425 g.WindowsFocusOrder.push_back(window);
6426 window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1);
6427 }
6428 else if (!just_created && child_flag_changed && new_is_explicit_child)
6429 {
6430 IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window);
6431 for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++)
6432 g.WindowsFocusOrder[n]->FocusOrder--;
6433 g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder);
6434 window->FocusOrder = -1;
6435 }
6436 window->IsExplicitChild = new_is_explicit_child;
6437}
6438
6439static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
6440{
6441 // Initial window state with e.g. default/arbitrary window position
6442 // Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
6443 const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
6444 window->Pos = main_viewport->Pos + ImVec2(60, 60);
6445 window->Size = window->SizeFull = ImVec2(0, 0);
6446 window->ViewportPos = main_viewport->Pos;
6447 window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = window->SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
6448
6449 if (settings != NULL)
6450 {
6451 SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
6452 ApplyWindowSettings(window, settings);
6453 }
6454 window->DC.CursorStartPos = window->DC.CursorMaxPos = window->DC.IdealMaxPos = window->Pos; // So first call to CalcWindowContentSizes() doesn't return crazy values
6455
6456 if ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
6457 {
6458 window->AutoFitFramesX = window->AutoFitFramesY = 2;
6459 window->AutoFitOnlyGrows = false;
6460 }
6461 else
6462 {
6463 if (window->Size.x <= 0.0f)
6464 window->AutoFitFramesX = 2;
6465 if (window->Size.y <= 0.0f)
6466 window->AutoFitFramesY = 2;
6467 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
6468 }
6469}
6470
6471static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
6472{
6473 // Create window the first time
6474 //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
6475 ImGuiContext& g = *GImGui;
6476 ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
6477 window->Flags = flags;
6478 g.WindowsById.SetVoidPtr(window->ID, window);
6479
6480 ImGuiWindowSettings* settings = NULL;
6481 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
6482 if ((settings = ImGui::FindWindowSettingsByWindow(window)) != 0)
6483 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
6484
6485 InitOrLoadWindowSettings(window, settings);
6486
6487 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
6488 g.Windows.push_front(window); // Quite slow but rare and only once
6489 else
6490 g.Windows.push_back(window);
6491
6492 return window;
6493}
6494
6495static ImGuiWindow* GetWindowForTitleDisplay(ImGuiWindow* window)
6496{
6497 return window->DockNodeAsHost ? window->DockNodeAsHost->VisibleWindow : window;
6498}
6499
6500static ImGuiWindow* GetWindowForTitleAndMenuHeight(ImGuiWindow* window)
6501{
6502 return (window->DockNodeAsHost && window->DockNodeAsHost->VisibleWindow) ? window->DockNodeAsHost->VisibleWindow : window;
6503}
6504
6505static inline ImVec2 CalcWindowMinSize(ImGuiWindow* window)
6506{
6507 // We give windows non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)
6508 // FIXME: Essentially we want to restrict manual resizing to WindowMinSize+Decoration, and allow api resizing to be smaller.
6509 // Perhaps should tend further a neater test for this.
6510 ImGuiContext& g = *GImGui;
6511 ImVec2 size_min;
6512 if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup))
6513 {
6514 size_min.x = (window->ChildFlags & ImGuiChildFlags_ResizeX) ? g.Style.WindowMinSize.x : 4.0f;
6515 size_min.y = (window->ChildFlags & ImGuiChildFlags_ResizeY) ? g.Style.WindowMinSize.y : 4.0f;
6516 }
6517 else
6518 {
6519 size_min.x = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : 4.0f;
6520 size_min.y = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.y : 4.0f;
6521 }
6522
6523 // Reduce artifacts with very small windows
6524 ImGuiWindow* window_for_height = GetWindowForTitleAndMenuHeight(window);
6525 size_min.y = ImMax(size_min.y, window_for_height->TitleBarHeight + window_for_height->MenuBarHeight + ImMax(0.0f, g.Style.WindowRounding - 1.0f));
6526 return size_min;
6527}
6528
6529static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired)
6530{
6531 ImGuiContext& g = *GImGui;
6532 ImVec2 new_size = size_desired;
6533 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
6534 {
6535 // See comments in SetNextWindowSizeConstraints() for details about setting size_min an size_max.
6536 ImRect cr = g.NextWindowData.SizeConstraintRect;
6537 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
6538 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
6539 if (g.NextWindowData.SizeCallback)
6540 {
6541 ImGuiSizeCallbackData data;
6542 data.UserData = g.NextWindowData.SizeCallbackUserData;
6543 data.Pos = window->Pos;
6544 data.CurrentSize = window->SizeFull;
6545 data.DesiredSize = new_size;
6546 g.NextWindowData.SizeCallback(&data);
6547 new_size = data.DesiredSize;
6548 }
6549 new_size.x = IM_TRUNC(new_size.x);
6550 new_size.y = IM_TRUNC(new_size.y);
6551 }
6552
6553 // Minimum size
6554 ImVec2 size_min = CalcWindowMinSize(window);
6555 return ImMax(new_size, size_min);
6556}
6557
6558static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)
6559{
6560 bool preserve_old_content_sizes = false;
6561 if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
6562 preserve_old_content_sizes = true;
6563 else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
6564 preserve_old_content_sizes = true;
6565 if (preserve_old_content_sizes)
6566 {
6567 *content_size_current = window->ContentSize;
6568 *content_size_ideal = window->ContentSizeIdeal;
6569 return;
6570 }
6571
6572 content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_TRUNC(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
6573 content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_TRUNC(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
6574 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);
6575 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);
6576}
6577
6578static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
6579{
6580 ImGuiContext& g = *GImGui;
6581 ImGuiStyle& style = g.Style;
6582 const float decoration_w_without_scrollbars = window->DecoOuterSizeX1 + window->DecoOuterSizeX2 - window->ScrollbarSizes.x;
6583 const float decoration_h_without_scrollbars = window->DecoOuterSizeY1 + window->DecoOuterSizeY2 - window->ScrollbarSizes.y;
6584 ImVec2 size_pad = window->WindowPadding * 2.0f;
6585 ImVec2 size_desired = size_contents + size_pad + ImVec2(decoration_w_without_scrollbars, decoration_h_without_scrollbars);
6586 if (window->Flags & ImGuiWindowFlags_Tooltip)
6587 {
6588 // Tooltip always resize
6589 return size_desired;
6590 }
6591 else
6592 {
6593 // Maximum window size is determined by the viewport size or monitor size
6594 ImVec2 size_min = CalcWindowMinSize(window);
6595 ImVec2 size_max = ImVec2(FLT_MAX, FLT_MAX);
6596
6597 // Child windows are layed within their parent (unless they are also popups/menus) and thus have no restriction
6598 if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || (window->Flags & ImGuiWindowFlags_Popup) != 0)
6599 {
6600 if (!window->ViewportOwned)
6601 size_max = ImGui::GetMainViewport()->WorkSize - style.DisplaySafeAreaPadding * 2.0f;
6602 const int monitor_idx = window->ViewportAllowPlatformMonitorExtend;
6603 if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size)
6604 size_max = g.PlatformIO.Monitors[monitor_idx].WorkSize - style.DisplaySafeAreaPadding * 2.0f;
6605 }
6606
6607 ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, size_max));
6608
6609 // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one axis may be auto-fit when calculating scrollbars,
6610 // we may need to compute/store three variants of size_auto_fit, for x/y/xy.
6611 // Here we implement a workaround for child windows only, but a full solution would apply to normal windows as well:
6612 if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && !(window->ChildFlags & ImGuiChildFlags_ResizeY))
6613 size_auto_fit.y = window->SizeFull.y;
6614 else if (!(window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->ChildFlags & ImGuiChildFlags_ResizeY))
6615 size_auto_fit.x = window->SizeFull.x;
6616
6617 // When the window cannot fit all contents (either because of constraints, either because screen is too small),
6618 // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
6619 ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
6620 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);
6621 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);
6622 if (will_have_scrollbar_x)
6623 size_auto_fit.y += style.ScrollbarSize;
6624 if (will_have_scrollbar_y)
6625 size_auto_fit.x += style.ScrollbarSize;
6626 return size_auto_fit;
6627 }
6628}
6629
6630ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window)
6631{
6632 ImVec2 size_contents_current;
6633 ImVec2 size_contents_ideal;
6634 CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal);
6635 ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal);
6636 ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
6637 return size_final;
6638}
6639
6640static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window)
6641{
6642 if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
6643 return ImGuiCol_PopupBg;
6644 if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !window->DockIsActive)
6645 return ImGuiCol_ChildBg;
6646 return ImGuiCol_WindowBg;
6647}
6648
6649static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
6650{
6651 ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left
6652 ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
6653 ImVec2 size_expected = pos_max - pos_min;
6654 ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
6655 *out_pos = pos_min;
6656 if (corner_norm.x == 0.0f)
6657 out_pos->x -= (size_constrained.x - size_expected.x);
6658 if (corner_norm.y == 0.0f)
6659 out_pos->y -= (size_constrained.y - size_expected.y);
6660 *out_size = size_constrained;
6661}
6662
6663// Data for resizing from resize grip / corner
6670static const ImGuiResizeGripDef resize_grip_def[4] =
6671{
6672 { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right
6673 { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left
6674 { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused)
6675 { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 } // Upper-right (Unused)
6676};
6677
6678// Data for resizing from borders
6680{
6681 ImVec2 InnerDir; // Normal toward inside
6682 ImVec2 SegmentN1, SegmentN2; // End positions, normalized (0,0: upper left)
6683 float OuterAngle; // Angle toward outside
6684};
6685static const ImGuiResizeBorderDef resize_border_def[4] =
6686{
6687 { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left
6688 { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right
6689 { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up
6690 { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f } // Down
6691};
6692
6693static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
6694{
6695 ImRect rect = window->Rect();
6696 if (thickness == 0.0f)
6697 rect.Max -= ImVec2(1, 1);
6698 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); }
6699 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); }
6700 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); }
6701 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); }
6702 IM_ASSERT(0);
6703 return ImRect();
6704}
6705
6706// 0..3: corners (Lower-right, Lower-left, Unused, Unused)
6707ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n)
6708{
6709 IM_ASSERT(n >= 0 && n < 4);
6710 ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID;
6711 id = ImHashStr("#RESIZE", 0, id);
6712 id = ImHashData(&n, sizeof(int), id);
6713 return id;
6714}
6715
6716// Borders (Left, Right, Up, Down)
6717ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir)
6718{
6719 IM_ASSERT(dir >= 0 && dir < 4);
6720 int n = (int)dir + 4;
6721 ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID;
6722 id = ImHashStr("#RESIZE", 0, id);
6723 id = ImHashData(&n, sizeof(int), id);
6724 return id;
6725}
6726
6727// Handle resize for: Resize Grips, Borders, Gamepad
6728// Return true when using auto-fit (double-click on resize grip)
6729static 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)
6730{
6731 ImGuiContext& g = *GImGui;
6732 ImGuiWindowFlags flags = window->Flags;
6733
6734 if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
6735 return false;
6736 if (window->WasActive == false) // Early out to avoid running this code for e.g. a hidden implicit/fallback Debug window.
6737 return false;
6738
6739 int ret_auto_fit_mask = 0x00;
6740 const float grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
6741 const float grip_hover_inner_size = (resize_grip_count > 0) ? IM_TRUNC(grip_draw_size * 0.75f) : 0.0f;
6742 const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_HOVER_PADDING : 0.0f;
6743
6744 ImRect clamp_rect = visibility_rect;
6745 const bool window_move_from_title_bar = g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar);
6746 if (window_move_from_title_bar)
6747 clamp_rect.Min.y -= window->TitleBarHeight;
6748
6749 ImVec2 pos_target(FLT_MAX, FLT_MAX);
6750 ImVec2 size_target(FLT_MAX, FLT_MAX);
6751
6752 // Clip mouse interaction rectangles within the viewport rectangle (in practice the narrowing is going to happen most of the time).
6753 // - Not narrowing would mostly benefit the situation where OS windows _without_ decoration have a threshold for hovering when outside their limits.
6754 // This is however not the case with current backends under Win32, but a custom borderless window implementation would benefit from it.
6755 // - 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.
6756 // - Note that we are unable to tell if the platform setup allows hovering with a distance threshold (on Win32, decorated window have such threshold).
6757 // We only clip interaction so we overwrite window->ClipRect, cannot call PushClipRect() yet as DrawList is not yet setup.
6758 const bool clip_with_viewport_rect = !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) || (g.IO.MouseHoveredViewport != window->ViewportId) || !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration);
6759 if (clip_with_viewport_rect)
6760 window->ClipRect = window->Viewport->GetMainRect();
6761
6762 // Resize grips and borders are on layer 1
6763 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
6764
6765 // Manual resize grips
6766 PushID("#RESIZE");
6767 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
6768 {
6769 const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n];
6770 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN);
6771
6772 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
6773 bool hovered, held;
6774 ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size);
6775 if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
6776 if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
6777 ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID()
6778 ItemAdd(resize_rect, resize_grip_id, NULL, ImGuiItemFlags_NoNav);
6779 ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
6780 //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
6781 if (hovered || held)
6782 SetMouseCursor((resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE);
6783
6784 if (held && g.IO.MouseDoubleClicked[0])
6785 {
6786 // Auto-fit when double-clicking
6787 size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
6788 ret_auto_fit_mask = 0x03; // Both axises
6789 ClearActiveID();
6790 }
6791 else if (held)
6792 {
6793 // Resize from any of the four corners
6794 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
6795 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);
6796 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);
6797 ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(def.InnerDir * grip_hover_outer_size, def.InnerDir * -grip_hover_inner_size, def.CornerPosN); // Corner of the window corresponding to our corner grip
6798 corner_target = ImClamp(corner_target, clamp_min, clamp_max);
6799 CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target);
6800 }
6801
6802 // Only lower-left grip is visible before hovering/activating
6803 if (resize_grip_n == 0 || held || hovered)
6804 resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
6805 }
6806
6807 int resize_border_mask = 0x00;
6808 if (window->Flags & ImGuiWindowFlags_ChildWindow)
6809 resize_border_mask |= ((window->ChildFlags & ImGuiChildFlags_ResizeX) ? 0x02 : 0) | ((window->ChildFlags & ImGuiChildFlags_ResizeY) ? 0x08 : 0);
6810 else
6811 resize_border_mask = g.IO.ConfigWindowsResizeFromEdges ? 0x0F : 0x00;
6812 for (int border_n = 0; border_n < 4; border_n++)
6813 {
6814 if ((resize_border_mask & (1 << border_n)) == 0)
6815 continue;
6816 const ImGuiResizeBorderDef& def = resize_border_def[border_n];
6817 const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
6818
6819 bool hovered, held;
6820 ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING);
6821 ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID()
6822 ItemAdd(border_rect, border_id, NULL, ImGuiItemFlags_NoNav);
6823 ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
6824 //GetForegroundDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
6825 if (hovered && g.HoveredIdTimer <= WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER)
6826 hovered = false;
6827 if (hovered || held)
6828 SetMouseCursor((axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS);
6829 if (held && g.IO.MouseDoubleClicked[0])
6830 {
6831 // Double-clicking bottom or right border auto-fit on this axis
6832 // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one side may be auto-fit when calculating scrollbars.
6833 // FIXME: Support top and right borders: rework CalcResizePosSizeFromAnyCorner() to be reusable in both cases.
6834 if (border_n == 1 || border_n == 3) // Right and bottom border
6835 {
6836 size_target[axis] = CalcWindowSizeAfterConstraint(window, size_auto_fit)[axis];
6837 ret_auto_fit_mask |= (1 << axis);
6838 hovered = held = false; // So border doesn't show highlighted at new position
6839 }
6840 ClearActiveID();
6841 }
6842 else if (held)
6843 {
6844 // Switch to relative resizing mode when border geometry moved (e.g. resizing a child altering parent scroll), in order to avoid resizing feedback loop.
6845 // 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.
6846 // FIXME: May want to generalize this idiom at lower-level, so more widgets can use it!
6847 const bool just_scrolled_manually_while_resizing = (g.WheelingWindow != NULL && g.WheelingWindowScrolledFrame == g.FrameCount && IsWindowChildOf(window, g.WheelingWindow, false, true));
6848 if (g.ActiveIdIsJustActivated || just_scrolled_manually_while_resizing)
6849 {
6850 g.WindowResizeBorderExpectedRect = border_rect;
6851 g.WindowResizeRelativeMode = false;
6852 }
6853 if ((window->Flags & ImGuiWindowFlags_ChildWindow) && memcmp(&g.WindowResizeBorderExpectedRect, &border_rect, sizeof(ImRect)) != 0)
6854 g.WindowResizeRelativeMode = true;
6855
6856 const ImVec2 border_curr = (window->Pos + ImMin(def.SegmentN1, def.SegmentN2) * window->Size);
6857 const float border_target_rel_mode_for_axis = border_curr[axis] + g.IO.MouseDelta[axis];
6858 const float border_target_abs_mode_for_axis = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING; // Match ButtonBehavior() padding above.
6859
6860 // Use absolute mode position
6861 ImVec2 border_target = window->Pos;
6862 border_target[axis] = border_target_abs_mode_for_axis;
6863
6864 // Use relative mode target for child window, ignore resize when moving back toward the ideal absolute position.
6865 bool ignore_resize = false;
6866 if (g.WindowResizeRelativeMode)
6867 {
6868 //GetForegroundDrawList()->AddText(GetMainViewport()->WorkPos, IM_COL32_WHITE, "Relative Mode");
6869 border_target[axis] = border_target_rel_mode_for_axis;
6870 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))
6871 ignore_resize = true;
6872 }
6873
6874 // Clamp, apply
6875 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);
6876 ImVec2 clamp_max(border_n == ImGuiDir_Left ? clamp_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? clamp_rect.Max.y : +FLT_MAX);
6877 border_target = ImClamp(border_target, clamp_min, clamp_max);
6878 if (flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent
6879 {
6880 ImGuiWindow* parent_window = window->ParentWindow;
6881 ImGuiWindowFlags parent_flags = parent_window->Flags;
6882 ImRect border_limit_rect = parent_window->InnerRect;
6883 border_limit_rect.Expand(ImVec2(-ImMax(parent_window->WindowPadding.x, parent_window->WindowBorderSize), -ImMax(parent_window->WindowPadding.y, parent_window->WindowBorderSize)));
6884 if ((axis == ImGuiAxis_X) && ((parent_flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (parent_flags & ImGuiWindowFlags_NoScrollbar)))
6885 border_target.x = ImClamp(border_target.x, border_limit_rect.Min.x, border_limit_rect.Max.x);
6886 if ((axis == ImGuiAxis_Y) && (parent_flags & ImGuiWindowFlags_NoScrollbar))
6887 border_target.y = ImClamp(border_target.y, border_limit_rect.Min.y, border_limit_rect.Max.y);
6888 }
6889 if (!ignore_resize)
6890 CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target);
6891 }
6892 if (hovered)
6893 *border_hovered = border_n;
6894 if (held)
6895 *border_held = border_n;
6896 }
6897 PopID();
6898
6899 // Restore nav layer
6900 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
6901
6902 // Navigation resize (keyboard/gamepad)
6903 // FIXME: This cannot be moved to NavUpdateWindowing() because CalcWindowSizeAfterConstraint() need to callback into user.
6904 // Not even sure the callback works here.
6905 if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindowDockTree == window)
6906 {
6907 ImVec2 nav_resize_dir;
6908 if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift)
6909 nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);
6910 if (g.NavInputSource == ImGuiInputSource_Gamepad)
6911 nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown);
6912 if (nav_resize_dir.x != 0.0f || nav_resize_dir.y != 0.0f)
6913 {
6914 const float NAV_RESIZE_SPEED = 600.0f;
6915 const float resize_step = NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y);
6916 g.NavWindowingAccumDeltaSize += nav_resize_dir * resize_step;
6917 g.NavWindowingAccumDeltaSize = ImMax(g.NavWindowingAccumDeltaSize, 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
6918 g.NavWindowingToggleLayer = false;
6919 g.NavHighlightItemUnderNav = true;
6920 resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
6921 ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaSize);
6922 if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
6923 {
6924 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
6925 size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + accum_floored);
6926 g.NavWindowingAccumDeltaSize -= accum_floored;
6927 }
6928 }
6929 }
6930
6931 // Apply back modified position/size to window
6932 const ImVec2 curr_pos = window->Pos;
6933 const ImVec2 curr_size = window->SizeFull;
6934 if (size_target.x != FLT_MAX && (window->Size.x != size_target.x || window->SizeFull.x != size_target.x))
6935 window->Size.x = window->SizeFull.x = size_target.x;
6936 if (size_target.y != FLT_MAX && (window->Size.y != size_target.y || window->SizeFull.y != size_target.y))
6937 window->Size.y = window->SizeFull.y = size_target.y;
6938 if (pos_target.x != FLT_MAX && window->Pos.x != ImTrunc(pos_target.x))
6939 window->Pos.x = ImTrunc(pos_target.x);
6940 if (pos_target.y != FLT_MAX && window->Pos.y != ImTrunc(pos_target.y))
6941 window->Pos.y = ImTrunc(pos_target.y);
6942 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)
6943 MarkIniSettingsDirty(window);
6944
6945 // Recalculate next expected border expected coordinates
6946 if (*border_held != -1)
6947 g.WindowResizeBorderExpectedRect = GetResizeBorderRect(window, *border_held, grip_hover_inner_size, WINDOWS_HOVER_PADDING);
6948
6949 return ret_auto_fit_mask;
6950}
6951
6952static inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_rect)
6953{
6954 ImGuiContext& g = *GImGui;
6955 ImVec2 size_for_clamping = window->Size;
6956 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && window->DockNodeAsHost)
6957 size_for_clamping.y = ImGui::GetFrameHeight(); // Not using window->TitleBarHeight() as DockNodeAsHost will report 0.0f here.
6958 else if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
6959 size_for_clamping.y = window->TitleBarHeight;
6960 window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
6961}
6962
6963static void RenderWindowOuterSingleBorder(ImGuiWindow* window, int border_n, ImU32 border_col, float border_size)
6964{
6965 const ImGuiResizeBorderDef& def = resize_border_def[border_n];
6966 const float rounding = window->WindowRounding;
6967 const ImRect border_r = GetResizeBorderRect(window, border_n, rounding, 0.0f);
6968 window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle);
6969 window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f);
6970 window->DrawList->PathStroke(border_col, ImDrawFlags_None, border_size);
6971}
6972
6973static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
6974{
6975 ImGuiContext& g = *GImGui;
6976 const float border_size = window->WindowBorderSize;
6977 const ImU32 border_col = GetColorU32(ImGuiCol_Border);
6978 if (border_size > 0.0f && (window->Flags & ImGuiWindowFlags_NoBackground) == 0)
6979 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, border_col, window->WindowRounding, 0, window->WindowBorderSize);
6980 else if (border_size > 0.0f)
6981 {
6982 if (window->ChildFlags & ImGuiChildFlags_ResizeX) // Similar code as 'resize_border_mask' computation in UpdateWindowManualResize() but we specifically only always draw explicit child resize border.
6983 RenderWindowOuterSingleBorder(window, 1, border_col, border_size);
6984 if (window->ChildFlags & ImGuiChildFlags_ResizeY)
6985 RenderWindowOuterSingleBorder(window, 3, border_col, border_size);
6986 }
6987 if (window->ResizeBorderHovered != -1 || window->ResizeBorderHeld != -1)
6988 {
6989 const int border_n = (window->ResizeBorderHeld != -1) ? window->ResizeBorderHeld : window->ResizeBorderHovered;
6990 const ImU32 border_col_resizing = GetColorU32((window->ResizeBorderHeld != -1) ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered);
6991 RenderWindowOuterSingleBorder(window, border_n, border_col_resizing, ImMax(2.0f, window->WindowBorderSize)); // Thicker than usual
6992 }
6993 if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
6994 {
6995 float y = window->Pos.y + window->TitleBarHeight - 1;
6996 window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), border_col, g.Style.FrameBorderSize);
6997 }
6998}
6999
7000// Draw background and borders
7001// Draw and handle scrollbars
7002void 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)
7003{
7004 ImGuiContext& g = *GImGui;
7005 ImGuiStyle& style = g.Style;
7006 ImGuiWindowFlags flags = window->Flags;
7007
7008 // Ensure that ScrollBar doesn't read last frame's SkipItems
7009 IM_ASSERT(window->BeginCount == 0);
7010 window->SkipItems = false;
7011
7012 // Draw window + handle manual resize
7013 // As we highlight the title bar when want_focus is set, multiple reappearing windows will have their title bar highlighted on their reappearing frame.
7014 const float window_rounding = window->WindowRounding;
7015 const float window_border_size = window->WindowBorderSize;
7016 if (window->Collapsed)
7017 {
7018 // Title bar only
7019 const float backup_border_size = style.FrameBorderSize;
7020 g.Style.FrameBorderSize = window->WindowBorderSize;
7021 ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && g.NavCursorVisible) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
7022 if (window->ViewportOwned)
7023 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)
7024 RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
7025 g.Style.FrameBorderSize = backup_border_size;
7026 }
7027 else
7028 {
7029 // Window background
7030 if (!(flags & ImGuiWindowFlags_NoBackground))
7031 {
7032 bool is_docking_transparent_payload = false;
7033 if (g.DragDropActive && (g.FrameCount - g.DragDropAcceptFrameCount) <= 1 && g.IO.ConfigDockingTransparentPayload)
7034 if (g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && *(ImGuiWindow**)g.DragDropPayload.Data == window)
7035 is_docking_transparent_payload = true;
7036
7037 ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window));
7038 if (window->ViewportOwned)
7039 {
7040 bg_col |= IM_COL32_A_MASK; // No alpha
7041 if (is_docking_transparent_payload)
7042 window->Viewport->Alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA;
7043 }
7044 else
7045 {
7046 // Adjust alpha. For docking
7047 bool override_alpha = false;
7048 float alpha = 1.0f;
7049 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
7050 {
7051 alpha = g.NextWindowData.BgAlphaVal;
7052 override_alpha = true;
7053 }
7054 if (is_docking_transparent_payload)
7055 {
7056 alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA; // FIXME-DOCK: Should that be an override?
7057 override_alpha = true;
7058 }
7059 if (override_alpha)
7060 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
7061 }
7062
7063 // Render, for docked windows and host windows we ensure bg goes before decorations
7064 if (window->DockIsActive)
7065 window->DockNode->LastBgColor = bg_col;
7066 ImDrawList* bg_draw_list = window->DockIsActive ? window->DockNode->HostWindow->DrawList : window->DrawList;
7067 if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost))
7068 bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG);
7069 bg_draw_list->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom);
7070 if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost))
7071 bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG);
7072 }
7073 if (window->DockIsActive)
7074 window->DockNode->IsBgDrawnThisFrame = true;
7075
7076 // Title bar
7077 // (when docked, DockNode are drawing their own title bar. Individual windows however do NOT set the _NoTitleBar flag,
7078 // in order for their pos/size to be matching their undocking state.)
7079 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
7080 {
7081 ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
7082 if (window->ViewportOwned)
7083 title_bar_col |= IM_COL32_A_MASK; // No alpha
7084 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop);
7085 }
7086
7087 // Menu bar
7088 if (flags & ImGuiWindowFlags_MenuBar)
7089 {
7090 ImRect menu_bar_rect = window->MenuBarRect();
7091 menu_bar_rect.ClipWith(window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them.
7092 window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop);
7093 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
7094 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
7095 }
7096
7097 // Docking: Unhide tab bar (small triangle in the corner), drag from small triangle to quickly undock
7098 ImGuiDockNode* node = window->DockNode;
7099 if (window->DockIsActive && node->IsHiddenTabBar() && !node->IsNoTabBar())
7100 {
7101 float unhide_sz_draw = ImTrunc(g.FontSize * 0.70f);
7102 float unhide_sz_hit = ImTrunc(g.FontSize * 0.55f);
7103 ImVec2 p = node->Pos;
7104 ImRect r(p, p + ImVec2(unhide_sz_hit, unhide_sz_hit));
7105 ImGuiID unhide_id = window->GetID("#UNHIDE");
7106 KeepAliveID(unhide_id);
7107 bool hovered, held;
7108 if (ButtonBehavior(r, unhide_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren))
7109 node->WantHiddenTabBarToggle = true;
7110 else if (held && IsMouseDragging(0))
7111 StartMouseMovingWindowOrNode(window, node, true); // Undock from tab-bar triangle = same as window/collapse menu button
7112
7113 // FIXME-DOCK: Ideally we'd use ImGuiCol_TitleBgActive/ImGuiCol_TitleBg here, but neither is guaranteed to be visible enough at this sort of size..
7114 ImU32 col = GetColorU32(((held && hovered) || (node->IsFocused && !hovered)) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
7115 window->DrawList->AddTriangleFilled(p, p + ImVec2(unhide_sz_draw, 0.0f), p + ImVec2(0.0f, unhide_sz_draw), col);
7116 }
7117
7118 // Scrollbars
7119 if (window->ScrollbarX)
7120 Scrollbar(ImGuiAxis_X);
7121 if (window->ScrollbarY)
7122 Scrollbar(ImGuiAxis_Y);
7123
7124 // Render resize grips (after their input handling so we don't have a frame of latency)
7125 if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize))
7126 {
7127 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
7128 {
7129 const ImU32 col = resize_grip_col[resize_grip_n];
7130 if ((col & IM_COL32_A_MASK) == 0)
7131 continue;
7132 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
7133 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
7134 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size)));
7135 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size)));
7136 window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12);
7137 window->DrawList->PathFillConvex(col);
7138 }
7139 }
7140
7141 // Borders (for dock node host they will be rendered over after the tab bar)
7142 if (handle_borders_and_resize_grips && !window->DockNodeAsHost)
7143 RenderWindowOuterBorders(window);
7144 }
7145}
7146
7147// When inside a dock node, this is handled in DockNodeCalcTabBarLayout() instead.
7148// Render title text, collapse button, close button
7149void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
7150{
7151 ImGuiContext& g = *GImGui;
7152 ImGuiStyle& style = g.Style;
7153 ImGuiWindowFlags flags = window->Flags;
7154
7155 const bool has_close_button = (p_open != NULL);
7156 const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
7157
7158 // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
7159 // 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?
7160 const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags;
7161 g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
7162 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
7163
7164 // Layout buttons
7165 // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
7166 float pad_l = style.FramePadding.x;
7167 float pad_r = style.FramePadding.x;
7168 float button_sz = g.FontSize;
7169 ImVec2 close_button_pos;
7170 ImVec2 collapse_button_pos;
7171 if (has_close_button)
7172 {
7173 close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
7174 pad_r += button_sz + style.ItemInnerSpacing.x;
7175 }
7176 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
7177 {
7178 collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
7179 pad_r += button_sz + style.ItemInnerSpacing.x;
7180 }
7181 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
7182 {
7183 collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y + style.FramePadding.y);
7184 pad_l += button_sz + style.ItemInnerSpacing.x;
7185 }
7186
7187 // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
7188 if (has_collapse_button)
7189 if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos, NULL))
7190 window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
7191
7192 // Close button
7193 if (has_close_button)
7194 if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
7195 *p_open = false;
7196
7197 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7198 g.CurrentItemFlags = item_flags_backup;
7199
7200 // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
7201 // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
7202 const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f;
7203 const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
7204
7205 // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
7206 // while uncentered title text will still reach edges correctly.
7207 if (pad_l > style.FramePadding.x)
7208 pad_l += g.Style.ItemInnerSpacing.x;
7209 if (pad_r > style.FramePadding.x)
7210 pad_r += g.Style.ItemInnerSpacing.x;
7211 if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
7212 {
7213 float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
7214 float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
7215 pad_l = ImMax(pad_l, pad_extend * centerness);
7216 pad_r = ImMax(pad_r, pad_extend * centerness);
7217 }
7218
7219 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);
7220 ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y);
7221 if (flags & ImGuiWindowFlags_UnsavedDocument)
7222 {
7223 ImVec2 marker_pos;
7224 marker_pos.x = ImClamp(layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x + text_size.x, layout_r.Min.x, layout_r.Max.x);
7225 marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f;
7226 if (marker_pos.x > layout_r.Min.x)
7227 {
7228 RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text));
7229 clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f));
7230 }
7231 }
7232 //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
7233 //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
7234 RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
7235}
7236
7237void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
7238{
7239 window->ParentWindow = parent_window;
7240 window->RootWindow = window->RootWindowPopupTree = window->RootWindowDockTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
7241 if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
7242 {
7243 window->RootWindowDockTree = parent_window->RootWindowDockTree;
7244 if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost))
7245 window->RootWindow = parent_window->RootWindow;
7246 }
7247 if (parent_window && (flags & ImGuiWindowFlags_Popup))
7248 window->RootWindowPopupTree = parent_window->RootWindowPopupTree;
7249 if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) // FIXME: simply use _NoTitleBar ?
7250 window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
7251 while (window->RootWindowForNav->ChildFlags & ImGuiChildFlags_NavFlattened)
7252 {
7253 IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
7254 window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
7255 }
7256}
7257
7258// [EXPERIMENTAL] Called by Begin(). NextWindowData is valid at this point.
7259// This is designed as a toy/test-bed for
7260void ImGui::UpdateWindowSkipRefresh(ImGuiWindow* window)
7261{
7262 ImGuiContext& g = *GImGui;
7263 window->SkipRefresh = false;
7264 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasRefreshPolicy) == 0)
7265 return;
7266 if (g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_TryToAvoidRefresh)
7267 {
7268 // FIXME-IDLE: Tests for e.g. mouse clicks or keyboard while focused.
7269 if (window->Appearing) // If currently appearing
7270 return;
7271 if (window->Hidden) // If was hidden (previous frame)
7272 return;
7273 if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnHover) && g.HoveredWindow)
7274 if (window->RootWindow == g.HoveredWindow->RootWindow || IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, window))
7275 return;
7276 if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnFocus) && g.NavWindow)
7277 if (window->RootWindow == g.NavWindow->RootWindow || IsWindowWithinBeginStackOf(g.NavWindow->RootWindow, window))
7278 return;
7279 window->DrawList = NULL;
7280 window->SkipRefresh = true;
7281 }
7282}
7283
7284static void SetWindowActiveForSkipRefresh(ImGuiWindow* window)
7285{
7286 window->Active = true;
7287 for (ImGuiWindow* child : window->DC.ChildWindows)
7288 if (!child->Hidden)
7289 {
7290 child->Active = child->SkipRefresh = true;
7291 SetWindowActiveForSkipRefresh(child);
7292 }
7293}
7294
7295// When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing)
7296// should be positioned behind that modal window, unless the window was created inside the modal begin-stack.
7297// In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent.
7298// - WindowA // FindBlockingModal() returns Modal1
7299// - WindowB // .. returns Modal1
7300// - Modal1 // .. returns Modal2
7301// - WindowC // .. returns Modal2
7302// - WindowD // .. returns Modal2
7303// - Modal2 // .. returns Modal2
7304// - WindowE // .. returns NULL
7305// Notes:
7306// - FindBlockingModal(NULL) == NULL is generally equivalent to GetTopMostPopupModal() == NULL.
7307// Only difference is here we check for ->Active/WasActive but it may be unnecessary.
7308ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window)
7309{
7310 ImGuiContext& g = *GImGui;
7311 if (g.OpenPopupStack.Size <= 0)
7312 return NULL;
7313
7314 // Find a modal that has common parent with specified window. Specified window should be positioned behind that modal.
7315 for (ImGuiPopupData& popup_data : g.OpenPopupStack)
7316 {
7317 ImGuiWindow* popup_window = popup_data.Window;
7318 if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal))
7319 continue;
7320 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.
7321 continue;
7322 if (window == NULL) // FindBlockingModal(NULL) test for if FocusWindow(NULL) is naturally possible via a mouse click.
7323 return popup_window;
7324 if (IsWindowWithinBeginStackOf(window, popup_window)) // Window may be over modal
7325 continue;
7326 return popup_window; // Place window right below first block modal
7327 }
7328 return NULL;
7329}
7330
7331// Push a new Dear ImGui window to add widgets to.
7332// - 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.
7333// - Begin/End can be called multiple times during the frame with the same window name to append content.
7334// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
7335// 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.
7336// - 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.
7337// - 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.
7338bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
7339{
7340 ImGuiContext& g = *GImGui;
7341 const ImGuiStyle& style = g.Style;
7342 IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required
7343 IM_ASSERT(g.WithinFrameScope); // Forgot to call ImGui::NewFrame()
7344 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
7345
7346 // Find or create
7347 ImGuiWindow* window = FindWindowByName(name);
7348 const bool window_just_created = (window == NULL);
7349 if (window_just_created)
7350 window = CreateNewWindow(name, flags);
7351
7352 // [DEBUG] Debug break requested by user
7353 if (g.DebugBreakInWindow == window->ID)
7354 IM_DEBUG_BREAK();
7355
7356 // Automatically disable manual moving/resizing when NoInputs is set
7357 if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
7358 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
7359
7360 const int current_frame = g.FrameCount;
7361 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
7362 window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
7363
7364 // Update the Appearing flag (note: the BeginDocked() path may also set this to true later)
7365 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
7366 if (flags & ImGuiWindowFlags_Popup)
7367 {
7368 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
7369 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
7370 window_just_activated_by_user |= (window != popup_ref.Window);
7371 }
7372
7373 // Update Flags, LastFrameActive, BeginOrderXXX fields
7374 const bool window_was_appearing = window->Appearing;
7375 if (first_begin_of_the_frame)
7376 {
7377 UpdateWindowInFocusOrderList(window, window_just_created, flags);
7378 window->Appearing = window_just_activated_by_user;
7379 if (window->Appearing)
7380 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
7381 window->FlagsPreviousFrame = window->Flags;
7382 window->Flags = (ImGuiWindowFlags)flags;
7383 window->ChildFlags = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : 0;
7384 window->LastFrameActive = current_frame;
7385 window->LastTimeActive = (float)g.Time;
7386 window->BeginOrderWithinParent = 0;
7387 window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
7388 }
7389 else
7390 {
7391 flags = window->Flags;
7392 }
7393
7394 // Docking
7395 // (NB: during the frame dock nodes are created, it is possible that (window->DockIsActive == false) even though (window->DockNode->Windows.Size > 1)
7396 IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); // Cannot be both
7397 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasDock)
7398 SetWindowDock(window, g.NextWindowData.DockId, g.NextWindowData.DockCond);
7399 if (first_begin_of_the_frame)
7400 {
7401 bool has_dock_node = (window->DockId != 0 || window->DockNode != NULL);
7402 bool new_auto_dock_node = !has_dock_node && GetWindowAlwaysWantOwnTabBar(window);
7403 bool dock_node_was_visible = window->DockNodeIsVisible;
7404 bool dock_tab_was_visible = window->DockTabIsVisible;
7405 if (has_dock_node || new_auto_dock_node)
7406 {
7407 BeginDocked(window, p_open);
7408 flags = window->Flags;
7409 if (window->DockIsActive)
7410 {
7411 IM_ASSERT(window->DockNode != NULL);
7412 g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; // Docking currently override constraints
7413 }
7414
7415 // Amend the Appearing flag
7416 if (window->DockTabIsVisible && !dock_tab_was_visible && dock_node_was_visible && !window->Appearing && !window_was_appearing)
7417 {
7418 window->Appearing = true;
7419 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
7420 }
7421 }
7422 else
7423 {
7424 window->DockIsActive = window->DockNodeIsVisible = window->DockTabIsVisible = false;
7425 }
7426 }
7427
7428 // 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
7429 ImGuiWindow* parent_window_in_stack = (window->DockIsActive && window->DockNode->HostWindow) ? window->DockNode->HostWindow : g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window;
7430 ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
7431 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
7432
7433 // We allow window memory to be compacted so recreate the base stack when needed.
7434 if (window->IDStack.Size == 0)
7435 window->IDStack.push_back(window->ID);
7436
7437 // Add to stack
7438 g.CurrentWindow = window;
7439 g.CurrentWindowStack.resize(g.CurrentWindowStack.Size + 1);
7440 ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();
7441 window_stack_data.Window = window;
7442 window_stack_data.ParentLastItemDataBackup = g.LastItemData;
7443 window_stack_data.DisabledOverrideReenable = (flags & ImGuiWindowFlags_Tooltip) && (g.CurrentItemFlags & ImGuiItemFlags_Disabled);
7444 ErrorRecoveryStoreState(&window_stack_data.StackSizesInBegin);
7445 g.StackSizesInBeginForCurrentWindow = &window_stack_data.StackSizesInBegin;
7446 if (flags & ImGuiWindowFlags_ChildMenu)
7447 g.BeginMenuDepth++;
7448
7449 // Update ->RootWindow and others pointers (before any possible call to FocusWindow)
7450 if (first_begin_of_the_frame)
7451 {
7452 UpdateWindowParentAndRootLinks(window, flags, parent_window);
7453 window->ParentWindowInBeginStack = parent_window_in_stack;
7454
7455 // Focus route
7456 // There's little point to expose a flag to set this: because the interesting cases won't be using parent_window_in_stack,
7457 // Use for e.g. linking a tool window in a standalone viewport to a document window, regardless of their Begin() stack parenting. (#6798)
7458 window->ParentWindowForFocusRoute = (window->RootWindow != window) ? parent_window_in_stack : NULL;
7459 if (window->ParentWindowForFocusRoute == NULL && window->DockNode != NULL)
7460 if (window->DockNode->MergedFlags & ImGuiDockNodeFlags_DockedWindowsInFocusRoute)
7461 window->ParentWindowForFocusRoute = window->DockNode->HostWindow;
7462
7463 // Override with SetNextWindowClass() field or direct call to SetWindowParentWindowForFocusRoute()
7464 if (window->WindowClass.FocusRouteParentWindowId != 0)
7465 {
7466 window->ParentWindowForFocusRoute = FindWindowByID(window->WindowClass.FocusRouteParentWindowId);
7467 IM_ASSERT(window->ParentWindowForFocusRoute != 0); // Invalid value for FocusRouteParentWindowId.
7468 }
7469 }
7470
7471 // Add to focus scope stack
7472 PushFocusScope((window->ChildFlags & ImGuiChildFlags_NavFlattened) ? g.CurrentFocusScopeId : window->ID);
7473 window->NavRootFocusScopeId = g.CurrentFocusScopeId;
7474
7475 // Add to popup stacks: update OpenPopupStack[] data, push to BeginPopupStack[]
7476 if (flags & ImGuiWindowFlags_Popup)
7477 {
7478 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
7479 popup_ref.Window = window;
7480 popup_ref.ParentNavLayer = parent_window_in_stack->DC.NavLayerCurrent;
7481 g.BeginPopupStack.push_back(popup_ref);
7482 window->PopupId = popup_ref.PopupId;
7483 }
7484
7485 // Process SetNextWindow***() calls
7486 // (FIXME: Consider splitting the HasXXX flags into X/Y components
7487 bool window_pos_set_by_api = false;
7488 bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
7489 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
7490 {
7491 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
7492 if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
7493 {
7494 // May be processed on the next frame if this is our first frame and we are measuring size
7495 // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
7496 window->SetWindowPosVal = g.NextWindowData.PosVal;
7497 window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
7498 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7499 }
7500 else
7501 {
7502 SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
7503 }
7504 }
7505 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
7506 {
7507 window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
7508 window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
7509 if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0) // Axis-specific conditions for BeginChild()
7510 g.NextWindowData.SizeVal.x = window->SizeFull.x;
7511 if ((window->ChildFlags & ImGuiChildFlags_ResizeY) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0)
7512 g.NextWindowData.SizeVal.y = window->SizeFull.y;
7513 SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
7514 }
7515 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll)
7516 {
7517 if (g.NextWindowData.ScrollVal.x >= 0.0f)
7518 {
7519 window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
7520 window->ScrollTargetCenterRatio.x = 0.0f;
7521 }
7522 if (g.NextWindowData.ScrollVal.y >= 0.0f)
7523 {
7524 window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
7525 window->ScrollTargetCenterRatio.y = 0.0f;
7526 }
7527 }
7528 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
7529 window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
7530 else if (first_begin_of_the_frame)
7531 window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
7532 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasWindowClass)
7533 window->WindowClass = g.NextWindowData.WindowClass;
7534 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
7535 SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
7536 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
7537 FocusWindow(window);
7538 if (window->Appearing)
7539 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
7540
7541 // [EXPERIMENTAL] Skip Refresh mode
7542 UpdateWindowSkipRefresh(window);
7543
7544 // Nested root windows (typically tooltips) override disabled state
7545 if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window)
7546 BeginDisabledOverrideReenable();
7547
7548 // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
7549 g.CurrentWindow = NULL;
7550
7551 // When reusing window again multiple times a frame, just append content (don't need to setup again)
7552 if (first_begin_of_the_frame && !window->SkipRefresh)
7553 {
7554 // Initialize
7555 const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
7556 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
7557 window->Active = true;
7558 window->HasCloseButton = (p_open != NULL);
7559 window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
7560 window->IDStack.resize(1);
7561 window->DrawList->_ResetForNewFrame();
7562 window->DC.CurrentTableIdx = -1;
7563 if (flags & ImGuiWindowFlags_DockNodeHost)
7564 {
7565 window->DrawList->ChannelsSplit(2);
7566 window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); // Render decorations on channel 1 as we will render the backgrounds manually later
7567 }
7568
7569 // Restore buffer capacity when woken from a compacted state, to avoid
7570 if (window->MemoryCompacted)
7571 GcAwakeTransientWindowBuffers(window);
7572
7573 // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
7574 // 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.
7575 bool window_title_visible_elsewhere = false;
7576 if ((window->Viewport && window->Viewport->Window == window) || (window->DockIsActive))
7577 window_title_visible_elsewhere = true;
7578 else if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
7579 window_title_visible_elsewhere = true;
7580 if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
7581 {
7582 size_t buf_len = (size_t)window->NameBufLen;
7583 window->Name = ImStrdupcpy(window->Name, &buf_len, name);
7584 window->NameBufLen = (int)buf_len;
7585 }
7586
7587 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
7588
7589 // Update contents size from last frame for auto-fitting (or use explicit size)
7590 CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal);
7591
7592 // FIXME: These flags are decremented before they are used. This means that in order to have these fields produce their intended behaviors
7593 // for one frame we must set them to at least 2, which is counter-intuitive. HiddenFramesCannotSkipItems is a more complicated case because
7594 // it has a single usage before this code block and may be set below before it is finally checked.
7595 if (window->HiddenFramesCanSkipItems > 0)
7596 window->HiddenFramesCanSkipItems--;
7597 if (window->HiddenFramesCannotSkipItems > 0)
7598 window->HiddenFramesCannotSkipItems--;
7599 if (window->HiddenFramesForRenderOnly > 0)
7600 window->HiddenFramesForRenderOnly--;
7601
7602 // Hide new windows for one frame until they calculate their size
7603 if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
7604 window->HiddenFramesCannotSkipItems = 1;
7605
7606 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
7607 // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
7608 if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
7609 {
7610 window->HiddenFramesCannotSkipItems = 1;
7611 if (flags & ImGuiWindowFlags_AlwaysAutoResize)
7612 {
7613 if (!window_size_x_set_by_api)
7614 window->Size.x = window->SizeFull.x = 0.f;
7615 if (!window_size_y_set_by_api)
7616 window->Size.y = window->SizeFull.y = 0.f;
7617 window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f);
7618 }
7619 }
7620
7621 // SELECT VIEWPORT
7622 // We need to do this before using any style/font sizes, as viewport with a different DPI may affect font sizes.
7623
7624 WindowSelectViewport(window);
7625 SetCurrentViewport(window, window->Viewport);
7626 window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f;
7627 SetCurrentWindow(window);
7628 flags = window->Flags;
7629
7630 // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
7631 // We read Style data after the call to UpdateSelectWindowViewport() which might be swapping the style.
7632
7633 if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow))
7634 window->WindowBorderSize = style.ChildBorderSize;
7635 else
7636 window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
7637 window->WindowPadding = style.WindowPadding;
7638 if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !(window->ChildFlags & ImGuiChildFlags_AlwaysUseWindowPadding) && window->WindowBorderSize == 0.0f)
7639 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
7640
7641 // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
7642 window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
7643 window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
7644 window->TitleBarHeight = (flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : g.FontSize + g.Style.FramePadding.y * 2.0f;
7645 window->MenuBarHeight = (flags & ImGuiWindowFlags_MenuBar) ? window->DC.MenuBarOffset.y + g.FontSize + g.Style.FramePadding.y * 2.0f : 0.0f;
7646
7647 // Depending on condition we use previous or current window size to compare against contents size to decide if a scrollbar should be visible.
7648 // Those flags will be altered further down in the function depending on more conditions.
7649 bool use_current_size_for_scrollbar_x = window_just_created;
7650 bool use_current_size_for_scrollbar_y = window_just_created;
7651 if (window_size_x_set_by_api && window->ContentSizeExplicit.x != 0.0f)
7652 use_current_size_for_scrollbar_x = true;
7653 if (window_size_y_set_by_api && window->ContentSizeExplicit.y != 0.0f) // #7252
7654 use_current_size_for_scrollbar_y = true;
7655
7656 // Collapse window by double-clicking on title bar
7657 // 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
7658 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse) && !window->DockIsActive)
7659 {
7660 // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed),
7661 // so verify that we don't have items over the title bar.
7662 ImRect title_bar_rect = window->TitleBarRect();
7663 if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && g.ActiveId == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max))
7664 if (g.IO.MouseClickedCount[0] == 2 && GetKeyOwner(ImGuiKey_MouseLeft) == ImGuiKeyOwner_NoOwner)
7665 window->WantCollapseToggle = true;
7666 if (window->WantCollapseToggle)
7667 {
7668 window->Collapsed = !window->Collapsed;
7669 if (!window->Collapsed)
7670 use_current_size_for_scrollbar_y = true;
7671 MarkIniSettingsDirty(window);
7672 }
7673 }
7674 else
7675 {
7676 window->Collapsed = false;
7677 }
7678 window->WantCollapseToggle = false;
7679
7680 // SIZE
7681
7682 // Outer Decoration Sizes
7683 // (we need to clear ScrollbarSize immediately as CalcWindowAutoFitSize() needs it and can be called from other locations).
7684 const ImVec2 scrollbar_sizes_from_last_frame = window->ScrollbarSizes;
7685 window->DecoOuterSizeX1 = 0.0f;
7686 window->DecoOuterSizeX2 = 0.0f;
7687 window->DecoOuterSizeY1 = window->TitleBarHeight + window->MenuBarHeight;
7688 window->DecoOuterSizeY2 = 0.0f;
7689 window->ScrollbarSizes = ImVec2(0.0f, 0.0f);
7690
7691 // Calculate auto-fit size, handle automatic resize
7692 const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal);
7693 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
7694 {
7695 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
7696 if (!window_size_x_set_by_api)
7697 {
7698 window->SizeFull.x = size_auto_fit.x;
7699 use_current_size_for_scrollbar_x = true;
7700 }
7701 if (!window_size_y_set_by_api)
7702 {
7703 window->SizeFull.y = size_auto_fit.y;
7704 use_current_size_for_scrollbar_y = true;
7705 }
7706 }
7707 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
7708 {
7709 // Auto-fit may only grow window during the first few frames
7710 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
7711 if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
7712 {
7713 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
7714 use_current_size_for_scrollbar_x = true;
7715 }
7716 if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
7717 {
7718 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
7719 use_current_size_for_scrollbar_y = true;
7720 }
7721 if (!window->Collapsed)
7722 MarkIniSettingsDirty(window);
7723 }
7724
7725 // Apply minimum/maximum window size constraints and final size
7726 window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
7727 window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
7728
7729 // POSITION
7730
7731 // Popup latch its initial position, will position itself when it appears next frame
7732 if (window_just_activated_by_user)
7733 {
7734 window->AutoPosLastDirection = ImGuiDir_None;
7735 if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
7736 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
7737 }
7738
7739 // Position child window
7740 if (flags & ImGuiWindowFlags_ChildWindow)
7741 {
7742 IM_ASSERT(parent_window && parent_window->Active);
7743 window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
7744 parent_window->DC.ChildWindows.push_back(window);
7745 if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
7746 window->Pos = parent_window->DC.CursorPos;
7747 }
7748
7749 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
7750 if (window_pos_with_pivot)
7751 SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
7752 else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
7753 window->Pos = FindBestWindowPosForPopup(window);
7754 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
7755 window->Pos = FindBestWindowPosForPopup(window);
7756 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
7757 window->Pos = FindBestWindowPosForPopup(window);
7758
7759 // Late create viewport if we don't fit within our current host viewport.
7760 if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_IsMinimized))
7761 if (!window->Viewport->GetMainRect().Contains(window->Rect()))
7762 {
7763 // This is based on the assumption that the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport)
7764 //ImGuiViewport* old_viewport = window->Viewport;
7765 window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing);
7766
7767 // FIXME-DPI
7768 //IM_ASSERT(old_viewport->DpiScale == window->Viewport->DpiScale); // FIXME-DPI: Something went wrong
7769 SetCurrentViewport(window, window->Viewport);
7770 window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f;
7771 SetCurrentWindow(window);
7772 }
7773
7774 if (window->ViewportOwned)
7775 WindowSyncOwnedViewport(window, parent_window_in_stack);
7776
7777 // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
7778 // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
7779 ImRect viewport_rect(window->Viewport->GetMainRect());
7780 ImRect viewport_work_rect(window->Viewport->GetWorkRect());
7781 ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
7782 ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding);
7783
7784 // Clamp position/size so window stays visible within its viewport or monitor
7785 // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
7786 // FIXME: Similar to code in GetWindowAllowedExtentRect()
7787 if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow))
7788 {
7789 if (!window->ViewportOwned && viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f)
7790 {
7791 ClampWindowPos(window, visibility_rect);
7792 }
7793 else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0)
7794 {
7795 if (g.MovingWindow != NULL && window->RootWindowDockTree == g.MovingWindow->RootWindowDockTree)
7796 {
7797 // While moving windows we allow them to straddle monitors (#7299, #3071)
7798 visibility_rect = g.PlatformMonitorsFullWorkRect;
7799 }
7800 else
7801 {
7802 // When not moving ensure visible in its monitor
7803 // Lost windows (e.g. a monitor disconnected) will naturally moved to the fallback/dummy monitor aka the main viewport.
7804 const ImGuiPlatformMonitor* monitor = GetViewportPlatformMonitor(window->Viewport);
7805 visibility_rect = ImRect(monitor->WorkPos, monitor->WorkPos + monitor->WorkSize);
7806 }
7807 visibility_rect.Expand(-visibility_padding);
7808 ClampWindowPos(window, visibility_rect);
7809 }
7810 }
7811 window->Pos = ImTrunc(window->Pos);
7812
7813 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
7814 // Large values tend to lead to variety of artifacts and are not recommended.
7815 if (window->ViewportOwned || window->DockIsActive)
7816 window->WindowRounding = 0.0f;
7817 else
7818 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
7819
7820 // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
7821 //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
7822 // window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
7823
7824 // Apply window focus (new and reactivated windows are moved to front)
7825 bool want_focus = false;
7826 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
7827 {
7828 if (flags & ImGuiWindowFlags_Popup)
7829 want_focus = true;
7830 else if ((window->DockIsActive || (flags & ImGuiWindowFlags_ChildWindow) == 0) && !(flags & ImGuiWindowFlags_Tooltip))
7831 want_focus = true;
7832 }
7833
7834 // [Test Engine] Register whole window in the item system (before submitting further decorations)
7835#ifdef IMGUI_ENABLE_TEST_ENGINE
7836 if (g.TestEngineHookItems)
7837 {
7838 IM_ASSERT(window->IDStack.Size == 1);
7839 window->IDStack.Size = 0; // As window->IDStack[0] == window->ID here, make sure TestEngine doesn't erroneously see window as parent of itself.
7840 IMGUI_TEST_ENGINE_ITEM_ADD(window->ID, window->Rect(), NULL);
7841 IMGUI_TEST_ENGINE_ITEM_INFO(window->ID, window->Name, (g.HoveredWindow == window) ? ImGuiItemStatusFlags_HoveredRect : 0);
7842 window->IDStack.Size = 1;
7843 }
7844#endif
7845
7846 // Decide if we are going to handle borders and resize grips
7847 const bool handle_borders_and_resize_grips = (window->DockNodeAsHost || !window->DockIsActive);
7848
7849 // Handle manual resize: Resize Grips, Borders, Gamepad
7850 int border_hovered = -1, border_held = -1;
7851 ImU32 resize_grip_col[4] = {};
7852 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.
7853 const float resize_grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
7854 if (handle_borders_and_resize_grips && !window->Collapsed)
7855 if (int auto_fit_mask = UpdateWindowManualResize(window, size_auto_fit, &border_hovered, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
7856 {
7857 if (auto_fit_mask & (1 << ImGuiAxis_X))
7858 use_current_size_for_scrollbar_x = true;
7859 if (auto_fit_mask & (1 << ImGuiAxis_Y))
7860 use_current_size_for_scrollbar_y = true;
7861 }
7862 window->ResizeBorderHovered = (signed char)border_hovered;
7863 window->ResizeBorderHeld = (signed char)border_held;
7864
7865 // Synchronize window --> viewport again and one last time (clamping and manual resize may have affected either)
7866 if (window->ViewportOwned)
7867 {
7868 if (!window->Viewport->PlatformRequestMove)
7869 window->Viewport->Pos = window->Pos;
7870 if (!window->Viewport->PlatformRequestResize)
7871 window->Viewport->Size = window->Size;
7872 window->Viewport->UpdateWorkRect();
7873 viewport_rect = window->Viewport->GetMainRect();
7874 }
7875
7876 // Save last known viewport position within the window itself (so it can be saved in .ini file and restored)
7877 window->ViewportPos = window->Viewport->Pos;
7878
7879 // SCROLLBAR VISIBILITY
7880
7881 // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
7882 if (!window->Collapsed)
7883 {
7884 // When reading the current size we need to read it after size constraints have been applied.
7885 // Intentionally use previous frame values for InnerRect and ScrollbarSizes.
7886 // And when we use window->DecorationUp here it doesn't have ScrollbarSizes.y applied yet.
7887 ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2));
7888 ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + scrollbar_sizes_from_last_frame;
7889 ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
7890 float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
7891 float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
7892 //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
7893 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
7894 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));
7895 if (window->ScrollbarX && !window->ScrollbarY)
7896 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
7897 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
7898
7899 // Amend the partially filled window->DecorationXXX values.
7900 window->DecoOuterSizeX2 += window->ScrollbarSizes.x;
7901 window->DecoOuterSizeY2 += window->ScrollbarSizes.y;
7902 }
7903
7904 // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
7905 // Update various regions. Variables they depend on should be set above in this function.
7906 // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
7907
7908 // Outer rectangle
7909 // Not affected by window border size. Used by:
7910 // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
7911 // - Begin() initial clipping rect for drawing window background and borders.
7912 // - Begin() clipping whole child
7913 const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
7914 const ImRect outer_rect = window->Rect();
7915 const ImRect title_bar_rect = window->TitleBarRect();
7916 window->OuterRectClipped = outer_rect;
7917 if (window->DockIsActive)
7918 window->OuterRectClipped.Min.y += window->TitleBarHeight;
7919 window->OuterRectClipped.ClipWith(host_rect);
7920
7921 // Inner rectangle
7922 // Not affected by window border size. Used by:
7923 // - InnerClipRect
7924 // - ScrollToRectEx()
7925 // - NavUpdatePageUpPageDown()
7926 // - Scrollbar()
7927 window->InnerRect.Min.x = window->Pos.x + window->DecoOuterSizeX1;
7928 window->InnerRect.Min.y = window->Pos.y + window->DecoOuterSizeY1;
7929 window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->DecoOuterSizeX2;
7930 window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->DecoOuterSizeY2;
7931
7932 // Inner clipping rectangle.
7933 // - Extend a outside of normal work region up to borders.
7934 // - This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
7935 // - It also makes clipped items be more noticeable.
7936 // - And is consistent on both axis (prior to 2024/05/03 ClipRect used WindowPadding.x * 0.5f on left and right edge), see #3312
7937 // - Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
7938 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
7939 // Affected by window/frame border size. Used by:
7940 // - Begin() initial clip rect
7941 float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
7942
7943 // Try to match the fact that our border is drawn centered over the window rectangle, rather than inner.
7944 // This is why we do a *0.5f here. We don't currently even technically support large values for WindowBorderSize,
7945 // 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).
7946 window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + window->WindowBorderSize * 0.5f);
7947 window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size * 0.5f);
7948 window->InnerClipRect.Max.x = ImFloor(window->InnerRect.Max.x - window->WindowBorderSize * 0.5f);
7949 window->InnerClipRect.Max.y = ImFloor(window->InnerRect.Max.y - window->WindowBorderSize * 0.5f);
7950 window->InnerClipRect.ClipWithFull(host_rect);
7951
7952 // Default item width. Make it proportional to window size if window manually resizes
7953 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
7954 window->ItemWidthDefault = ImTrunc(window->Size.x * 0.65f);
7955 else
7956 window->ItemWidthDefault = ImTrunc(g.FontSize * 16.0f);
7957
7958 // SCROLLING
7959
7960 // Lock down maximum scrolling
7961 // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
7962 // for right/bottom aligned items without creating a scrollbar.
7963 window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
7964 window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
7965
7966 // Apply scrolling
7967 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
7968 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
7969 window->DecoInnerSizeX1 = window->DecoInnerSizeY1 = 0.0f;
7970
7971 // DRAWING
7972
7973 // Setup draw list and outer clipping rectangle
7974 IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
7975 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
7976 PushClipRect(host_rect.Min, host_rect.Max, false);
7977
7978 // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)
7979 // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
7980 // 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)
7981 const bool is_undocked_or_docked_visible = !window->DockIsActive || window->DockTabIsVisible;
7982 if (is_undocked_or_docked_visible)
7983 {
7984 bool render_decorations_in_parent = false;
7985 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
7986 {
7987 // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here)
7988 // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs
7989 ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL;
7990 bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false;
7991 bool parent_is_empty = (parent_window->DrawList->VtxBuffer.Size == 0);
7992 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && !parent_is_empty && !previous_child_overlapping)
7993 render_decorations_in_parent = true;
7994 }
7995 if (render_decorations_in_parent)
7996 window->DrawList = parent_window->DrawList;
7997
7998 // Handle title bar, scrollbar, resize grips and resize borders
7999 const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
8000 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)));
8001 RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size);
8002
8003 if (render_decorations_in_parent)
8004 window->DrawList = &window->DrawListInst;
8005 }
8006
8007 // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
8008
8009 // Work rectangle.
8010 // Affected by window padding and border size. Used by:
8011 // - Columns() for right-most edge
8012 // - TreeNode(), CollapsingHeader() for right-most edge
8013 // - BeginTabBar() for right-most edge
8014 const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
8015 const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
8016 const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2)));
8017 const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)));
8018 window->WorkRect.Min.x = ImTrunc(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
8019 window->WorkRect.Min.y = ImTrunc(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
8020 window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
8021 window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
8022 window->ParentWorkRect = window->WorkRect;
8023
8024 // [LEGACY] Content Region
8025 // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
8026 // Unless explicit content size is specified by user, this currently represent the region leading to no scrolling.
8027 // Used by:
8028 // - Mouse wheel scrolling + many other things
8029 window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x + window->DecoOuterSizeX1;
8030 window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->DecoOuterSizeY1;
8031 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)));
8032 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)));
8033
8034 // Setup drawing context
8035 // (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.)
8036 window->DC.Indent.x = window->DecoOuterSizeX1 + window->WindowPadding.x - window->Scroll.x;
8037 window->DC.GroupOffset.x = 0.0f;
8038 window->DC.ColumnsOffset.x = 0.0f;
8039
8040 // Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount.
8041 // 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.
8042 double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DecoOuterSizeX1 + window->DC.ColumnsOffset.x;
8043 double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + window->DecoOuterSizeY1;
8044 window->DC.CursorStartPos = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y);
8045 window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y));
8046 window->DC.CursorPos = window->DC.CursorStartPos;
8047 window->DC.CursorPosPrevLine = window->DC.CursorPos;
8048 window->DC.CursorMaxPos = window->DC.CursorStartPos;
8049 window->DC.IdealMaxPos = window->DC.CursorStartPos;
8050 window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
8051 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
8052 window->DC.IsSameLine = window->DC.IsSetPos = false;
8053
8054 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
8055 window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext;
8056 window->DC.NavLayersActiveMaskNext = 0x00;
8057 window->DC.NavIsScrollPushableX = true;
8058 window->DC.NavHideHighlightOneFrame = false;
8059 window->DC.NavWindowHasScrollY = (window->ScrollMax.y > 0.0f);
8060
8061 window->DC.MenuBarAppending = false;
8062 window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user);
8063 window->DC.TreeDepth = 0;
8064 window->DC.TreeHasStackDataDepthMask = 0x00;
8065 window->DC.ChildWindows.resize(0);
8066 window->DC.StateStorage = &window->StateStorage;
8067 window->DC.CurrentColumns = NULL;
8068 window->DC.LayoutType = ImGuiLayoutType_Vertical;
8069 window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
8070
8071 window->DC.ItemWidth = window->ItemWidthDefault;
8072 window->DC.TextWrapPos = -1.0f; // disabled
8073 window->DC.ItemWidthStack.resize(0);
8074 window->DC.TextWrapPosStack.resize(0);
8075 if (flags & ImGuiWindowFlags_Modal)
8076 window->DC.ModalDimBgColor = ColorConvertFloat4ToU32(GetStyleColorVec4(ImGuiCol_ModalWindowDimBg));
8077
8078 if (window->AutoFitFramesX > 0)
8079 window->AutoFitFramesX--;
8080 if (window->AutoFitFramesY > 0)
8081 window->AutoFitFramesY--;
8082
8083 // Clear SetNextWindowXXX data (can aim to move this higher in the function)
8084 g.NextWindowData.ClearFlags();
8085
8086 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
8087 // We ImGuiFocusRequestFlags_UnlessBelowModal to:
8088 // - Avoid focusing a window that is created outside of a modal. This will prevent active modal from being closed.
8089 // - Position window behind the modal that is not a begin-parent of this window.
8090 if (want_focus)
8091 FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal);
8092 if (want_focus && window == g.NavWindow)
8093 NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls
8094
8095 // Close requested by platform window (apply to all windows in this viewport)
8096 if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport())
8097 {
8098 IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Window '%s' closed by PlatformRequestClose\n", window->Name);
8099 *p_open = false;
8100 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.
8101 }
8102
8103 // Title bar
8104 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
8105 RenderWindowTitleBarContents(window, 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);
8106
8107 // Clear hit test shape every frame
8108 window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
8109
8110 if (flags & ImGuiWindowFlags_Tooltip)
8111 g.TooltipPreviousWindow = window;
8112
8113 // Pressing CTRL+C while holding on a window copy its content to the clipboard
8114 // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope.
8115 // Maybe we can support CTRL+C on every element?
8116 /*
8117 //if (g.NavWindow == window && g.ActiveId == 0)
8118 if (g.ActiveId == window->MoveId)
8119 if (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_C))
8120 LogToClipboard();
8121 */
8122
8123 if (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)
8124 {
8125 // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source.
8126 // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginDockableDragDropSource() also overwrites it.
8127 if (g.MovingWindow == window && (window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoDocking) == 0)
8128 BeginDockableDragDropSource(window);
8129
8130 // Docking: Any dockable window can act as a target. For dock node hosts we call BeginDockableDragDropTarget() in DockNodeUpdate() instead.
8131 if (g.DragDropActive && !(flags & ImGuiWindowFlags_NoDocking))
8132 if (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != window)
8133 if ((window == window->RootWindowDockTree) && !(window->Flags & ImGuiWindowFlags_DockNodeHost))
8134 BeginDockableDragDropTarget(window);
8135 }
8136
8137 // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
8138 // This is useful to allow creating context menus on title bar only, etc.
8139 SetLastItemDataForWindow(window, title_bar_rect);
8140
8141 // [DEBUG]
8142#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8143 if (g.DebugLocateId != 0 && (window->ID == g.DebugLocateId || window->MoveId == g.DebugLocateId))
8144 DebugLocateItemResolveWithLastItem();
8145#endif
8146
8147 // [Test Engine] Register title bar / tab with MoveId.
8148#ifdef IMGUI_ENABLE_TEST_ENGINE
8149 if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
8150 IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.ID, g.LastItemData.Rect, &g.LastItemData);
8151#endif
8152 }
8153 else
8154 {
8155 // Skip refresh always mark active
8156 if (window->SkipRefresh)
8157 SetWindowActiveForSkipRefresh(window);
8158
8159 // Append
8160 SetCurrentViewport(window, window->Viewport);
8161 SetCurrentWindow(window);
8162 g.NextWindowData.ClearFlags();
8163 SetLastItemDataForWindow(window, window->TitleBarRect());
8164 }
8165
8166 if (!(flags & ImGuiWindowFlags_DockNodeHost) && !window->SkipRefresh)
8167 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
8168
8169 // 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)
8170 window->WriteAccessed = false;
8171 window->BeginCount++;
8172
8173 // Update visibility
8174 if (first_begin_of_the_frame && !window->SkipRefresh)
8175 {
8176 // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesCannotSkipItems.
8177 // This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents.
8178 // This is analogous to regular windows being hidden from one frame.
8179 // 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.
8180 if (window->DockIsActive && !window->DockTabIsVisible)
8181 {
8182 if (window->LastFrameJustFocused == g.FrameCount)
8183 window->HiddenFramesCannotSkipItems = 1;
8184 else
8185 window->HiddenFramesCanSkipItems = 1;
8186 }
8187
8188 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu))
8189 {
8190 // Child window can be out of sight and have "negative" clip windows.
8191 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
8192 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0 || window->DockIsActive);
8193 const bool nav_request = (window->ChildFlags & ImGuiChildFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
8194 if (!g.LogEnabled && !nav_request)
8195 if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
8196 {
8197 if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
8198 window->HiddenFramesCannotSkipItems = 1;
8199 else
8200 window->HiddenFramesCanSkipItems = 1;
8201 }
8202
8203 // Hide along with parent or if parent is collapsed
8204 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
8205 window->HiddenFramesCanSkipItems = 1;
8206 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
8207 window->HiddenFramesCannotSkipItems = 1;
8208 }
8209
8210 // 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)
8211 if (style.Alpha <= 0.0f)
8212 window->HiddenFramesCanSkipItems = 1;
8213
8214 // Update the Hidden flag
8215 bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);
8216 window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0);
8217
8218 // Disable inputs for requested number of frames
8219 if (window->DisableInputsFrames > 0)
8220 {
8221 window->DisableInputsFrames--;
8222 window->Flags |= ImGuiWindowFlags_NoInputs;
8223 }
8224
8225 // Update the SkipItems flag, used to early out of all items functions (no layout required)
8226 bool skip_items = false;
8227 if (window->Collapsed || !window->Active || hidden_regular)
8228 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
8229 skip_items = true;
8230 window->SkipItems = skip_items;
8231
8232 // Restore NavLayersActiveMaskNext to previous value when not visible, so a CTRL+Tab back can use a safe value.
8233 if (window->SkipItems)
8234 window->DC.NavLayersActiveMaskNext = window->DC.NavLayersActiveMask;
8235
8236 // Sanity check: there are two spots which can set Appearing = true
8237 // - when 'window_just_activated_by_user' is set -> HiddenFramesCannotSkipItems is set -> SkipItems always false
8238 // - in BeginDocked() path when DockNodeIsVisible == DockTabIsVisible == true -> hidden _should_ be all zero // FIXME: Not formally proven, hence the assert.
8239 if (window->SkipItems && !window->Appearing)
8240 IM_ASSERT(window->Appearing == false); // Please report on GitHub if this triggers: https://github.com/ocornut/imgui/issues/4177
8241 }
8242 else if (first_begin_of_the_frame)
8243 {
8244 // Skip refresh mode
8245 window->SkipItems = true;
8246 }
8247
8248 // [DEBUG] io.ConfigDebugBeginReturnValue override return value to test Begin/End and BeginChild/EndChild behaviors.
8249 // (The implicit fallback window is NOT automatically ended allowing it to always be able to receive commands without crashing)
8250#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8251 if (!window->IsFallbackWindow)
8252 if ((g.IO.ConfigDebugBeginReturnValueOnce && window_just_created) || (g.IO.ConfigDebugBeginReturnValueLoop && g.DebugBeginReturnValueCullDepth == g.CurrentWindowStack.Size))
8253 {
8254 if (window->AutoFitFramesX > 0) { window->AutoFitFramesX++; }
8255 if (window->AutoFitFramesY > 0) { window->AutoFitFramesY++; }
8256 return false;
8257 }
8258#endif
8259
8260 return !window->SkipItems;
8261}
8262
8263static void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect)
8264{
8265 ImGuiContext& g = *GImGui;
8266 if (window->DockIsActive)
8267 SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DockTabItemStatusFlags, window->DockTabItemRect);
8268 else
8269 SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(rect.Min, rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, rect);
8270}
8271
8272void ImGui::End()
8273{
8274 ImGuiContext& g = *GImGui;
8275 ImGuiWindow* window = g.CurrentWindow;
8276
8277 // Error checking: verify that user hasn't called End() too many times!
8278 if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
8279 {
8280 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
8281 return;
8282 }
8283 ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();
8284
8285 // Error checking: verify that user doesn't directly call End() on a child window.
8286 if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_DockNodeHost) && !window->DockIsActive)
8287 IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
8288
8289 // Close anything that is open
8290 if (window->DC.CurrentColumns)
8291 EndColumns();
8292 if (!(window->Flags & ImGuiWindowFlags_DockNodeHost) && !window->SkipRefresh) // Pop inner window clip rectangle
8293 PopClipRect();
8294 PopFocusScope();
8295 if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window)
8296 EndDisabledOverrideReenable();
8297
8298 if (window->SkipRefresh)
8299 {
8300 IM_ASSERT(window->DrawList == NULL);
8301 window->DrawList = &window->DrawListInst;
8302 }
8303
8304 // Stop logging
8305 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
8306 LogFinish();
8307
8308 if (window->DC.IsSetPos)
8309 ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
8310
8311 // Docking: report contents sizes to parent to allow for auto-resize
8312 if (window->DockNode && window->DockTabIsVisible)
8313 if (ImGuiWindow* host_window = window->DockNode->HostWindow) // FIXME-DOCK
8314 host_window->DC.CursorMaxPos = window->DC.CursorMaxPos + window->WindowPadding - host_window->WindowPadding;
8315
8316 // Pop from window stack
8317 g.LastItemData = window_stack_data.ParentLastItemDataBackup;
8318 if (window->Flags & ImGuiWindowFlags_ChildMenu)
8319 g.BeginMenuDepth--;
8320 if (window->Flags & ImGuiWindowFlags_Popup)
8321 g.BeginPopupStack.pop_back();
8322
8323 // Error handling, state recovery
8324 if (g.IO.ConfigErrorRecovery)
8325 ErrorRecoveryTryToRecoverWindowState(&window_stack_data.StackSizesInBegin);
8326
8327 g.CurrentWindowStack.pop_back();
8328 SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
8329 if (g.CurrentWindow)
8330 SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport);
8331}
8332
8333void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
8334{
8335 ImGuiContext& g = *GImGui;
8336 IM_ASSERT(window == window->RootWindow);
8337
8338 const int cur_order = window->FocusOrder;
8339 IM_ASSERT(g.WindowsFocusOrder[cur_order] == window);
8340 if (g.WindowsFocusOrder.back() == window)
8341 return;
8342
8343 const int new_order = g.WindowsFocusOrder.Size - 1;
8344 for (int n = cur_order; n < new_order; n++)
8345 {
8346 g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1];
8347 g.WindowsFocusOrder[n]->FocusOrder--;
8348 IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n);
8349 }
8350 g.WindowsFocusOrder[new_order] = window;
8351 window->FocusOrder = (short)new_order;
8352}
8353
8354void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
8355{
8356 ImGuiContext& g = *GImGui;
8357 ImGuiWindow* current_front_window = g.Windows.back();
8358 if (current_front_window == window || current_front_window->RootWindowDockTree == window) // Cheap early out (could be better)
8359 return;
8360 for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
8361 if (g.Windows[i] == window)
8362 {
8363 memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
8364 g.Windows[g.Windows.Size - 1] = window;
8365 break;
8366 }
8367}
8368
8369void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
8370{
8371 ImGuiContext& g = *GImGui;
8372 if (g.Windows[0] == window)
8373 return;
8374 for (int i = 0; i < g.Windows.Size; i++)
8375 if (g.Windows[i] == window)
8376 {
8377 memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
8378 g.Windows[0] = window;
8379 break;
8380 }
8381}
8382
8383void ImGui::BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* behind_window)
8384{
8385 IM_ASSERT(window != NULL && behind_window != NULL);
8386 ImGuiContext& g = *GImGui;
8387 window = window->RootWindow;
8388 behind_window = behind_window->RootWindow;
8389 int pos_wnd = FindWindowDisplayIndex(window);
8390 int pos_beh = FindWindowDisplayIndex(behind_window);
8391 if (pos_wnd < pos_beh)
8392 {
8393 size_t copy_bytes = (pos_beh - pos_wnd - 1) * sizeof(ImGuiWindow*);
8394 memmove(&g.Windows.Data[pos_wnd], &g.Windows.Data[pos_wnd + 1], copy_bytes);
8395 g.Windows[pos_beh - 1] = window;
8396 }
8397 else
8398 {
8399 size_t copy_bytes = (pos_wnd - pos_beh) * sizeof(ImGuiWindow*);
8400 memmove(&g.Windows.Data[pos_beh + 1], &g.Windows.Data[pos_beh], copy_bytes);
8401 g.Windows[pos_beh] = window;
8402 }
8403}
8404
8405int ImGui::FindWindowDisplayIndex(ImGuiWindow* window)
8406{
8407 ImGuiContext& g = *GImGui;
8408 return g.Windows.index_from_ptr(g.Windows.find(window));
8409}
8410
8411// Moving window to front of display and set focus (which happens to be back of our sorted list)
8412void ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags)
8413{
8414 ImGuiContext& g = *GImGui;
8415
8416 // Modal check?
8417 if ((flags & ImGuiFocusRequestFlags_UnlessBelowModal) && (g.NavWindow != window)) // Early out in common case.
8418 if (ImGuiWindow* blocking_modal = FindBlockingModal(window))
8419 {
8420 // This block would typically be reached in two situations:
8421 // - API call to FocusWindow() with a window under a modal and ImGuiFocusRequestFlags_UnlessBelowModal flag.
8422 // - User clicking on void or anything behind a modal while a modal is open (window == NULL)
8423 IMGUI_DEBUG_LOG_FOCUS("[focus] FocusWindow(\"%s\", UnlessBelowModal): prevented by \"%s\".\n", window ? window->Name : "<NULL>", blocking_modal->Name);
8424 if (window && window == window->RootWindow && (window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
8425 BringWindowToDisplayBehind(window, blocking_modal); // Still bring right under modal. (FIXME: Could move in focus list too?)
8426 ClosePopupsOverWindow(GetTopMostPopupModal(), false); // Note how we need to use GetTopMostPopupModal() aad NOT blocking_modal, to handle nested modals
8427 return;
8428 }
8429
8430 // Find last focused child (if any) and focus it instead.
8431 if ((flags & ImGuiFocusRequestFlags_RestoreFocusedChild) && window != NULL)
8432 window = NavRestoreLastChildNavWindow(window);
8433
8434 // Apply focus
8435 if (g.NavWindow != window)
8436 {
8437 SetNavWindow(window);
8438 if (window && g.NavHighlightItemUnderNav)
8439 g.NavMousePosDirty = true;
8440 g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
8441 g.NavLayer = ImGuiNavLayer_Main;
8442 SetNavFocusScope(window ? window->NavRootFocusScopeId : 0);
8443 g.NavIdIsAlive = false;
8444 g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
8445
8446 // Close popups if any
8447 ClosePopupsOverWindow(window, false);
8448 }
8449
8450 // Move the root window to the top of the pile
8451 IM_ASSERT(window == NULL || window->RootWindowDockTree != NULL);
8452 ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL;
8453 ImGuiWindow* display_front_window = window ? window->RootWindowDockTree : NULL;
8454 ImGuiDockNode* dock_node = window ? window->DockNode : NULL;
8455 bool active_id_window_is_dock_node_host = (g.ActiveIdWindow && dock_node && dock_node->HostWindow == g.ActiveIdWindow);
8456
8457 // Steal active widgets. Some of the cases it triggers includes:
8458 // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
8459 // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
8460 // - Using dock host items (tab, collapse button) can trigger this before we redirect the ActiveIdWindow toward the child window.
8461 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
8462 if (!g.ActiveIdNoClearOnFocusLoss && !active_id_window_is_dock_node_host)
8463 ClearActiveID();
8464
8465 // Passing NULL allow to disable keyboard focus
8466 if (!window)
8467 return;
8468 window->LastFrameJustFocused = g.FrameCount;
8469
8470 // Select in dock node
8471 // For #2304 we avoid applying focus immediately before the tabbar is visible.
8472 //if (dock_node && dock_node->TabBar)
8473 // dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->TabId;
8474
8475 // Bring to front
8476 BringWindowToFocusFront(focus_front_window);
8477 if (((window->Flags | focus_front_window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
8478 BringWindowToDisplayFront(display_front_window);
8479}
8480
8481void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags)
8482{
8483 ImGuiContext& g = *GImGui;
8484 int start_idx = g.WindowsFocusOrder.Size - 1;
8485 if (under_this_window != NULL)
8486 {
8487 // Aim at root window behind us, if we are in a child window that's our own root (see #4640)
8488 int offset = -1;
8489 while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow)
8490 {
8491 under_this_window = under_this_window->ParentWindow;
8492 offset = 0;
8493 }
8494 start_idx = FindWindowFocusIndex(under_this_window) + offset;
8495 }
8496 for (int i = start_idx; i >= 0; i--)
8497 {
8498 // 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.
8499 ImGuiWindow* window = g.WindowsFocusOrder[i];
8500 if (window == ignore_window || !window->WasActive)
8501 continue;
8502 if (filter_viewport != NULL && window->Viewport != filter_viewport)
8503 continue;
8504 if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
8505 {
8506 // FIXME-DOCK: When ImGuiFocusRequestFlags_RestoreFocusedChild is set...
8507 // This is failing (lagging by one frame) for docked windows.
8508 // If A and B are docked into window and B disappear, at the NewFrame() call site window->NavLastChildNavWindow will still point to B.
8509 // 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)
8510 // to tell which is the "previous" window. Or we may leverage 'LastFrameFocused/LastFrameJustFocused' and have this function handle child window itself?
8511 FocusWindow(window, flags);
8512 return;
8513 }
8514 }
8515 FocusWindow(NULL, flags);
8516}
8517
8518// Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only.
8519void ImGui::SetCurrentFont(ImFont* font)
8520{
8521 ImGuiContext& g = *GImGui;
8522 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
8523 IM_ASSERT(font->Scale > 0.0f);
8524 g.Font = font;
8525 g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
8526 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
8527 g.FontScale = g.FontSize / g.Font->FontSize;
8528
8529 ImFontAtlas* atlas = g.Font->ContainerAtlas;
8530 g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
8531 g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
8532 g.DrawListSharedData.Font = g.Font;
8533 g.DrawListSharedData.FontSize = g.FontSize;
8534 g.DrawListSharedData.FontScale = g.FontScale;
8535}
8536
8537// Use ImDrawList::_SetTextureID(), making our shared g.FontStack[] authorative against window-local ImDrawList.
8538// - Whereas ImDrawList::PushTextureID()/PopTextureID() is not to be used across Begin() calls.
8539// - Note that we don't propagate current texture id when e.g. Begin()-ing into a new window, we never really did...
8540// - Some code paths never really fully worked with multiple atlas textures.
8541// - The right-ish solution may be to remove _SetTextureID() and make AddText/RenderText lazily call PushTextureID()/PopTextureID()
8542// the same way AddImage() does, but then all other primitives would also need to? I don't think we should tackle this problem
8543// because we have a concrete need and a test bed for multiple atlas textures.
8544void ImGui::PushFont(ImFont* font)
8545{
8546 ImGuiContext& g = *GImGui;
8547 if (font == NULL)
8548 font = GetDefaultFont();
8549 g.FontStack.push_back(font);
8550 SetCurrentFont(font);
8551 g.CurrentWindow->DrawList->_SetTextureID(font->ContainerAtlas->TexID);
8552}
8553
8554void ImGui::PopFont()
8555{
8556 ImGuiContext& g = *GImGui;
8557 if (g.FontStack.Size <= 0)
8558 {
8559 IM_ASSERT_USER_ERROR(0, "Calling PopFont() too many times!");
8560 return;
8561 }
8562 g.FontStack.pop_back();
8563 ImFont* font = g.FontStack.Size == 0 ? GetDefaultFont() : g.FontStack.back();
8564 SetCurrentFont(font);
8565 g.CurrentWindow->DrawList->_SetTextureID(font->ContainerAtlas->TexID);
8566}
8567
8568void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
8569{
8570 ImGuiContext& g = *GImGui;
8571 ImGuiItemFlags item_flags = g.CurrentItemFlags;
8572 IM_ASSERT(item_flags == g.ItemFlagsStack.back());
8573 if (enabled)
8574 item_flags |= option;
8575 else
8576 item_flags &= ~option;
8577 g.CurrentItemFlags = item_flags;
8578 g.ItemFlagsStack.push_back(item_flags);
8579}
8580
8581void ImGui::PopItemFlag()
8582{
8583 ImGuiContext& g = *GImGui;
8584 if (g.ItemFlagsStack.Size <= 1)
8585 {
8586 IM_ASSERT_USER_ERROR(0, "Calling PopItemFlag() too many times!");
8587 return;
8588 }
8589 g.ItemFlagsStack.pop_back();
8590 g.CurrentItemFlags = g.ItemFlagsStack.back();
8591}
8592
8593// BeginDisabled()/EndDisabled()
8594// - 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)
8595// - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.
8596// - Feedback welcome at https://github.com/ocornut/imgui/issues/211
8597// - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions.
8598// (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)
8599// - Note: mixing up BeginDisabled() and PushItemFlag(ImGuiItemFlags_Disabled) is currently NOT SUPPORTED.
8600void ImGui::BeginDisabled(bool disabled)
8601{
8602 ImGuiContext& g = *GImGui;
8603 bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
8604 if (!was_disabled && disabled)
8605 {
8606 g.DisabledAlphaBackup = g.Style.Alpha;
8607 g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha);
8608 }
8609 if (was_disabled || disabled)
8610 g.CurrentItemFlags |= ImGuiItemFlags_Disabled;
8611 g.ItemFlagsStack.push_back(g.CurrentItemFlags); // FIXME-OPT: can we simply skip this and use DisabledStackSize?
8612 g.DisabledStackSize++;
8613}
8614
8615void ImGui::EndDisabled()
8616{
8617 ImGuiContext& g = *GImGui;
8618 if (g.DisabledStackSize <= 0)
8619 {
8620 IM_ASSERT_USER_ERROR(0, "Calling EndDisabled() too many times!");
8621 return;
8622 }
8623 g.DisabledStackSize--;
8624 bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
8625 //PopItemFlag();
8626 g.ItemFlagsStack.pop_back();
8627 g.CurrentItemFlags = g.ItemFlagsStack.back();
8628 if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)
8629 g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar();
8630}
8631
8632// Could have been called BeginDisabledDisable() but it didn't want to be award nominated for most awkward function name.
8633// Ideally we would use a shared e.g. BeginDisabled()->BeginDisabledEx() but earlier needs to be optimal.
8634// The whole code for this is awkward, will reevaluate if we find a way to implement SetNextItemDisabled().
8635void ImGui::BeginDisabledOverrideReenable()
8636{
8637 ImGuiContext& g = *GImGui;
8638 IM_ASSERT(g.CurrentItemFlags & ImGuiItemFlags_Disabled);
8639 g.Style.Alpha = g.DisabledAlphaBackup;
8640 g.CurrentItemFlags &= ~ImGuiItemFlags_Disabled;
8641 g.ItemFlagsStack.push_back(g.CurrentItemFlags);
8642 g.DisabledStackSize++;
8643}
8644
8645void ImGui::EndDisabledOverrideReenable()
8646{
8647 ImGuiContext& g = *GImGui;
8648 g.DisabledStackSize--;
8649 IM_ASSERT(g.DisabledStackSize > 0);
8650 g.ItemFlagsStack.pop_back();
8651 g.CurrentItemFlags = g.ItemFlagsStack.back();
8652 g.Style.Alpha = g.DisabledAlphaBackup * g.Style.DisabledAlpha;
8653}
8654
8655void ImGui::PushTextWrapPos(float wrap_pos_x)
8656{
8657 ImGuiContext& g = *GImGui;
8658 ImGuiWindow* window = g.CurrentWindow;
8659 window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos);
8660 window->DC.TextWrapPos = wrap_pos_x;
8661}
8662
8663void ImGui::PopTextWrapPos()
8664{
8665 ImGuiContext& g = *GImGui;
8666 ImGuiWindow* window = g.CurrentWindow;
8667 if (window->DC.TextWrapPosStack.Size <= 0)
8668 {
8669 IM_ASSERT_USER_ERROR(0, "Calling PopTextWrapPos() too many times!");
8670 return;
8671 }
8672 window->DC.TextWrapPos = window->DC.TextWrapPosStack.back();
8673 window->DC.TextWrapPosStack.pop_back();
8674}
8675
8676static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy, bool dock_hierarchy)
8677{
8678 ImGuiWindow* last_window = NULL;
8679 while (last_window != window)
8680 {
8681 last_window = window;
8682 window = window->RootWindow;
8683 if (popup_hierarchy)
8684 window = window->RootWindowPopupTree;
8685 if (dock_hierarchy)
8686 window = window->RootWindowDockTree;
8687 }
8688 return window;
8689}
8690
8691bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy)
8692{
8693 ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy, dock_hierarchy);
8694 if (window_root == potential_parent)
8695 return true;
8696 while (window != NULL)
8697 {
8698 if (window == potential_parent)
8699 return true;
8700 if (window == window_root) // end of chain
8701 return false;
8702 window = window->ParentWindow;
8703 }
8704 return false;
8705}
8706
8707bool ImGui::IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
8708{
8709 if (window->RootWindow == potential_parent)
8710 return true;
8711 while (window != NULL)
8712 {
8713 if (window == potential_parent)
8714 return true;
8715 window = window->ParentWindowInBeginStack;
8716 }
8717 return false;
8718}
8719
8720bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)
8721{
8722 ImGuiContext& g = *GImGui;
8723
8724 // 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
8725 const int display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below);
8726 if (display_layer_delta != 0)
8727 return display_layer_delta > 0;
8728
8729 for (int i = g.Windows.Size - 1; i >= 0; i--)
8730 {
8731 ImGuiWindow* candidate_window = g.Windows[i];
8732 if (candidate_window == potential_above)
8733 return true;
8734 if (candidate_window == potential_below)
8735 return false;
8736 }
8737 return false;
8738}
8739
8740// Is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options.
8741// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
8742// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
8743// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
8744bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
8745{
8746 ImGuiContext& g = *GImGui;
8747 IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0, "Invalid flags for IsWindowHovered()!");
8748
8749 ImGuiWindow* ref_window = g.HoveredWindow;
8750 ImGuiWindow* cur_window = g.CurrentWindow;
8751 if (ref_window == NULL)
8752 return false;
8753
8754 if ((flags & ImGuiHoveredFlags_AnyWindow) == 0)
8755 {
8756 IM_ASSERT(cur_window); // Not inside a Begin()/End()
8757 const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0;
8758 const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0;
8759 if (flags & ImGuiHoveredFlags_RootWindow)
8760 cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy);
8761
8762 bool result;
8763 if (flags & ImGuiHoveredFlags_ChildWindows)
8764 result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy);
8765 else
8766 result = (ref_window == cur_window);
8767 if (!result)
8768 return false;
8769 }
8770
8771 if (!IsWindowContentHoverable(ref_window, flags))
8772 return false;
8773 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
8774 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId)
8775 return false;
8776
8777 // When changing hovered window we requires a bit of stationary delay before activating hover timer.
8778 // FIXME: We don't support delay other than stationary one for now, other delay would need a way
8779 // to fulfill the possibility that multiple IsWindowHovered() with varying flag could return true
8780 // for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache.
8781 // We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow.
8782 if (flags & ImGuiHoveredFlags_ForTooltip)
8783 flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);
8784 if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverWindowUnlockedStationaryId != ref_window->ID)
8785 return false;
8786
8787 return true;
8788}
8789
8790bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
8791{
8792 ImGuiContext& g = *GImGui;
8793 ImGuiWindow* ref_window = g.NavWindow;
8794 ImGuiWindow* cur_window = g.CurrentWindow;
8795
8796 if (ref_window == NULL)
8797 return false;
8798 if (flags & ImGuiFocusedFlags_AnyWindow)
8799 return true;
8800
8801 IM_ASSERT(cur_window); // Not inside a Begin()/End()
8802 const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0;
8803 const bool dock_hierarchy = (flags & ImGuiFocusedFlags_DockHierarchy) != 0;
8804 if (flags & ImGuiHoveredFlags_RootWindow)
8805 cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy);
8806
8807 if (flags & ImGuiHoveredFlags_ChildWindows)
8808 return IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy);
8809 else
8810 return (ref_window == cur_window);
8811}
8812
8813ImGuiID ImGui::GetWindowDockID()
8814{
8815 ImGuiContext& g = *GImGui;
8816 return g.CurrentWindow->DockId;
8817}
8818
8819bool ImGui::IsWindowDocked()
8820{
8821 ImGuiContext& g = *GImGui;
8822 return g.CurrentWindow->DockIsActive;
8823}
8824
8825// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
8826// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
8827// If you want a window to never be focused, you may use the e.g. NoInputs flag.
8828bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
8829{
8830 return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
8831}
8832
8833float ImGui::GetWindowWidth()
8834{
8835 ImGuiWindow* window = GImGui->CurrentWindow;
8836 return window->Size.x;
8837}
8838
8839float ImGui::GetWindowHeight()
8840{
8841 ImGuiWindow* window = GImGui->CurrentWindow;
8842 return window->Size.y;
8843}
8844
8845ImVec2 ImGui::GetWindowPos()
8846{
8847 ImGuiContext& g = *GImGui;
8848 ImGuiWindow* window = g.CurrentWindow;
8849 return window->Pos;
8850}
8851
8852void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
8853{
8854 // Test condition (NB: bit 0 is always true) and clear flags for next time
8855 if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
8856 return;
8857
8858 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8859 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8860 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
8861
8862 // Set
8863 const ImVec2 old_pos = window->Pos;
8864 window->Pos = ImTrunc(pos);
8865 ImVec2 offset = window->Pos - old_pos;
8866 if (offset.x == 0.0f && offset.y == 0.0f)
8867 return;
8868 MarkIniSettingsDirty(window);
8869 // FIXME: share code with TranslateWindow(), need to confirm whether the 3 rect modified by TranslateWindow() are desirable here.
8870 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
8871 window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
8872 window->DC.IdealMaxPos += offset;
8873 window->DC.CursorStartPos += offset;
8874}
8875
8876void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
8877{
8878 ImGuiWindow* window = GetCurrentWindowRead();
8879 SetWindowPos(window, pos, cond);
8880}
8881
8882void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
8883{
8884 if (ImGuiWindow* window = FindWindowByName(name))
8885 SetWindowPos(window, pos, cond);
8886}
8887
8888ImVec2 ImGui::GetWindowSize()
8889{
8890 ImGuiWindow* window = GetCurrentWindowRead();
8891 return window->Size;
8892}
8893
8894void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
8895{
8896 // Test condition (NB: bit 0 is always true) and clear flags for next time
8897 if (cond && (window->SetWindowSizeAllowFlags & 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->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8902
8903 // Enable auto-fit (not done in BeginChild() path unless appearing or combined with ImGuiChildFlags_AlwaysAutoResize)
8904 if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
8905 window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0;
8906 if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
8907 window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0;
8908
8909 // Set
8910 ImVec2 old_size = window->SizeFull;
8911 if (size.x <= 0.0f)
8912 window->AutoFitOnlyGrows = false;
8913 else
8914 window->SizeFull.x = IM_TRUNC(size.x);
8915 if (size.y <= 0.0f)
8916 window->AutoFitOnlyGrows = false;
8917 else
8918 window->SizeFull.y = IM_TRUNC(size.y);
8919 if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y)
8920 MarkIniSettingsDirty(window);
8921}
8922
8923void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
8924{
8925 SetWindowSize(GImGui->CurrentWindow, size, cond);
8926}
8927
8928void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
8929{
8930 if (ImGuiWindow* window = FindWindowByName(name))
8931 SetWindowSize(window, size, cond);
8932}
8933
8934void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
8935{
8936 // Test condition (NB: bit 0 is always true) and clear flags for next time
8937 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
8938 return;
8939 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8940
8941 // Set
8942 window->Collapsed = collapsed;
8943}
8944
8945void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
8946{
8947 IM_ASSERT(window->HitTestHoleSize.x == 0); // We don't support multiple holes/hit test filters
8948 window->HitTestHoleSize = ImVec2ih(size);
8949 window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
8950}
8951
8952void ImGui::SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window)
8953{
8954 window->Hidden = window->SkipItems = true;
8955 window->HiddenFramesCanSkipItems = 1;
8956}
8957
8958void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
8959{
8960 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
8961}
8962
8963bool ImGui::IsWindowCollapsed()
8964{
8965 ImGuiWindow* window = GetCurrentWindowRead();
8966 return window->Collapsed;
8967}
8968
8969bool ImGui::IsWindowAppearing()
8970{
8971 ImGuiWindow* window = GetCurrentWindowRead();
8972 return window->Appearing;
8973}
8974
8975void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
8976{
8977 if (ImGuiWindow* window = FindWindowByName(name))
8978 SetWindowCollapsed(window, collapsed, cond);
8979}
8980
8981void ImGui::SetWindowFocus()
8982{
8983 FocusWindow(GImGui->CurrentWindow);
8984}
8985
8986void ImGui::SetWindowFocus(const char* name)
8987{
8988 if (name)
8989 {
8990 if (ImGuiWindow* window = FindWindowByName(name))
8991 FocusWindow(window);
8992 }
8993 else
8994 {
8995 FocusWindow(NULL);
8996 }
8997}
8998
8999void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
9000{
9001 ImGuiContext& g = *GImGui;
9002 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
9003 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
9004 g.NextWindowData.PosVal = pos;
9005 g.NextWindowData.PosPivotVal = pivot;
9006 g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
9007 g.NextWindowData.PosUndock = true;
9008}
9009
9010void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
9011{
9012 ImGuiContext& g = *GImGui;
9013 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
9014 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
9015 g.NextWindowData.SizeVal = size;
9016 g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
9017}
9018
9019// For each axis:
9020// - 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.
9021// - Use -1 for both min and max of same axis to preserve current size which itself is a constraint.
9022// - See "Demo->Examples->Constrained-resizing window" for examples.
9023void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
9024{
9025 ImGuiContext& g = *GImGui;
9026 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
9027 g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
9028 g.NextWindowData.SizeCallback = custom_callback;
9029 g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
9030}
9031
9032// Content size = inner scrollable rectangle, padded with WindowPadding.
9033// SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
9034void ImGui::SetNextWindowContentSize(const ImVec2& size)
9035{
9036 ImGuiContext& g = *GImGui;
9037 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
9038 g.NextWindowData.ContentSizeVal = ImTrunc(size);
9039}
9040
9041void ImGui::SetNextWindowScroll(const ImVec2& scroll)
9042{
9043 ImGuiContext& g = *GImGui;
9044 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll;
9045 g.NextWindowData.ScrollVal = scroll;
9046}
9047
9048void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
9049{
9050 ImGuiContext& g = *GImGui;
9051 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
9052 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
9053 g.NextWindowData.CollapsedVal = collapsed;
9054 g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
9055}
9056
9057void ImGui::SetNextWindowFocus()
9058{
9059 ImGuiContext& g = *GImGui;
9060 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
9061}
9062
9063void ImGui::SetNextWindowBgAlpha(float alpha)
9064{
9065 ImGuiContext& g = *GImGui;
9066 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
9067 g.NextWindowData.BgAlphaVal = alpha;
9068}
9069
9070void ImGui::SetNextWindowViewport(ImGuiID id)
9071{
9072 ImGuiContext& g = *GImGui;
9073 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasViewport;
9074 g.NextWindowData.ViewportId = id;
9075}
9076
9077void ImGui::SetNextWindowDockID(ImGuiID id, ImGuiCond cond)
9078{
9079 ImGuiContext& g = *GImGui;
9080 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasDock;
9081 g.NextWindowData.DockCond = cond ? cond : ImGuiCond_Always;
9082 g.NextWindowData.DockId = id;
9083}
9084
9085void ImGui::SetNextWindowClass(const ImGuiWindowClass* window_class)
9086{
9087 ImGuiContext& g = *GImGui;
9088 IM_ASSERT((window_class->ViewportFlagsOverrideSet & window_class->ViewportFlagsOverrideClear) == 0); // Cannot set both set and clear for the same bit
9089 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasWindowClass;
9090 g.NextWindowData.WindowClass = *window_class;
9091}
9092
9093// This is experimental and meant to be a toy for exploring a future/wider range of features.
9094void ImGui::SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags)
9095{
9096 ImGuiContext& g = *GImGui;
9097 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasRefreshPolicy;
9098 g.NextWindowData.RefreshFlagsVal = flags;
9099}
9100
9101ImDrawList* ImGui::GetWindowDrawList()
9102{
9103 ImGuiWindow* window = GetCurrentWindow();
9104 return window->DrawList;
9105}
9106
9107float ImGui::GetWindowDpiScale()
9108{
9109 ImGuiContext& g = *GImGui;
9110 return g.CurrentDpiScale;
9111}
9112
9113ImGuiViewport* ImGui::GetWindowViewport()
9114{
9115 ImGuiContext& g = *GImGui;
9116 IM_ASSERT(g.CurrentViewport != NULL && g.CurrentViewport == g.CurrentWindow->Viewport);
9117 return g.CurrentViewport;
9118}
9119
9120ImFont* ImGui::GetFont()
9121{
9122 return GImGui->Font;
9123}
9124
9125float ImGui::GetFontSize()
9126{
9127 return GImGui->FontSize;
9128}
9129
9130ImVec2 ImGui::GetFontTexUvWhitePixel()
9131{
9132 return GImGui->DrawListSharedData.TexUvWhitePixel;
9133}
9134
9135void ImGui::SetWindowFontScale(float scale)
9136{
9137 IM_ASSERT(scale > 0.0f);
9138 ImGuiContext& g = *GImGui;
9139 ImGuiWindow* window = GetCurrentWindow();
9140 window->FontWindowScale = scale;
9141 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
9142 g.FontScale = g.DrawListSharedData.FontScale = g.FontSize / g.Font->FontSize;
9143}
9144
9145void ImGui::PushFocusScope(ImGuiID id)
9146{
9147 ImGuiContext& g = *GImGui;
9148 ImGuiFocusScopeData data;
9149 data.ID = id;
9150 data.WindowID = g.CurrentWindow->ID;
9151 g.FocusScopeStack.push_back(data);
9152 g.CurrentFocusScopeId = id;
9153}
9154
9155void ImGui::PopFocusScope()
9156{
9157 ImGuiContext& g = *GImGui;
9158 if (g.FocusScopeStack.Size <= g.StackSizesInBeginForCurrentWindow->SizeOfFocusScopeStack)
9159 {
9160 IM_ASSERT_USER_ERROR(0, "Calling PopFocusScope() too many times!");
9161 return;
9162 }
9163 g.FocusScopeStack.pop_back();
9164 g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back().ID : 0;
9165}
9166
9167void ImGui::SetNavFocusScope(ImGuiID focus_scope_id)
9168{
9169 ImGuiContext& g = *GImGui;
9170 g.NavFocusScopeId = focus_scope_id;
9171 g.NavFocusRoute.resize(0); // Invalidate
9172 if (focus_scope_id == 0)
9173 return;
9174 IM_ASSERT(g.NavWindow != NULL);
9175
9176 // Store current path (in reverse order)
9177 if (focus_scope_id == g.CurrentFocusScopeId)
9178 {
9179 // Top of focus stack contains local focus scopes inside current window
9180 for (int n = g.FocusScopeStack.Size - 1; n >= 0 && g.FocusScopeStack.Data[n].WindowID == g.CurrentWindow->ID; n--)
9181 g.NavFocusRoute.push_back(g.FocusScopeStack.Data[n]);
9182 }
9183 else if (focus_scope_id == g.NavWindow->NavRootFocusScopeId)
9184 g.NavFocusRoute.push_back({ focus_scope_id, g.NavWindow->ID });
9185 else
9186 return;
9187
9188 // Then follow on manually set ParentWindowForFocusRoute field (#6798)
9189 for (ImGuiWindow* window = g.NavWindow->ParentWindowForFocusRoute; window != NULL; window = window->ParentWindowForFocusRoute)
9190 g.NavFocusRoute.push_back({ window->NavRootFocusScopeId, window->ID });
9191 IM_ASSERT(g.NavFocusRoute.Size < 100); // Maximum depth is technically 251 as per CalcRoutingScore(): 254 - 3
9192}
9193
9194// Focus = move navigation cursor, set scrolling, set focus window.
9195void ImGui::FocusItem()
9196{
9197 ImGuiContext& g = *GImGui;
9198 ImGuiWindow* window = g.CurrentWindow;
9199 IMGUI_DEBUG_LOG_FOCUS("FocusItem(0x%08x) in window \"%s\"\n", g.LastItemData.ID, window->Name);
9200 if (g.DragDropActive || g.MovingWindow != NULL) // FIXME: Opt-in flags for this?
9201 {
9202 IMGUI_DEBUG_LOG_FOCUS("FocusItem() ignored while DragDropActive!\n");
9203 return;
9204 }
9205
9206 ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible | ImGuiNavMoveFlags_NoSelect;
9207 ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
9208 SetNavWindow(window);
9209 NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_Up, move_flags, scroll_flags);
9210 NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);
9211}
9212
9213void ImGui::ActivateItemByID(ImGuiID id)
9214{
9215 ImGuiContext& g = *GImGui;
9216 g.NavNextActivateId = id;
9217 g.NavNextActivateFlags = ImGuiActivateFlags_None;
9218}
9219
9220// Note: this will likely be called ActivateItem() once we rework our Focus/Activation system!
9221// But ActivateItem() should function without altering scroll/focus?
9222void ImGui::SetKeyboardFocusHere(int offset)
9223{
9224 ImGuiContext& g = *GImGui;
9225 ImGuiWindow* window = g.CurrentWindow;
9226 IM_ASSERT(offset >= -1); // -1 is allowed but not below
9227 IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name);
9228
9229 // It makes sense in the vast majority of cases to never interrupt a drag and drop.
9230 // When we refactor this function into ActivateItem() we may want to make this an option.
9231 // MovingWindow is protected from most user inputs using SetActiveIdUsingNavAndKeys(), but
9232 // is also automatically dropped in the event g.ActiveId is stolen.
9233 if (g.DragDropActive || g.MovingWindow != NULL)
9234 {
9235 IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere() ignored while DragDropActive!\n");
9236 return;
9237 }
9238
9239 SetNavWindow(window);
9240
9241 ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible;
9242 ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
9243 NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
9244 if (offset == -1)
9245 {
9246 NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);
9247 }
9248 else
9249 {
9250 g.NavTabbingDir = 1;
9251 g.NavTabbingCounter = offset + 1;
9252 }
9253}
9254
9255void ImGui::SetItemDefaultFocus()
9256{
9257 ImGuiContext& g = *GImGui;
9258 ImGuiWindow* window = g.CurrentWindow;
9259 if (!window->Appearing)
9260 return;
9261 if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResult.ID == 0) || g.NavLayer != window->DC.NavLayerCurrent)
9262 return;
9263
9264 g.NavInitRequest = false;
9265 NavApplyItemToResult(&g.NavInitResult);
9266 NavUpdateAnyRequestFlag();
9267
9268 // Scroll could be done in NavInitRequestApplyResult() via an opt-in flag (we however don't want regular init requests to scroll)
9269 if (!window->ClipRect.Contains(g.LastItemData.Rect))
9270 ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None);
9271}
9272
9273void ImGui::SetStateStorage(ImGuiStorage* tree)
9274{
9275 ImGuiWindow* window = GImGui->CurrentWindow;
9276 window->DC.StateStorage = tree ? tree : &window->StateStorage;
9277}
9278
9279ImGuiStorage* ImGui::GetStateStorage()
9280{
9281 ImGuiWindow* window = GImGui->CurrentWindow;
9282 return window->DC.StateStorage;
9283}
9284
9285bool ImGui::IsRectVisible(const ImVec2& size)
9286{
9287 ImGuiWindow* window = GImGui->CurrentWindow;
9288 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
9289}
9290
9291bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
9292{
9293 ImGuiWindow* window = GImGui->CurrentWindow;
9294 return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
9295}
9296
9297//-----------------------------------------------------------------------------
9298// [SECTION] ID STACK
9299//-----------------------------------------------------------------------------
9300
9301// This is one of the very rare legacy case where we use ImGuiWindow methods,
9302// it should ideally be flattened at some point but it's been used a lots by widgets.
9303IM_MSVC_RUNTIME_CHECKS_OFF
9304ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
9305{
9306 ImGuiID seed = IDStack.back();
9307 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
9308#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9309 ImGuiContext& g = *Ctx;
9310 if (g.DebugHookIdInfo == id)
9311 ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
9312#endif
9313 return id;
9314}
9315
9316ImGuiID ImGuiWindow::GetID(const void* ptr)
9317{
9318 ImGuiID seed = IDStack.back();
9319 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
9320#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9321 ImGuiContext& g = *Ctx;
9322 if (g.DebugHookIdInfo == id)
9323 ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
9324#endif
9325 return id;
9326}
9327
9328ImGuiID ImGuiWindow::GetID(int n)
9329{
9330 ImGuiID seed = IDStack.back();
9331 ImGuiID id = ImHashData(&n, sizeof(n), seed);
9332#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9333 ImGuiContext& g = *Ctx;
9334 if (g.DebugHookIdInfo == id)
9335 ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
9336#endif
9337 return id;
9338}
9339
9340// This is only used in rare/specific situations to manufacture an ID out of nowhere.
9341// FIXME: Consider instead storing last non-zero ID + count of successive zero-ID, and combine those?
9342ImGuiID ImGuiWindow::GetIDFromPos(const ImVec2& p_abs)
9343{
9344 ImGuiID seed = IDStack.back();
9345 ImVec2 p_rel = ImGui::WindowPosAbsToRel(this, p_abs);
9346 ImGuiID id = ImHashData(&p_rel, sizeof(p_rel), seed);
9347 return id;
9348}
9349
9350// "
9351ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
9352{
9353 ImGuiID seed = IDStack.back();
9354 ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs);
9355 ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
9356 return id;
9357}
9358
9359void ImGui::PushID(const char* str_id)
9360{
9361 ImGuiContext& g = *GImGui;
9362 ImGuiWindow* window = g.CurrentWindow;
9363 ImGuiID id = window->GetID(str_id);
9364 window->IDStack.push_back(id);
9365}
9366
9367void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
9368{
9369 ImGuiContext& g = *GImGui;
9370 ImGuiWindow* window = g.CurrentWindow;
9371 ImGuiID id = window->GetID(str_id_begin, str_id_end);
9372 window->IDStack.push_back(id);
9373}
9374
9375void ImGui::PushID(const void* ptr_id)
9376{
9377 ImGuiContext& g = *GImGui;
9378 ImGuiWindow* window = g.CurrentWindow;
9379 ImGuiID id = window->GetID(ptr_id);
9380 window->IDStack.push_back(id);
9381}
9382
9383void ImGui::PushID(int int_id)
9384{
9385 ImGuiContext& g = *GImGui;
9386 ImGuiWindow* window = g.CurrentWindow;
9387 ImGuiID id = window->GetID(int_id);
9388 window->IDStack.push_back(id);
9389}
9390
9391// Push a given id value ignoring the ID stack as a seed.
9392void ImGui::PushOverrideID(ImGuiID id)
9393{
9394 ImGuiContext& g = *GImGui;
9395 ImGuiWindow* window = g.CurrentWindow;
9396#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9397 if (g.DebugHookIdInfo == id)
9398 DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL);
9399#endif
9400 window->IDStack.push_back(id);
9401}
9402
9403// Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call
9404// (note that when using this pattern, ID Stack Tool will tend to not display the intermediate stack level.
9405// for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more)
9406ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
9407{
9408 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
9409#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9410 ImGuiContext& g = *GImGui;
9411 if (g.DebugHookIdInfo == id)
9412 DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
9413#endif
9414 return id;
9415}
9416
9417ImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed)
9418{
9419 ImGuiID id = ImHashData(&n, sizeof(n), seed);
9420#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9421 ImGuiContext& g = *GImGui;
9422 if (g.DebugHookIdInfo == id)
9423 DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
9424#endif
9425 return id;
9426}
9427
9428void ImGui::PopID()
9429{
9430 ImGuiWindow* window = GImGui->CurrentWindow;
9431 if (window->IDStack.Size <= 1)
9432 {
9433 IM_ASSERT_USER_ERROR(0, "Calling PopID() too many times!");
9434 return;
9435 }
9436 window->IDStack.pop_back();
9437}
9438
9439ImGuiID ImGui::GetID(const char* str_id)
9440{
9441 ImGuiWindow* window = GImGui->CurrentWindow;
9442 return window->GetID(str_id);
9443}
9444
9445ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
9446{
9447 ImGuiWindow* window = GImGui->CurrentWindow;
9448 return window->GetID(str_id_begin, str_id_end);
9449}
9450
9451ImGuiID ImGui::GetID(const void* ptr_id)
9452{
9453 ImGuiWindow* window = GImGui->CurrentWindow;
9454 return window->GetID(ptr_id);
9455}
9456
9457ImGuiID ImGui::GetID(int int_id)
9458{
9459 ImGuiWindow* window = GImGui->CurrentWindow;
9460 return window->GetID(int_id);
9461}
9462IM_MSVC_RUNTIME_CHECKS_RESTORE
9463
9464//-----------------------------------------------------------------------------
9465// [SECTION] INPUTS
9466//-----------------------------------------------------------------------------
9467// - GetModForLRModKey() [Internal]
9468// - FixupKeyChord() [Internal]
9469// - GetKeyData() [Internal]
9470// - GetKeyIndex() [Internal]
9471// - GetKeyName()
9472// - GetKeyChordName() [Internal]
9473// - CalcTypematicRepeatAmount() [Internal]
9474// - GetTypematicRepeatRate() [Internal]
9475// - GetKeyPressedAmount() [Internal]
9476// - GetKeyMagnitude2d() [Internal]
9477//-----------------------------------------------------------------------------
9478// - UpdateKeyRoutingTable() [Internal]
9479// - GetRoutingIdFromOwnerId() [Internal]
9480// - GetShortcutRoutingData() [Internal]
9481// - CalcRoutingScore() [Internal]
9482// - SetShortcutRouting() [Internal]
9483// - TestShortcutRouting() [Internal]
9484//-----------------------------------------------------------------------------
9485// - IsKeyDown()
9486// - IsKeyPressed()
9487// - IsKeyReleased()
9488//-----------------------------------------------------------------------------
9489// - IsMouseDown()
9490// - IsMouseClicked()
9491// - IsMouseReleased()
9492// - IsMouseDoubleClicked()
9493// - GetMouseClickedCount()
9494// - IsMouseHoveringRect() [Internal]
9495// - IsMouseDragPastThreshold() [Internal]
9496// - IsMouseDragging()
9497// - GetMousePos()
9498// - SetMousePos() [Internal]
9499// - GetMousePosOnOpeningCurrentPopup()
9500// - IsMousePosValid()
9501// - IsAnyMouseDown()
9502// - GetMouseDragDelta()
9503// - ResetMouseDragDelta()
9504// - GetMouseCursor()
9505// - SetMouseCursor()
9506//-----------------------------------------------------------------------------
9507// - UpdateAliasKey()
9508// - GetMergedModsFromKeys()
9509// - UpdateKeyboardInputs()
9510// - UpdateMouseInputs()
9511//-----------------------------------------------------------------------------
9512// - LockWheelingWindow [Internal]
9513// - FindBestWheelingWindow [Internal]
9514// - UpdateMouseWheel() [Internal]
9515//-----------------------------------------------------------------------------
9516// - SetNextFrameWantCaptureKeyboard()
9517// - SetNextFrameWantCaptureMouse()
9518//-----------------------------------------------------------------------------
9519// - GetInputSourceName() [Internal]
9520// - DebugPrintInputEvent() [Internal]
9521// - UpdateInputEvents() [Internal]
9522//-----------------------------------------------------------------------------
9523// - GetKeyOwner() [Internal]
9524// - TestKeyOwner() [Internal]
9525// - SetKeyOwner() [Internal]
9526// - SetItemKeyOwner() [Internal]
9527// - Shortcut() [Internal]
9528//-----------------------------------------------------------------------------
9529
9530static ImGuiKeyChord GetModForLRModKey(ImGuiKey key)
9531{
9532 if (key == ImGuiKey_LeftCtrl || key == ImGuiKey_RightCtrl)
9533 return ImGuiMod_Ctrl;
9534 if (key == ImGuiKey_LeftShift || key == ImGuiKey_RightShift)
9535 return ImGuiMod_Shift;
9536 if (key == ImGuiKey_LeftAlt || key == ImGuiKey_RightAlt)
9537 return ImGuiMod_Alt;
9538 if (key == ImGuiKey_LeftSuper || key == ImGuiKey_RightSuper)
9539 return ImGuiMod_Super;
9540 return ImGuiMod_None;
9541}
9542
9543ImGuiKeyChord ImGui::FixupKeyChord(ImGuiKeyChord key_chord)
9544{
9545 // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
9546 ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9547 if (IsLRModKey(key))
9548 key_chord |= GetModForLRModKey(key);
9549 return key_chord;
9550}
9551
9552ImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key)
9553{
9554 ImGuiContext& g = *ctx;
9555
9556 // Special storage location for mods
9557 if (key & ImGuiMod_Mask_)
9558 key = ConvertSingleModFlagToKey(key);
9559
9560#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
9561 IM_ASSERT(key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_NamedKey_END);
9562 if (IsLegacyKey(key) && g.IO.KeyMap[key] != -1)
9563 key = (ImGuiKey)g.IO.KeyMap[key]; // Remap native->imgui or imgui->native
9564#else
9565 IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code.");
9566#endif
9567 return &g.IO.KeysData[key - ImGuiKey_KeysData_OFFSET];
9568}
9569
9570#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
9571// Formally moved to obsolete section in 1.90.5 in spite of documented as obsolete since 1.87
9572ImGuiKey ImGui::GetKeyIndex(ImGuiKey key)
9573{
9574 ImGuiContext& g = *GImGui;
9575 IM_ASSERT(IsNamedKey(key));
9576 const ImGuiKeyData* key_data = GetKeyData(key);
9577 return (ImGuiKey)(key_data - g.IO.KeysData);
9578}
9579#endif
9580
9581// Those names a provided for debugging purpose and are not meant to be saved persistently not compared.
9582static const char* const GKeyNames[] =
9583{
9584 "Tab", "LeftArrow", "RightArrow", "UpArrow", "DownArrow", "PageUp", "PageDown",
9585 "Home", "End", "Insert", "Delete", "Backspace", "Space", "Enter", "Escape",
9586 "LeftCtrl", "LeftShift", "LeftAlt", "LeftSuper", "RightCtrl", "RightShift", "RightAlt", "RightSuper", "Menu",
9587 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H",
9588 "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
9589 "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
9590 "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24",
9591 "Apostrophe", "Comma", "Minus", "Period", "Slash", "Semicolon", "Equal", "LeftBracket",
9592 "Backslash", "RightBracket", "GraveAccent", "CapsLock", "ScrollLock", "NumLock", "PrintScreen",
9593 "Pause", "Keypad0", "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6",
9594 "Keypad7", "Keypad8", "Keypad9", "KeypadDecimal", "KeypadDivide", "KeypadMultiply",
9595 "KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual",
9596 "AppBack", "AppForward",
9597 "GamepadStart", "GamepadBack",
9598 "GamepadFaceLeft", "GamepadFaceRight", "GamepadFaceUp", "GamepadFaceDown",
9599 "GamepadDpadLeft", "GamepadDpadRight", "GamepadDpadUp", "GamepadDpadDown",
9600 "GamepadL1", "GamepadR1", "GamepadL2", "GamepadR2", "GamepadL3", "GamepadR3",
9601 "GamepadLStickLeft", "GamepadLStickRight", "GamepadLStickUp", "GamepadLStickDown",
9602 "GamepadRStickLeft", "GamepadRStickRight", "GamepadRStickUp", "GamepadRStickDown",
9603 "MouseLeft", "MouseRight", "MouseMiddle", "MouseX1", "MouseX2", "MouseWheelX", "MouseWheelY",
9604 "ModCtrl", "ModShift", "ModAlt", "ModSuper", // ReservedForModXXX are showing the ModXXX names.
9605};
9606IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames));
9607
9608const char* ImGui::GetKeyName(ImGuiKey key)
9609{
9610 if (key == ImGuiKey_None)
9611 return "None";
9612#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
9613 IM_ASSERT(IsNamedKeyOrMod(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code.");
9614#else
9615 ImGuiContext& g = *GImGui;
9616 if (IsLegacyKey(key))
9617 {
9618 if (g.IO.KeyMap[key] == -1)
9619 return "N/A";
9620 IM_ASSERT(IsNamedKey((ImGuiKey)g.IO.KeyMap[key]));
9621 key = (ImGuiKey)g.IO.KeyMap[key];
9622 }
9623#endif
9624 if (key & ImGuiMod_Mask_)
9625 key = ConvertSingleModFlagToKey(key);
9626 if (!IsNamedKey(key))
9627 return "Unknown";
9628
9629 return GKeyNames[key - ImGuiKey_NamedKey_BEGIN];
9630}
9631
9632// Return untranslated names: on macOS, Cmd key will show as Ctrl, Ctrl key will show as super.
9633// Lifetime of return value: valid until next call to same function.
9634const char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord)
9635{
9636 ImGuiContext& g = *GImGui;
9637
9638 const ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9639 if (IsLRModKey(key))
9640 key_chord &= ~GetModForLRModKey(key); // Return "Ctrl+LeftShift" instead of "Ctrl+Shift+LeftShift"
9641 ImFormatString(g.TempKeychordName, IM_ARRAYSIZE(g.TempKeychordName), "%s%s%s%s%s",
9642 (key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "",
9643 (key_chord & ImGuiMod_Shift) ? "Shift+" : "",
9644 (key_chord & ImGuiMod_Alt) ? "Alt+" : "",
9645 (key_chord & ImGuiMod_Super) ? "Super+" : "",
9646 (key != ImGuiKey_None || key_chord == ImGuiKey_None) ? GetKeyName(key) : "");
9647 size_t len;
9648 if (key == ImGuiKey_None && key_chord != 0)
9649 if ((len = strlen(g.TempKeychordName)) != 0) // Remove trailing '+'
9650 g.TempKeychordName[len - 1] = 0;
9651 return g.TempKeychordName;
9652}
9653
9654// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
9655// t1 = current time (e.g.: g.Time)
9656// An event is triggered at:
9657// t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N
9658int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
9659{
9660 if (t1 == 0.0f)
9661 return 1;
9662 if (t0 >= t1)
9663 return 0;
9664 if (repeat_rate <= 0.0f)
9665 return (t0 < repeat_delay) && (t1 >= repeat_delay);
9666 const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
9667 const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
9668 const int count = count_t1 - count_t0;
9669 return count;
9670}
9671
9672void ImGui::GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate)
9673{
9674 ImGuiContext& g = *GImGui;
9675 switch (flags & ImGuiInputFlags_RepeatRateMask_)
9676 {
9677 case ImGuiInputFlags_RepeatRateNavMove: *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.80f; return;
9678 case ImGuiInputFlags_RepeatRateNavTweak: *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.30f; return;
9679 case ImGuiInputFlags_RepeatRateDefault: default: *repeat_delay = g.IO.KeyRepeatDelay * 1.00f; *repeat_rate = g.IO.KeyRepeatRate * 1.00f; return;
9680 }
9681}
9682
9683// Return value representing the number of presses in the last time period, for the given repeat rate
9684// (most often returns 0 or 1. The result is generally only >1 when RepeatRate is smaller than DeltaTime, aka large DeltaTime or fast RepeatRate)
9685int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_rate)
9686{
9687 ImGuiContext& g = *GImGui;
9688 const ImGuiKeyData* key_data = GetKeyData(key);
9689 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)
9690 return 0;
9691 const float t = key_data->DownDuration;
9692 return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
9693}
9694
9695// Return 2D vector representing the combination of four cardinal direction, with analog value support (for e.g. ImGuiKey_GamepadLStick* values).
9696ImVec2 ImGui::GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down)
9697{
9698 return ImVec2(
9699 GetKeyData(key_right)->AnalogValue - GetKeyData(key_left)->AnalogValue,
9700 GetKeyData(key_down)->AnalogValue - GetKeyData(key_up)->AnalogValue);
9701}
9702
9703// Rewrite routing data buffers to strip old entries + sort by key to make queries not touch scattered data.
9704// Entries D,A,B,B,A,C,B --> A,A,B,B,B,C,D
9705// Index A:1 B:2 C:5 D:0 --> A:0 B:2 C:5 D:6
9706// See 'Metrics->Key Owners & Shortcut Routing' to visualize the result of that operation.
9707static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt)
9708{
9709 ImGuiContext& g = *GImGui;
9710 rt->EntriesNext.resize(0);
9711 for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
9712 {
9713 const int new_routing_start_idx = rt->EntriesNext.Size;
9714 ImGuiKeyRoutingData* routing_entry;
9715 for (int old_routing_idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; old_routing_idx != -1; old_routing_idx = routing_entry->NextEntryIndex)
9716 {
9717 routing_entry = &rt->Entries[old_routing_idx];
9718 routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore;
9719 routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry
9720 routing_entry->RoutingNext = ImGuiKeyOwner_NoOwner;
9721 routing_entry->RoutingNextScore = 255;
9722 if (routing_entry->RoutingCurr == ImGuiKeyOwner_NoOwner)
9723 continue;
9724 rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer
9725
9726 // Apply routing to owner if there's no owner already (RoutingCurr == None at this point)
9727 // This is the result of previous frame's SetShortcutRouting() call.
9728 if (routing_entry->Mods == g.IO.KeyMods)
9729 {
9730 ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
9731 if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)
9732 {
9733 owner_data->OwnerCurr = routing_entry->RoutingCurr;
9734 //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X) via Routing\n", GetKeyName(key), routing_entry->RoutingCurr);
9735 }
9736 }
9737 }
9738
9739 // Rewrite linked-list
9740 rt->Index[key - ImGuiKey_NamedKey_BEGIN] = (ImGuiKeyRoutingIndex)(new_routing_start_idx < rt->EntriesNext.Size ? new_routing_start_idx : -1);
9741 for (int n = new_routing_start_idx; n < rt->EntriesNext.Size; n++)
9742 rt->EntriesNext[n].NextEntryIndex = (ImGuiKeyRoutingIndex)((n + 1 < rt->EntriesNext.Size) ? n + 1 : -1);
9743 }
9744 rt->Entries.swap(rt->EntriesNext); // Swap new and old indexes
9745}
9746
9747// owner_id may be None/Any, but routing_id needs to be always be set, so we default to GetCurrentFocusScope().
9748static inline ImGuiID GetRoutingIdFromOwnerId(ImGuiID owner_id)
9749{
9750 ImGuiContext& g = *GImGui;
9751 return (owner_id != ImGuiKeyOwner_NoOwner && owner_id != ImGuiKeyOwner_Any) ? owner_id : g.CurrentFocusScopeId;
9752}
9753
9754ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord)
9755{
9756 // Majority of shortcuts will be Key + any number of Mods
9757 // We accept _Single_ mod with ImGuiKey_None.
9758 // - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl); // Legal
9759 // - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl | ImGuiMod_Shift); // Legal
9760 // - Shortcut(ImGuiMod_Ctrl); // Legal
9761 // - Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift); // Not legal
9762 ImGuiContext& g = *GImGui;
9763 ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
9764 ImGuiKeyRoutingData* routing_data;
9765 ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9766 ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
9767 if (key == ImGuiKey_None)
9768 key = ConvertSingleModFlagToKey(mods);
9769 IM_ASSERT(IsNamedKey(key));
9770
9771 // Get (in the majority of case, the linked list will have one element so this should be 2 reads.
9772 // Subsequent elements will be contiguous in memory as list is sorted/rebuilt in NewFrame).
9773 for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; idx = routing_data->NextEntryIndex)
9774 {
9775 routing_data = &rt->Entries[idx];
9776 if (routing_data->Mods == mods)
9777 return routing_data;
9778 }
9779
9780 // Add to linked-list
9781 ImGuiKeyRoutingIndex routing_data_idx = (ImGuiKeyRoutingIndex)rt->Entries.Size;
9782 rt->Entries.push_back(ImGuiKeyRoutingData());
9783 routing_data = &rt->Entries[routing_data_idx];
9784 routing_data->Mods = (ImU16)mods;
9785 routing_data->NextEntryIndex = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; // Setup linked list
9786 rt->Index[key - ImGuiKey_NamedKey_BEGIN] = routing_data_idx;
9787 return routing_data;
9788}
9789
9790// Current score encoding (lower is highest priority):
9791// - 0: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive
9792// - 1: ImGuiInputFlags_ActiveItem or ImGuiInputFlags_RouteFocused (if item active)
9793// - 2: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused
9794// - 3+: ImGuiInputFlags_RouteFocused (if window in focus-stack)
9795// - 254: ImGuiInputFlags_RouteGlobal
9796// - 255: never route
9797// 'flags' should include an explicit routing policy
9798static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags)
9799{
9800 ImGuiContext& g = *GImGui;
9801 if (flags & ImGuiInputFlags_RouteFocused)
9802 {
9803 // ActiveID gets top priority
9804 // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it)
9805 if (owner_id != 0 && g.ActiveId == owner_id)
9806 return 1;
9807
9808 // Score based on distance to focused window (lower is better)
9809 // Assuming both windows are submitting a routing request,
9810 // - When Window....... is focused -> Window scores 3 (best), Window/ChildB scores 255 (no match)
9811 // - When Window/ChildB is focused -> Window scores 4, Window/ChildB scores 3 (best)
9812 // Assuming only WindowA is submitting a routing request,
9813 // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score.
9814 // This essentially follow the window->ParentWindowForFocusRoute chain.
9815 if (focus_scope_id == 0)
9816 return 255;
9817 for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++)
9818 if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id)
9819 return 3 + index_in_focus_path;
9820 return 255;
9821 }
9822 else if (flags & ImGuiInputFlags_RouteActive)
9823 {
9824 if (owner_id != 0 && g.ActiveId == owner_id)
9825 return 1;
9826 return 255;
9827 }
9828 else if (flags & ImGuiInputFlags_RouteGlobal)
9829 {
9830 if (flags & ImGuiInputFlags_RouteOverActive)
9831 return 0;
9832 if (flags & ImGuiInputFlags_RouteOverFocused)
9833 return 2;
9834 return 254;
9835 }
9836 IM_ASSERT(0);
9837 return 0;
9838}
9839
9840// - We need this to filter some Shortcut() routes when an item e.g. an InputText() is active
9841// e.g. ImGuiKey_G won't be considered a shortcut when item is active, but ImGuiMod|ImGuiKey_G can be.
9842// - 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.
9843static bool IsKeyChordPotentiallyCharInput(ImGuiKeyChord key_chord)
9844{
9845 // Mimic 'ignore_char_inputs' logic in InputText()
9846 ImGuiContext& g = *GImGui;
9847
9848 // When the right mods are pressed it cannot be a char input so we won't filter the shortcut out.
9849 ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
9850 const bool ignore_char_inputs = ((mods & ImGuiMod_Ctrl) && !(mods & ImGuiMod_Alt)) || (g.IO.ConfigMacOSXBehaviors && (mods & ImGuiMod_Ctrl));
9851 if (ignore_char_inputs)
9852 return false;
9853
9854 // Return true for A-Z, 0-9 and other keys associated to char inputs. Other keys such as F1-F12 won't be filtered.
9855 ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9856 if (key == ImGuiKey_None)
9857 return false;
9858 return g.KeysMayBeCharInput.TestBit(key);
9859}
9860
9861// Request a desired route for an input chord (key + mods).
9862// Return true if the route is available this frame.
9863// - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state.
9864// (Conceptually this does a "Submit for next frame" + "Test for current frame".
9865// As such, it could be called TrySetXXX or SubmitXXX, or the Submit and Test operations should be separate.)
9866bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
9867{
9868 ImGuiContext& g = *GImGui;
9869 if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
9870 flags |= ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut()
9871 else
9872 IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteTypeMask_)); // Check that only 1 routing flag is used
9873 IM_ASSERT(owner_id != ImGuiKeyOwner_Any && owner_id != ImGuiKeyOwner_NoOwner);
9874 if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused))
9875 IM_ASSERT(flags & ImGuiInputFlags_RouteGlobal);
9876
9877 // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
9878 key_chord = FixupKeyChord(key_chord);
9879
9880 // [DEBUG] Debug break requested by user
9881 if (g.DebugBreakInShortcutRouting == key_chord)
9882 IM_DEBUG_BREAK();
9883
9884 if (flags & ImGuiInputFlags_RouteUnlessBgFocused)
9885 if (g.NavWindow == NULL)
9886 return false;
9887
9888 // Note how ImGuiInputFlags_RouteAlways won't set routing and thus won't set owner. May want to rework this?
9889 if (flags & ImGuiInputFlags_RouteAlways)
9890 {
9891 IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> always, no register\n", GetKeyChordName(key_chord), flags, owner_id);
9892 return true;
9893 }
9894
9895 // Specific culling when there's an active item.
9896 if (g.ActiveId != 0 && g.ActiveId != owner_id)
9897 {
9898 if (flags & ImGuiInputFlags_RouteActive)
9899 return false;
9900
9901 // Cull shortcuts with no modifiers when it could generate a character.
9902 // e.g. Shortcut(ImGuiKey_G) also generates 'g' character, should not trigger when InputText() is active.
9903 // but Shortcut(Ctrl+G) should generally trigger when InputText() is active.
9904 // TL;DR: lettered shortcut with no mods or with only Alt mod will not trigger while an item reading text input is active.
9905 // (We cannot filter based on io.InputQueueCharacters[] contents because of trickling and key<>chars submission order are undefined)
9906 if (g.IO.WantTextInput && IsKeyChordPotentiallyCharInput(key_chord))
9907 {
9908 IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> filtered as potential char input\n", GetKeyChordName(key_chord), flags, owner_id);
9909 return false;
9910 }
9911
9912 // ActiveIdUsingAllKeyboardKeys trumps all for ActiveId
9913 if ((flags & ImGuiInputFlags_RouteOverActive) == 0 && g.ActiveIdUsingAllKeyboardKeys)
9914 {
9915 ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9916 if (key == ImGuiKey_None)
9917 key = ConvertSingleModFlagToKey((ImGuiKey)(key_chord & ImGuiMod_Mask_));
9918 if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
9919 return false;
9920 }
9921 }
9922
9923 // Where do we evaluate route for?
9924 ImGuiID focus_scope_id = g.CurrentFocusScopeId;
9925 if (flags & ImGuiInputFlags_RouteFromRootWindow)
9926 focus_scope_id = g.CurrentWindow->RootWindow->ID; // See PushFocusScope() call in Begin()
9927
9928 const int score = CalcRoutingScore(focus_scope_id, owner_id, flags);
9929 IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d\n", GetKeyChordName(key_chord), flags, owner_id, score);
9930 if (score == 255)
9931 return false;
9932
9933 // Submit routing for NEXT frame (assuming score is sufficient)
9934 // FIXME: Could expose a way to use a "serve last" policy for same score resolution (using <= instead of <).
9935 ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord);
9936 //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score <= routing_data->RoutingNextScore) : (score < routing_data->RoutingNextScore);
9937 if (score < routing_data->RoutingNextScore)
9938 {
9939 routing_data->RoutingNext = owner_id;
9940 routing_data->RoutingNextScore = (ImU8)score;
9941 }
9942
9943 // Return routing state for CURRENT frame
9944 if (routing_data->RoutingCurr == owner_id)
9945 IMGUI_DEBUG_LOG_INPUTROUTING("--> granting current route\n");
9946 return routing_data->RoutingCurr == owner_id;
9947}
9948
9949// Currently unused by core (but used by tests)
9950// Note: this cannot be turned into GetShortcutRouting() because we do the owner_id->routing_id translation, name would be more misleading.
9951bool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id)
9952{
9953 const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id);
9954 key_chord = FixupKeyChord(key_chord);
9955 ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry.
9956 return routing_data->RoutingCurr == routing_id;
9957}
9958
9959// Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes.
9960// Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87)
9961bool ImGui::IsKeyDown(ImGuiKey key)
9962{
9963 return IsKeyDown(key, ImGuiKeyOwner_Any);
9964}
9965
9966bool ImGui::IsKeyDown(ImGuiKey key, ImGuiID owner_id)
9967{
9968 const ImGuiKeyData* key_data = GetKeyData(key);
9969 if (!key_data->Down)
9970 return false;
9971 if (!TestKeyOwner(key, owner_id))
9972 return false;
9973 return true;
9974}
9975
9976bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat)
9977{
9978 return IsKeyPressed(key, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
9979}
9980
9981// Important: unlike legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat.
9982bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id)
9983{
9984 const ImGuiKeyData* key_data = GetKeyData(key);
9985 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)
9986 return false;
9987 const float t = key_data->DownDuration;
9988 if (t < 0.0f)
9989 return false;
9990 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function!
9991 if (flags & (ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_)) // Setting any _RepeatXXX option enables _Repeat
9992 flags |= ImGuiInputFlags_Repeat;
9993
9994 bool pressed = (t == 0.0f);
9995 if (!pressed && (flags & ImGuiInputFlags_Repeat) != 0)
9996 {
9997 float repeat_delay, repeat_rate;
9998 GetTypematicRepeatRate(flags, &repeat_delay, &repeat_rate);
9999 pressed = (t > repeat_delay) && GetKeyPressedAmount(key, repeat_delay, repeat_rate) > 0;
10000 if (pressed && (flags & ImGuiInputFlags_RepeatUntilMask_))
10001 {
10002 // Slightly bias 'key_pressed_time' as DownDuration is an accumulation of DeltaTime which we compare to an absolute time value.
10003 // Ideally we'd replace DownDuration with KeyPressedTime but it would break user's code.
10004 ImGuiContext& g = *GImGui;
10005 double key_pressed_time = g.Time - t + 0.00001f;
10006 if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChange) && (g.LastKeyModsChangeTime > key_pressed_time))
10007 pressed = false;
10008 if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone) && (g.LastKeyModsChangeFromNoneTime > key_pressed_time))
10009 pressed = false;
10010 if ((flags & ImGuiInputFlags_RepeatUntilOtherKeyPress) && (g.LastKeyboardKeyPressTime > key_pressed_time))
10011 pressed = false;
10012 }
10013 }
10014 if (!pressed)
10015 return false;
10016 if (!TestKeyOwner(key, owner_id))
10017 return false;
10018 return true;
10019}
10020
10021bool ImGui::IsKeyReleased(ImGuiKey key)
10022{
10023 return IsKeyReleased(key, ImGuiKeyOwner_Any);
10024}
10025
10026bool ImGui::IsKeyReleased(ImGuiKey key, ImGuiID owner_id)
10027{
10028 const ImGuiKeyData* key_data = GetKeyData(key);
10029 if (key_data->DownDurationPrev < 0.0f || key_data->Down)
10030 return false;
10031 if (!TestKeyOwner(key, owner_id))
10032 return false;
10033 return true;
10034}
10035
10036bool ImGui::IsMouseDown(ImGuiMouseButton button)
10037{
10038 ImGuiContext& g = *GImGui;
10039 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10040 return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // should be same as IsKeyDown(MouseButtonToKey(button), ImGuiKeyOwner_Any), but this allows legacy code hijacking the io.Mousedown[] array.
10041}
10042
10043bool ImGui::IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id)
10044{
10045 ImGuiContext& g = *GImGui;
10046 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10047 return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyDown(MouseButtonToKey(button), owner_id), but this allows legacy code hijacking the io.Mousedown[] array.
10048}
10049
10050bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
10051{
10052 return IsMouseClicked(button, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
10053}
10054
10055bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id)
10056{
10057 ImGuiContext& g = *GImGui;
10058 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10059 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)
10060 return false;
10061 const float t = g.IO.MouseDownDuration[button];
10062 if (t < 0.0f)
10063 return false;
10064 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsMouseClicked) == 0); // Passing flags not supported by this function! // FIXME: Could support RepeatRate and RepeatUntil flags here.
10065
10066 const bool repeat = (flags & ImGuiInputFlags_Repeat) != 0;
10067 const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0);
10068 if (!pressed)
10069 return false;
10070
10071 if (!TestKeyOwner(MouseButtonToKey(button), owner_id))
10072 return false;
10073
10074 return true;
10075}
10076
10077bool ImGui::IsMouseReleased(ImGuiMouseButton button)
10078{
10079 ImGuiContext& g = *GImGui;
10080 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10081 return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // Should be same as IsKeyReleased(MouseButtonToKey(button), ImGuiKeyOwner_Any)
10082}
10083
10084bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id)
10085{
10086 ImGuiContext& g = *GImGui;
10087 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10088 return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id)
10089}
10090
10091bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
10092{
10093 ImGuiContext& g = *GImGui;
10094 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10095 return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any);
10096}
10097
10098bool ImGui::IsMouseDoubleClicked(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.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), owner_id);
10103}
10104
10105int ImGui::GetMouseClickedCount(ImGuiMouseButton button)
10106{
10107 ImGuiContext& g = *GImGui;
10108 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10109 return g.IO.MouseClickedCount[button];
10110}
10111
10112// Test if mouse cursor is hovering given rectangle
10113// NB- Rectangle is clipped by our current clip setting
10114// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
10115bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
10116{
10117 ImGuiContext& g = *GImGui;
10118
10119 // Clip
10120 ImRect rect_clipped(r_min, r_max);
10121 if (clip)
10122 rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
10123
10124 // Hit testing, expanded for touch input
10125 if (!rect_clipped.ContainsWithPad(g.IO.MousePos, g.Style.TouchExtraPadding))
10126 return false;
10127 if (!g.MouseViewport->GetMainRect().Overlaps(rect_clipped))
10128 return false;
10129 return true;
10130}
10131
10132// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
10133// [Internal] This doesn't test if the button is pressed
10134bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
10135{
10136 ImGuiContext& g = *GImGui;
10137 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10138 if (lock_threshold < 0.0f)
10139 lock_threshold = g.IO.MouseDragThreshold;
10140 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
10141}
10142
10143bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
10144{
10145 ImGuiContext& g = *GImGui;
10146 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10147 if (!g.IO.MouseDown[button])
10148 return false;
10149 return IsMouseDragPastThreshold(button, lock_threshold);
10150}
10151
10152ImVec2 ImGui::GetMousePos()
10153{
10154 ImGuiContext& g = *GImGui;
10155 return g.IO.MousePos;
10156}
10157
10158// This is called TeleportMousePos() and not SetMousePos() to emphasis that setting MousePosPrev will effectively clear mouse delta as well.
10159// It is expected you only call this if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) is set and supported by backend.
10160void ImGui::TeleportMousePos(const ImVec2& pos)
10161{
10162 ImGuiContext& g = *GImGui;
10163 g.IO.MousePos = g.IO.MousePosPrev = pos;
10164 g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
10165 g.IO.WantSetMousePos = true;
10166 //IMGUI_DEBUG_LOG_IO("TeleportMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y);
10167}
10168
10169// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
10170ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
10171{
10172 ImGuiContext& g = *GImGui;
10173 if (g.BeginPopupStack.Size > 0)
10174 return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
10175 return g.IO.MousePos;
10176}
10177
10178// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
10179bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
10180{
10181 // The assert is only to silence a false-positive in XCode Static Analysis.
10182 // 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).
10183 IM_ASSERT(GImGui != NULL);
10184 const float MOUSE_INVALID = -256000.0f;
10185 ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
10186 return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
10187}
10188
10189// [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.
10190bool ImGui::IsAnyMouseDown()
10191{
10192 ImGuiContext& g = *GImGui;
10193 for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
10194 if (g.IO.MouseDown[n])
10195 return true;
10196 return false;
10197}
10198
10199// Return the delta from the initial clicking position while the mouse button is clicked or was just released.
10200// This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
10201// NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.
10202ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
10203{
10204 ImGuiContext& g = *GImGui;
10205 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10206 if (lock_threshold < 0.0f)
10207 lock_threshold = g.IO.MouseDragThreshold;
10208 if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
10209 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
10210 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
10211 return g.IO.MousePos - g.IO.MouseClickedPos[button];
10212 return ImVec2(0.0f, 0.0f);
10213}
10214
10215void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
10216{
10217 ImGuiContext& g = *GImGui;
10218 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
10219 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
10220 g.IO.MouseClickedPos[button] = g.IO.MousePos;
10221}
10222
10223// Get desired mouse cursor shape.
10224// Important: this is meant to be used by a platform backend, it is reset in ImGui::NewFrame(),
10225// updated during the frame, and locked in EndFrame()/Render().
10226// If you use software rendering by setting io.MouseDrawCursor then Dear ImGui will render those for you
10227ImGuiMouseCursor ImGui::GetMouseCursor()
10228{
10229 ImGuiContext& g = *GImGui;
10230 return g.MouseCursor;
10231}
10232
10233// We intentionally accept values of ImGuiMouseCursor that are outside our bounds, in case users needs to hack-in a custom cursor value.
10234// 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
10235// handle it before the backend _NewFrame() call and temporarily set ImGuiConfigFlags_NoMouseCursorChange during the backend _NewFrame() call.
10236void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
10237{
10238 ImGuiContext& g = *GImGui;
10239 g.MouseCursor = cursor_type;
10240}
10241
10242static void UpdateAliasKey(ImGuiKey key, bool v, float analog_value)
10243{
10244 IM_ASSERT(ImGui::IsAliasKey(key));
10245 ImGuiKeyData* key_data = ImGui::GetKeyData(key);
10246 key_data->Down = v;
10247 key_data->AnalogValue = analog_value;
10248}
10249
10250// [Internal] Do not use directly
10251static ImGuiKeyChord GetMergedModsFromKeys()
10252{
10253 ImGuiKeyChord mods = 0;
10254 if (ImGui::IsKeyDown(ImGuiMod_Ctrl)) { mods |= ImGuiMod_Ctrl; }
10255 if (ImGui::IsKeyDown(ImGuiMod_Shift)) { mods |= ImGuiMod_Shift; }
10256 if (ImGui::IsKeyDown(ImGuiMod_Alt)) { mods |= ImGuiMod_Alt; }
10257 if (ImGui::IsKeyDown(ImGuiMod_Super)) { mods |= ImGuiMod_Super; }
10258 return mods;
10259}
10260
10261static void ImGui::UpdateKeyboardInputs()
10262{
10263 ImGuiContext& g = *GImGui;
10264 ImGuiIO& io = g.IO;
10265
10266 if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
10267 io.ClearInputKeys();
10268
10269 // Import legacy keys or verify they are not used
10270#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
10271 if (io.BackendUsingLegacyKeyArrays == 0)
10272 {
10273 // Backend used new io.AddKeyEvent() API: Good! Verify that old arrays are never written to externally.
10274 for (int n = 0; n < ImGuiKey_LegacyNativeKey_END; n++)
10275 IM_ASSERT((io.KeysDown[n] == false || IsKeyDown((ImGuiKey)n)) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
10276 }
10277 else
10278 {
10279 if (g.FrameCount == 0)
10280 for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++)
10281 IM_ASSERT(g.IO.KeyMap[n] == -1 && "Backend is not allowed to write to io.KeyMap[0..511]!");
10282
10283 // Build reverse KeyMap (Named -> Legacy)
10284 for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++)
10285 if (io.KeyMap[n] != -1)
10286 {
10287 IM_ASSERT(IsLegacyKey((ImGuiKey)io.KeyMap[n]));
10288 io.KeyMap[io.KeyMap[n]] = n;
10289 }
10290
10291 // Import legacy keys into new ones
10292 for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++)
10293 if (io.KeysDown[n] || io.BackendUsingLegacyKeyArrays == 1)
10294 {
10295 const ImGuiKey key = (ImGuiKey)(io.KeyMap[n] != -1 ? io.KeyMap[n] : n);
10296 IM_ASSERT(io.KeyMap[n] == -1 || IsNamedKey(key));
10297 io.KeysData[key].Down = io.KeysDown[n];
10298 if (key != n)
10299 io.KeysDown[key] = io.KeysDown[n]; // Allow legacy code using io.KeysDown[GetKeyIndex()] with old backends
10300 io.BackendUsingLegacyKeyArrays = 1;
10301 }
10302 if (io.BackendUsingLegacyKeyArrays == 1)
10303 {
10304 GetKeyData(ImGuiMod_Ctrl)->Down = io.KeyCtrl;
10305 GetKeyData(ImGuiMod_Shift)->Down = io.KeyShift;
10306 GetKeyData(ImGuiMod_Alt)->Down = io.KeyAlt;
10307 GetKeyData(ImGuiMod_Super)->Down = io.KeySuper;
10308 }
10309 }
10310#endif
10311
10312 // Import legacy ImGuiNavInput_ io inputs and convert to gamepad keys
10313#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
10314 const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
10315 if (io.BackendUsingLegacyNavInputArray && nav_gamepad_active)
10316 {
10317 #define MAP_LEGACY_NAV_INPUT_TO_KEY1(_KEY, _NAV1) do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f); io.KeysData[_KEY].AnalogValue = io.NavInputs[_NAV1]; } while (0)
10318 #define MAP_LEGACY_NAV_INPUT_TO_KEY2(_KEY, _NAV1, _NAV2) do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f) || (io.NavInputs[_NAV2] > 0.0f); io.KeysData[_KEY].AnalogValue = ImMax(io.NavInputs[_NAV1], io.NavInputs[_NAV2]); } while (0)
10319 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceDown, ImGuiNavInput_Activate);
10320 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceRight, ImGuiNavInput_Cancel);
10321 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceLeft, ImGuiNavInput_Menu);
10322 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceUp, ImGuiNavInput_Input);
10323 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadLeft, ImGuiNavInput_DpadLeft);
10324 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadRight, ImGuiNavInput_DpadRight);
10325 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadUp, ImGuiNavInput_DpadUp);
10326 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadDown, ImGuiNavInput_DpadDown);
10327 MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadL1, ImGuiNavInput_FocusPrev, ImGuiNavInput_TweakSlow);
10328 MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadR1, ImGuiNavInput_FocusNext, ImGuiNavInput_TweakFast);
10329 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickLeft, ImGuiNavInput_LStickLeft);
10330 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickRight, ImGuiNavInput_LStickRight);
10331 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickUp, ImGuiNavInput_LStickUp);
10332 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickDown, ImGuiNavInput_LStickDown);
10333 #undef NAV_MAP_KEY
10334 }
10335#endif
10336
10337 // Update aliases
10338 for (int n = 0; n < ImGuiMouseButton_COUNT; n++)
10339 UpdateAliasKey(MouseButtonToKey(n), io.MouseDown[n], io.MouseDown[n] ? 1.0f : 0.0f);
10340 UpdateAliasKey(ImGuiKey_MouseWheelX, io.MouseWheelH != 0.0f, io.MouseWheelH);
10341 UpdateAliasKey(ImGuiKey_MouseWheelY, io.MouseWheel != 0.0f, io.MouseWheel);
10342
10343 // Synchronize io.KeyMods and io.KeyCtrl/io.KeyShift/etc. values.
10344 // - New backends (1.87+): send io.AddKeyEvent(ImGuiMod_XXX) -> -> (here) deriving io.KeyMods + io.KeyXXX from key array.
10345 // - Legacy backends: set io.KeyXXX bools -> (above) set key array from io.KeyXXX -> (here) deriving io.KeyMods + io.KeyXXX from key array.
10346 // So with legacy backends the 4 values will do a unnecessary back-and-forth but it makes the code simpler and future facing.
10347 const ImGuiKeyChord prev_key_mods = io.KeyMods;
10348 io.KeyMods = GetMergedModsFromKeys();
10349 io.KeyCtrl = (io.KeyMods & ImGuiMod_Ctrl) != 0;
10350 io.KeyShift = (io.KeyMods & ImGuiMod_Shift) != 0;
10351 io.KeyAlt = (io.KeyMods & ImGuiMod_Alt) != 0;
10352 io.KeySuper = (io.KeyMods & ImGuiMod_Super) != 0;
10353 if (prev_key_mods != io.KeyMods)
10354 g.LastKeyModsChangeTime = g.Time;
10355 if (prev_key_mods != io.KeyMods && prev_key_mods == 0)
10356 g.LastKeyModsChangeFromNoneTime = g.Time;
10357
10358 // Clear gamepad data if disabled
10359 if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0)
10360 for (int i = ImGuiKey_Gamepad_BEGIN; i < ImGuiKey_Gamepad_END; i++)
10361 {
10362 io.KeysData[i - ImGuiKey_KeysData_OFFSET].Down = false;
10363 io.KeysData[i - ImGuiKey_KeysData_OFFSET].AnalogValue = 0.0f;
10364 }
10365
10366 // Update keys
10367 for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++)
10368 {
10369 ImGuiKeyData* key_data = &io.KeysData[i];
10370 key_data->DownDurationPrev = key_data->DownDuration;
10371 key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f;
10372 if (key_data->DownDuration == 0.0f)
10373 {
10374 ImGuiKey key = (ImGuiKey)(ImGuiKey_KeysData_OFFSET + i);
10375 if (IsKeyboardKey(key))
10376 g.LastKeyboardKeyPressTime = g.Time;
10377 else if (key == ImGuiKey_ReservedForModCtrl || key == ImGuiKey_ReservedForModShift || key == ImGuiKey_ReservedForModAlt || key == ImGuiKey_ReservedForModSuper)
10378 g.LastKeyboardKeyPressTime = g.Time;
10379 }
10380 }
10381
10382 // Update keys/input owner (named keys only): one entry per key
10383 for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
10384 {
10385 ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_KeysData_OFFSET];
10386 ImGuiKeyOwnerData* owner_data = &g.KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN];
10387 owner_data->OwnerCurr = owner_data->OwnerNext;
10388 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.
10389 owner_data->OwnerNext = ImGuiKeyOwner_NoOwner;
10390 owner_data->LockThisFrame = owner_data->LockUntilRelease = owner_data->LockUntilRelease && key_data->Down; // Clear LockUntilRelease when key is not Down anymore
10391 }
10392
10393 // Update key routing (for e.g. shortcuts)
10394 UpdateKeyRoutingTable(&g.KeysRoutingTable);
10395}
10396
10397static void ImGui::UpdateMouseInputs()
10398{
10399 ImGuiContext& g = *GImGui;
10400 ImGuiIO& io = g.IO;
10401
10402 // Mouse Wheel swapping flag
10403 // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead
10404 // - We avoid doing it on OSX as it the OS input layer handles this already.
10405 // - FIXME: However this means when running on OSX over Emscripten, Shift+WheelY will incur two swapping (1 in OS, 1 here), canceling the feature.
10406 // - FIXME: When we can distinguish e.g. touchpad scroll events from mouse ones, we'll set this accordingly based on input source.
10407 io.MouseWheelRequestAxisSwap = io.KeyShift && !io.ConfigMacOSXBehaviors;
10408
10409 // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
10410 if (IsMousePosValid(&io.MousePos))
10411 io.MousePos = g.MouseLastValidPos = ImFloor(io.MousePos);
10412
10413 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
10414 if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev))
10415 io.MouseDelta = io.MousePos - io.MousePosPrev;
10416 else
10417 io.MouseDelta = ImVec2(0.0f, 0.0f);
10418
10419 // Update stationary timer.
10420 // FIXME: May need to rework again to have some tolerance for occasional small movement, while being functional on high-framerates.
10421 const float mouse_stationary_threshold = (io.MouseSource == ImGuiMouseSource_Mouse) ? 2.0f : 3.0f; // Slightly higher threshold for ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen, may need rework.
10422 const bool mouse_stationary = (ImLengthSqr(io.MouseDelta) <= mouse_stationary_threshold * mouse_stationary_threshold);
10423 g.MouseStationaryTimer = mouse_stationary ? (g.MouseStationaryTimer + io.DeltaTime) : 0.0f;
10424 //IMGUI_DEBUG_LOG("%.4f\n", g.MouseStationaryTimer);
10425
10426 // 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.
10427 if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)
10428 g.NavHighlightItemUnderNav = false;
10429
10430 for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
10431 {
10432 io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f;
10433 io.MouseClickedCount[i] = 0; // Will be filled below
10434 io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f;
10435 io.MouseDownDurationPrev[i] = io.MouseDownDuration[i];
10436 io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f;
10437 if (io.MouseClicked[i])
10438 {
10439 bool is_repeated_click = false;
10440 if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime)
10441 {
10442 ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
10443 if (ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist)
10444 is_repeated_click = true;
10445 }
10446 if (is_repeated_click)
10447 io.MouseClickedLastCount[i]++;
10448 else
10449 io.MouseClickedLastCount[i] = 1;
10450 io.MouseClickedTime[i] = g.Time;
10451 io.MouseClickedPos[i] = io.MousePos;
10452 io.MouseClickedCount[i] = io.MouseClickedLastCount[i];
10453 io.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
10454 io.MouseDragMaxDistanceSqr[i] = 0.0f;
10455 }
10456 else if (io.MouseDown[i])
10457 {
10458 // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
10459 ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
10460 io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
10461 io.MouseDragMaxDistanceAbs[i].x = ImMax(io.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x);
10462 io.MouseDragMaxDistanceAbs[i].y = ImMax(io.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y);
10463 }
10464
10465 // We provide io.MouseDoubleClicked[] as a legacy service
10466 io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2);
10467
10468 // Clicking any mouse button reactivate mouse hovering which may have been deactivated by keyboard/gamepad navigation
10469 if (io.MouseClicked[i])
10470 g.NavHighlightItemUnderNav = false;
10471 }
10472}
10473
10474static void LockWheelingWindow(ImGuiWindow* window, float wheel_amount)
10475{
10476 ImGuiContext& g = *GImGui;
10477 if (window)
10478 g.WheelingWindowReleaseTimer = ImMin(g.WheelingWindowReleaseTimer + ImAbs(wheel_amount) * WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER, WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER);
10479 else
10480 g.WheelingWindowReleaseTimer = 0.0f;
10481 if (g.WheelingWindow == window)
10482 return;
10483 IMGUI_DEBUG_LOG_IO("[io] LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL");
10484 g.WheelingWindow = window;
10485 g.WheelingWindowRefMousePos = g.IO.MousePos;
10486 if (window == NULL)
10487 {
10488 g.WheelingWindowStartFrame = -1;
10489 g.WheelingAxisAvg = ImVec2(0.0f, 0.0f);
10490 }
10491}
10492
10493static ImGuiWindow* FindBestWheelingWindow(const ImVec2& wheel)
10494{
10495 // For each axis, find window in the hierarchy that may want to use scrolling
10496 ImGuiContext& g = *GImGui;
10497 ImGuiWindow* windows[2] = { NULL, NULL };
10498 for (int axis = 0; axis < 2; axis++)
10499 if (wheel[axis] != 0.0f)
10500 for (ImGuiWindow* window = windows[axis] = g.HoveredWindow; window->Flags & ImGuiWindowFlags_ChildWindow; window = windows[axis] = window->ParentWindow)
10501 {
10502 // Bubble up into parent window if:
10503 // - a child window doesn't allow any scrolling.
10504 // - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag.
10506 const bool has_scrolling = (window->ScrollMax[axis] != 0.0f);
10507 const bool inputs_disabled = (window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs);
10508 //const bool scrolling_past_limits = (wheel_v < 0.0f) ? (window->Scroll[axis] <= 0.0f) : (window->Scroll[axis] >= window->ScrollMax[axis]);
10509 if (has_scrolling && !inputs_disabled) // && !scrolling_past_limits)
10510 break; // select this window
10511 }
10512 if (windows[0] == NULL && windows[1] == NULL)
10513 return NULL;
10514
10515 // If there's only one window or only one axis then there's no ambiguity
10516 if (windows[0] == windows[1] || windows[0] == NULL || windows[1] == NULL)
10517 return windows[1] ? windows[1] : windows[0];
10518
10519 // If candidate are different windows we need to decide which one to prioritize
10520 // - First frame: only find a winner if one axis is zero.
10521 // - Subsequent frames: only find a winner when one is more than the other.
10522 if (g.WheelingWindowStartFrame == -1)
10523 g.WheelingWindowStartFrame = g.FrameCount;
10524 if ((g.WheelingWindowStartFrame == g.FrameCount && wheel.x != 0.0f && wheel.y != 0.0f) || (g.WheelingAxisAvg.x == g.WheelingAxisAvg.y))
10525 {
10526 g.WheelingWindowWheelRemainder = wheel;
10527 return NULL;
10528 }
10529 return (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? windows[0] : windows[1];
10530}
10531
10532// Called by NewFrame()
10533void ImGui::UpdateMouseWheel()
10534{
10535 // Reset the locked window if we move the mouse or after the timer elapses.
10536 // 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)
10537 ImGuiContext& g = *GImGui;
10538 if (g.WheelingWindow != NULL)
10539 {
10540 g.WheelingWindowReleaseTimer -= g.IO.DeltaTime;
10541 if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
10542 g.WheelingWindowReleaseTimer = 0.0f;
10543 if (g.WheelingWindowReleaseTimer <= 0.0f)
10544 LockWheelingWindow(NULL, 0.0f);
10545 }
10546
10547 ImVec2 wheel;
10548 wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheelH : 0.0f;
10549 wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheel : 0.0f;
10550
10551 //IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y);
10552 ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
10553 if (!mouse_window || mouse_window->Collapsed)
10554 return;
10555
10556 // Zoom / Scale window
10557 // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
10558 if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
10559 {
10560 LockWheelingWindow(mouse_window, wheel.y);
10561 ImGuiWindow* window = mouse_window;
10562 const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
10563 const float scale = new_font_scale / window->FontWindowScale;
10564 window->FontWindowScale = new_font_scale;
10565 if (window == window->RootWindow)
10566 {
10567 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
10568 SetWindowPos(window, window->Pos + offset, 0);
10569 window->Size = ImTrunc(window->Size * scale);
10570 window->SizeFull = ImTrunc(window->SizeFull * scale);
10571 }
10572 return;
10573 }
10574 if (g.IO.KeyCtrl)
10575 return;
10576
10577 // Mouse wheel scrolling
10578 // Read about io.MouseWheelRequestAxisSwap and its issue on Mac+Emscripten in UpdateMouseInputs()
10579 if (g.IO.MouseWheelRequestAxisSwap)
10580 wheel = ImVec2(wheel.y, 0.0f);
10581
10582 // Maintain a rough average of moving magnitude on both axises
10583 // FIXME: should by based on wall clock time rather than frame-counter
10584 g.WheelingAxisAvg.x = ImExponentialMovingAverage(g.WheelingAxisAvg.x, ImAbs(wheel.x), 30);
10585 g.WheelingAxisAvg.y = ImExponentialMovingAverage(g.WheelingAxisAvg.y, ImAbs(wheel.y), 30);
10586
10587 // In the rare situation where FindBestWheelingWindow() had to defer first frame of wheeling due to ambiguous main axis, reinject it now.
10588 wheel += g.WheelingWindowWheelRemainder;
10589 g.WheelingWindowWheelRemainder = ImVec2(0.0f, 0.0f);
10590 if (wheel.x == 0.0f && wheel.y == 0.0f)
10591 return;
10592
10593 // Mouse wheel scrolling: find target and apply
10594 // - don't renew lock if axis doesn't apply on the window.
10595 // - select a main axis when both axises are being moved.
10596 if (ImGuiWindow* window = (g.WheelingWindow ? g.WheelingWindow : FindBestWheelingWindow(wheel)))
10597 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
10598 {
10599 bool do_scroll[2] = { wheel.x != 0.0f && window->ScrollMax.x != 0.0f, wheel.y != 0.0f && window->ScrollMax.y != 0.0f };
10600 if (do_scroll[ImGuiAxis_X] && do_scroll[ImGuiAxis_Y])
10601 do_scroll[(g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? ImGuiAxis_Y : ImGuiAxis_X] = false;
10602 if (do_scroll[ImGuiAxis_X])
10603 {
10604 LockWheelingWindow(window, wheel.x);
10605 float max_step = window->InnerRect.GetWidth() * 0.67f;
10606 float scroll_step = ImTrunc(ImMin(2 * window->CalcFontSize(), max_step));
10607 SetScrollX(window, window->Scroll.x - wheel.x * scroll_step);
10608 g.WheelingWindowScrolledFrame = g.FrameCount;
10609 }
10610 if (do_scroll[ImGuiAxis_Y])
10611 {
10612 LockWheelingWindow(window, wheel.y);
10613 float max_step = window->InnerRect.GetHeight() * 0.67f;
10614 float scroll_step = ImTrunc(ImMin(5 * window->CalcFontSize(), max_step));
10615 SetScrollY(window, window->Scroll.y - wheel.y * scroll_step);
10616 g.WheelingWindowScrolledFrame = g.FrameCount;
10617 }
10618 }
10619}
10620
10621void ImGui::SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard)
10622{
10623 ImGuiContext& g = *GImGui;
10624 g.WantCaptureKeyboardNextFrame = want_capture_keyboard ? 1 : 0;
10625}
10626
10627void ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse)
10628{
10629 ImGuiContext& g = *GImGui;
10630 g.WantCaptureMouseNextFrame = want_capture_mouse ? 1 : 0;
10631}
10632
10633#ifndef IMGUI_DISABLE_DEBUG_TOOLS
10634static const char* GetInputSourceName(ImGuiInputSource source)
10635{
10636 const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad" };
10637 IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT && source >= 0 && source < ImGuiInputSource_COUNT);
10638 return input_source_names[source];
10639}
10640static const char* GetMouseSourceName(ImGuiMouseSource source)
10641{
10642 const char* mouse_source_names[] = { "Mouse", "TouchScreen", "Pen" };
10643 IM_ASSERT(IM_ARRAYSIZE(mouse_source_names) == ImGuiMouseSource_COUNT && source >= 0 && source < ImGuiMouseSource_COUNT);
10644 return mouse_source_names[source];
10645}
10646static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e)
10647{
10648 ImGuiContext& g = *GImGui;
10649 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; }
10650 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; }
10651 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; }
10652 if (e->Type == ImGuiInputEventType_MouseViewport){IMGUI_DEBUG_LOG_IO("[io] %s: MouseViewport (0x%08X)\n", prefix, e->MouseViewport.HoveredViewportID); return; }
10653 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; }
10654 if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG_IO("[io] %s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; }
10655 if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG_IO("[io] %s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; }
10656}
10657#endif
10658
10659// Process input queue
10660// We always call this with the value of 'bool g.IO.ConfigInputTrickleEventQueue'.
10661// - trickle_fast_inputs = false : process all events, turn into flattened input state (e.g. successive down/up/down/up will be lost)
10662// - 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)
10663void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
10664{
10665 ImGuiContext& g = *GImGui;
10666 ImGuiIO& io = g.IO;
10667
10668 // Only trickle chars<>key when working with InputText()
10669 // FIXME: InputText() could parse event trail?
10670 // FIXME: Could specialize chars<>keys trickling rules for control keys (those not typically associated to characters)
10671 const bool trickle_interleaved_nonchar_keys_and_text = (trickle_fast_inputs && g.WantTextInputNextFrame == 1);
10672
10673 bool mouse_moved = false, mouse_wheeled = false, key_changed = false, key_changed_nonchar = false, text_inputted = false;
10674 int mouse_button_changed = 0x00;
10675 ImBitArray<ImGuiKey_KeysData_SIZE> key_changed_mask;
10676
10677 int event_n = 0;
10678 for (; event_n < g.InputEventsQueue.Size; event_n++)
10679 {
10680 ImGuiInputEvent* e = &g.InputEventsQueue[event_n];
10681 if (e->Type == ImGuiInputEventType_MousePos)
10682 {
10683 if (g.IO.WantSetMousePos)
10684 continue;
10685 // Trickling Rule: Stop processing queued events if we already handled a mouse button change
10686 ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY);
10687 if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted))
10688 break;
10689 io.MousePos = event_pos;
10690 io.MouseSource = e->MousePos.MouseSource;
10691 mouse_moved = true;
10692 }
10693 else if (e->Type == ImGuiInputEventType_MouseButton)
10694 {
10695 // Trickling Rule: Stop processing queued events if we got multiple action on the same button
10696 const ImGuiMouseButton button = e->MouseButton.Button;
10697 IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT);
10698 if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled))
10699 break;
10700 if (trickle_fast_inputs && e->MouseButton.MouseSource == ImGuiMouseSource_TouchScreen && mouse_moved) // #2702: TouchScreen have no initial hover.
10701 break;
10702 io.MouseDown[button] = e->MouseButton.Down;
10703 io.MouseSource = e->MouseButton.MouseSource;
10704 mouse_button_changed |= (1 << button);
10705 }
10706 else if (e->Type == ImGuiInputEventType_MouseWheel)
10707 {
10708 // Trickling Rule: Stop processing queued events if we got multiple action on the event
10709 if (trickle_fast_inputs && (mouse_moved || mouse_button_changed != 0))
10710 break;
10711 io.MouseWheelH += e->MouseWheel.WheelX;
10712 io.MouseWheel += e->MouseWheel.WheelY;
10713 io.MouseSource = e->MouseWheel.MouseSource;
10714 mouse_wheeled = true;
10715 }
10716 else if (e->Type == ImGuiInputEventType_MouseViewport)
10717 {
10718 io.MouseHoveredViewport = e->MouseViewport.HoveredViewportID;
10719 }
10720 else if (e->Type == ImGuiInputEventType_Key)
10721 {
10722 // Trickling Rule: Stop processing queued events if we got multiple action on the same button
10723 if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
10724 continue;
10725 ImGuiKey key = e->Key.Key;
10726 IM_ASSERT(key != ImGuiKey_None);
10727 ImGuiKeyData* key_data = GetKeyData(key);
10728 const int key_data_index = (int)(key_data - g.IO.KeysData);
10729 if (trickle_fast_inputs && key_data->Down != e->Key.Down && (key_changed_mask.TestBit(key_data_index) || mouse_button_changed != 0))
10730 break;
10731
10732 const bool key_is_potentially_for_char_input = IsKeyChordPotentiallyCharInput(GetMergedModsFromKeys() | key);
10733 if (trickle_interleaved_nonchar_keys_and_text && (text_inputted && !key_is_potentially_for_char_input))
10734 break;
10735
10736 key_data->Down = e->Key.Down;
10737 key_data->AnalogValue = e->Key.AnalogValue;
10738 key_changed = true;
10739 key_changed_mask.SetBit(key_data_index);
10740 if (trickle_interleaved_nonchar_keys_and_text && !key_is_potentially_for_char_input)
10741 key_changed_nonchar = true;
10742
10743 // Allow legacy code using io.KeysDown[GetKeyIndex()] with new backends
10744#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
10745 io.KeysDown[key_data_index] = key_data->Down;
10746 if (io.KeyMap[key_data_index] != -1)
10747 io.KeysDown[io.KeyMap[key_data_index]] = key_data->Down;
10748#endif
10749 }
10750 else if (e->Type == ImGuiInputEventType_Text)
10751 {
10752 if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
10753 continue;
10754 // Trickling Rule: Stop processing queued events if keys/mouse have been interacted with
10755 if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_moved || mouse_wheeled))
10756 break;
10757 if (trickle_interleaved_nonchar_keys_and_text && key_changed_nonchar)
10758 break;
10759 unsigned int c = e->Text.Char;
10760 io.InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
10761 if (trickle_interleaved_nonchar_keys_and_text)
10762 text_inputted = true;
10763 }
10764 else if (e->Type == ImGuiInputEventType_Focus)
10765 {
10766 // We intentionally overwrite this and process in NewFrame(), in order to give a chance
10767 // to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame.
10768 const bool focus_lost = !e->AppFocused.Focused;
10769 io.AppFocusLost = focus_lost;
10770 }
10771 else
10772 {
10773 IM_ASSERT(0 && "Unknown event!");
10774 }
10775 }
10776
10777 // Record trail (for domain-specific applications wanting to access a precise trail)
10778 //if (event_n != 0) IMGUI_DEBUG_LOG_IO("Processed: %d / Remaining: %d\n", event_n, g.InputEventsQueue.Size - event_n);
10779 for (int n = 0; n < event_n; n++)
10780 g.InputEventsTrail.push_back(g.InputEventsQueue[n]);
10781
10782 // [DEBUG]
10783#ifndef IMGUI_DISABLE_DEBUG_TOOLS
10784 if (event_n != 0 && (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO))
10785 for (int n = 0; n < g.InputEventsQueue.Size; n++)
10786 DebugPrintInputEvent(n < event_n ? "Processed" : "Remaining", &g.InputEventsQueue[n]);
10787#endif
10788
10789 // Remaining events will be processed on the next frame
10790 if (event_n == g.InputEventsQueue.Size)
10791 g.InputEventsQueue.resize(0);
10792 else
10793 g.InputEventsQueue.erase(g.InputEventsQueue.Data, g.InputEventsQueue.Data + event_n);
10794
10795 // Clear buttons state when focus is lost
10796 // - this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle.
10797 // - we clear in EndFrame() and not now in order allow application/user code polling this flag
10798 // (e.g. custom backend may want to clear additional data, custom widgets may want to react with a "canceling" event).
10799 if (g.IO.AppFocusLost)
10800 {
10801 g.IO.ClearInputKeys();
10802 g.IO.ClearInputMouse();
10803 }
10804}
10805
10806ImGuiID ImGui::GetKeyOwner(ImGuiKey key)
10807{
10808 if (!IsNamedKeyOrMod(key))
10809 return ImGuiKeyOwner_NoOwner;
10810
10811 ImGuiContext& g = *GImGui;
10812 ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
10813 ImGuiID owner_id = owner_data->OwnerCurr;
10814
10815 if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)
10816 if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
10817 return ImGuiKeyOwner_NoOwner;
10818
10819 return owner_id;
10820}
10821
10822// TestKeyOwner(..., ID) : (owner == None || owner == ID)
10823// TestKeyOwner(..., None) : (owner == None)
10824// TestKeyOwner(..., Any) : no owner test
10825// All paths are also testing for key not being locked, for the rare cases that key have been locked with using ImGuiInputFlags_LockXXX flags.
10826bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id)
10827{
10828 if (!IsNamedKeyOrMod(key))
10829 return true;
10830
10831 ImGuiContext& g = *GImGui;
10832 if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)
10833 if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
10834 return false;
10835
10836 ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
10837 if (owner_id == ImGuiKeyOwner_Any)
10838 return (owner_data->LockThisFrame == false);
10839
10840 // Note: SetKeyOwner() sets OwnerCurr. It is not strictly required for most mouse routing overlap (because of ActiveId/HoveredId
10841 // are acting as filter before this has a chance to filter), but sane as soon as user tries to look into things.
10842 // Setting OwnerCurr in SetKeyOwner() is more consistent than testing OwnerNext here: would be inconsistent with getter and other functions.
10843 if (owner_data->OwnerCurr != owner_id)
10844 {
10845 if (owner_data->LockThisFrame)
10846 return false;
10847 if (owner_data->OwnerCurr != ImGuiKeyOwner_NoOwner)
10848 return false;
10849 }
10850
10851 return true;
10852}
10853
10854// _LockXXX flags are useful to lock keys away from code which is not input-owner aware.
10855// When using _LockXXX flags, you can use ImGuiKeyOwner_Any to lock keys from everyone.
10856// - SetKeyOwner(..., None) : clears owner
10857// - SetKeyOwner(..., Any, !Lock) : illegal (assert)
10858// - SetKeyOwner(..., Any or None, Lock) : set lock
10859void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)
10860{
10861 ImGuiContext& g = *GImGui;
10862 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)
10863 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetKeyOwner) == 0); // Passing flags not supported by this function!
10864 //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X, flags=%08X)\n", GetKeyName(key), owner_id, flags);
10865
10866 ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
10867 owner_data->OwnerCurr = owner_data->OwnerNext = owner_id;
10868
10869 // We cannot lock by default as it would likely break lots of legacy code.
10870 // In the case of using LockUntilRelease while key is not down we still lock during the frame (no key_data->Down test)
10871 owner_data->LockUntilRelease = (flags & ImGuiInputFlags_LockUntilRelease) != 0;
10872 owner_data->LockThisFrame = (flags & ImGuiInputFlags_LockThisFrame) != 0 || (owner_data->LockUntilRelease);
10873}
10874
10875// Rarely used helper
10876void ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags)
10877{
10878 if (key_chord & ImGuiMod_Ctrl) { SetKeyOwner(ImGuiMod_Ctrl, owner_id, flags); }
10879 if (key_chord & ImGuiMod_Shift) { SetKeyOwner(ImGuiMod_Shift, owner_id, flags); }
10880 if (key_chord & ImGuiMod_Alt) { SetKeyOwner(ImGuiMod_Alt, owner_id, flags); }
10881 if (key_chord & ImGuiMod_Super) { SetKeyOwner(ImGuiMod_Super, owner_id, flags); }
10882 if (key_chord & ~ImGuiMod_Mask_) { SetKeyOwner((ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); }
10883}
10884
10885// This is more or less equivalent to:
10886// if (IsItemHovered() || IsItemActive())
10887// SetKeyOwner(key, GetItemID());
10888// 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.
10889// More advanced usage scenarios may want to call SetKeyOwner() manually based on different condition.
10890// 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.
10891void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags)
10892{
10893 ImGuiContext& g = *GImGui;
10894 ImGuiID id = g.LastItemData.ID;
10895 if (id == 0 || (g.HoveredId != id && g.ActiveId != id))
10896 return;
10897 if ((flags & ImGuiInputFlags_CondMask_) == 0)
10898 flags |= ImGuiInputFlags_CondDefault_;
10899 if ((g.HoveredId == id && (flags & ImGuiInputFlags_CondHovered)) || (g.ActiveId == id && (flags & ImGuiInputFlags_CondActive)))
10900 {
10901 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetItemKeyOwner) == 0); // Passing flags not supported by this function!
10902 SetKeyOwner(key, id, flags & ~ImGuiInputFlags_CondMask_);
10903 }
10904}
10905
10906void ImGui::SetItemKeyOwner(ImGuiKey key)
10907{
10908 SetItemKeyOwner(key, ImGuiInputFlags_None);
10909}
10910
10911// This is the only public API until we expose owner_id versions of the API as replacements.
10912bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord)
10913{
10914 return IsKeyChordPressed(key_chord, ImGuiInputFlags_None, ImGuiKeyOwner_Any);
10915}
10916
10917// This is equivalent to comparing KeyMods + doing a IsKeyPressed()
10918bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
10919{
10920 ImGuiContext& g = *GImGui;
10921 key_chord = FixupKeyChord(key_chord);
10922 ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
10923 if (g.IO.KeyMods != mods)
10924 return false;
10925
10926 // Special storage location for mods
10927 ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
10928 if (key == ImGuiKey_None)
10929 key = ConvertSingleModFlagToKey(mods);
10930 if (!IsKeyPressed(key, (flags & ImGuiInputFlags_RepeatMask_), owner_id))
10931 return false;
10932 return true;
10933}
10934
10935void ImGui::SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
10936{
10937 ImGuiContext& g = *GImGui;
10938 g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasShortcut;
10939 g.NextItemData.Shortcut = key_chord;
10940 g.NextItemData.ShortcutFlags = flags;
10941}
10942
10943// Called from within ItemAdd: at this point we can read from NextItemData and write to LastItemData
10944void ImGui::ItemHandleShortcut(ImGuiID id)
10945{
10946 ImGuiContext& g = *GImGui;
10947 ImGuiInputFlags flags = g.NextItemData.ShortcutFlags;
10948 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetNextItemShortcut) == 0); // Passing flags not supported by SetNextItemShortcut()!
10949
10950 if (g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled)
10951 return;
10952 if (flags & ImGuiInputFlags_Tooltip)
10953 {
10954 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasShortcut;
10955 g.LastItemData.Shortcut = g.NextItemData.Shortcut;
10956 }
10957 if (!Shortcut(g.NextItemData.Shortcut, flags & ImGuiInputFlags_SupportedByShortcut, id) || g.NavActivateId != 0)
10958 return;
10959
10960 // FIXME: Generalize Activation queue?
10961 g.NavActivateId = id; // Will effectively disable clipping.
10962 g.NavActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_FromShortcut;
10963 //if (g.ActiveId == 0 || g.ActiveId == id)
10964 g.NavActivateDownId = g.NavActivatePressedId = id;
10965 NavHighlightActivated(id);
10966}
10967
10968bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
10969{
10970 return Shortcut(key_chord, flags, ImGuiKeyOwner_Any);
10971}
10972
10973bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
10974{
10975 ImGuiContext& g = *GImGui;
10976 //IMGUI_DEBUG_LOG("Shortcut(%s, flags=%X, owner_id=0x%08X)\n", GetKeyChordName(key_chord, g.TempBuffer.Data, g.TempBuffer.Size), flags, owner_id);
10977
10978 // When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any.
10979 if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
10980 flags |= ImGuiInputFlags_RouteFocused;
10981
10982 // Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default)
10983 // Effectively makes Shortcut() always input-owner aware.
10984 if (owner_id == ImGuiKeyOwner_Any || owner_id == ImGuiKeyOwner_NoOwner)
10985 owner_id = GetRoutingIdFromOwnerId(owner_id);
10986
10987 if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
10988 return false;
10989
10990 // Submit route
10991 if (!SetShortcutRouting(key_chord, flags, owner_id))
10992 return false;
10993
10994 // Default repeat behavior for Shortcut()
10995 // So e.g. pressing Ctrl+W and releasing Ctrl while holding W will not trigger the W shortcut.
10996 if ((flags & ImGuiInputFlags_Repeat) != 0 && (flags & ImGuiInputFlags_RepeatUntilMask_) == 0)
10997 flags |= ImGuiInputFlags_RepeatUntilKeyModsChange;
10998
10999 if (!IsKeyChordPressed(key_chord, flags, owner_id))
11000 return false;
11001
11002 // Claim mods during the press
11003 SetKeyOwnersForKeyChord(key_chord & ImGuiMod_Mask_, owner_id);
11004
11005 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function!
11006 return true;
11007}
11008
11009
11010//-----------------------------------------------------------------------------
11011// [SECTION] ERROR CHECKING, STATE RECOVERY
11012//-----------------------------------------------------------------------------
11013// - DebugCheckVersionAndDataLayout() (called via IMGUI_CHECKVERSION() macros)
11014// - ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
11015// - ErrorCheckNewFrameSanityChecks()
11016// - ErrorCheckEndFrameSanityChecks()
11017// - ErrorRecoveryStoreState()
11018// - ErrorRecoveryTryToRecoverState()
11019// - ErrorRecoveryTryToRecoverWindowState()
11020// - ErrorLog()
11021//-----------------------------------------------------------------------------
11022
11023// Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
11024// Called by IMGUI_CHECKVERSION().
11025// Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
11026// If this triggers you have mismatched headers and compiled code versions.
11027// - It could be because of a build issue (using new headers with old compiled code)
11028// - It could be because of mismatched configuration #define, compilation settings, packing pragma etc.
11029// THE CONFIGURATION SETTINGS MENTIONED IN imconfig.h MUST BE SET FOR ALL COMPILATION UNITS INVOLVED WITH DEAR IMGUI.
11030// Which is why it is required you put them in your imconfig file (and NOT only before including imgui.h).
11031// Otherwise it is possible that different compilation units would see different structure layout.
11032// If you don't want to modify imconfig.h you can use the IMGUI_USER_CONFIG define to change filename.
11033bool 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)
11034{
11035 bool error = false;
11036 if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
11037 if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
11038 if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
11039 if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
11040 if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
11041 if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
11042 if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
11043 return !error;
11044}
11045
11046// 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)
11047// This is causing issues and ambiguity and we need to retire that.
11048// See https://github.com/ocornut/imgui/issues/5548 for more details.
11049// [Scenario 1]
11050// Previously this would make the window content size ~200x200:
11051// Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End(); // NOT OK
11052// Instead, please submit an item:
11053// Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); // OK
11054// Alternative:
11055// Begin(...) + Dummy(ImVec2(200,200)) + End(); // OK
11056// [Scenario 2]
11057// For reference this is one of the issue what we aim to fix with this change:
11058// BeginGroup() + SomeItem("foobar") + SetCursorScreenPos(GetCursorScreenPos()) + EndGroup()
11059// 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!
11060// 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.
11061void ImGui::ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
11062{
11063 ImGuiContext& g = *GImGui;
11064 ImGuiWindow* window = g.CurrentWindow;
11065 IM_ASSERT(window->DC.IsSetPos);
11066 window->DC.IsSetPos = false;
11067#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
11068 if (window->DC.CursorPos.x <= window->DC.CursorMaxPos.x && window->DC.CursorPos.y <= window->DC.CursorMaxPos.y)
11069 return;
11070 if (window->SkipItems)
11071 return;
11072 IM_ASSERT(0 && "Code uses SetCursorPos()/SetCursorScreenPos() to extend window/parent boundaries. Please submit an item e.g. Dummy() to validate extent.");
11073#else
11074 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
11075#endif
11076}
11077
11078static void ImGui::ErrorCheckNewFrameSanityChecks()
11079{
11080 ImGuiContext& g = *GImGui;
11081
11082 // Check user IM_ASSERT macro
11083 // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined!
11084 // If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
11085 // This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
11086 // #define IM_ASSERT(EXPR) if (SomeCode(EXPR)) SomeMoreCode(); // Wrong!
11087 // #define IM_ASSERT(EXPR) do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0) // Correct!
11088 if (true) IM_ASSERT(1); else IM_ASSERT(0);
11089
11090 // Emscripten backends are often imprecise in their submission of DeltaTime. (#6114, #3644)
11091 // Ideally the Emscripten app/backend should aim to fix or smooth this value and avoid feeding zero, but we tolerate it.
11092#ifdef __EMSCRIPTEN__
11093 if (g.IO.DeltaTime <= 0.0f && g.FrameCount > 0)
11094 g.IO.DeltaTime = 0.00001f;
11095#endif
11096
11097 // Check user data
11098 // (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)
11099 IM_ASSERT(g.Initialized);
11100 IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!");
11101 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
11102 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!");
11103 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()");
11104 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!");
11105 IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!");
11106 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
11107 IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
11108 IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
11109 IM_ASSERT(g.Style.ColorButtonPosition == ImGuiDir_Left || g.Style.ColorButtonPosition == ImGuiDir_Right);
11110#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
11111 for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_COUNT; n++)
11112 IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < ImGuiKey_LegacyNativeKey_END && "io.KeyMap[] contains an out of bound value (need to be 0..511, or -1 for unmapped key)");
11113
11114 // Check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only added in 1.60 WIP)
11115 if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && g.IO.BackendUsingLegacyKeyArrays == 1)
11116 IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
11117#endif
11118
11119 // Error handling: we do not accept 100% silent recovery! Please contact me if you feel this is getting in your way.
11120 if (g.IO.ConfigErrorRecovery)
11121 IM_ASSERT(g.IO.ConfigErrorRecoveryEnableAssert || g.IO.ConfigErrorRecoveryEnableDebugLog || g.IO.ConfigErrorRecoveryEnableTooltip || g.ErrorCallback != NULL);
11122
11123#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
11124 // Remap legacy names
11125 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)
11126 {
11127 g.IO.ConfigNavMoveSetMousePos = true;
11128 g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavEnableSetMousePos;
11129 }
11130 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)
11131 {
11132 g.IO.ConfigNavCaptureKeyboard = false;
11133 g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavNoCaptureKeyboard;
11134 }
11135
11136 // Remap legacy clipboard handlers (OBSOLETED in 1.91.1, August 2024)
11137 if (g.IO.GetClipboardTextFn != NULL && (g.PlatformIO.Platform_GetClipboardTextFn == NULL || g.PlatformIO.Platform_GetClipboardTextFn == Platform_GetClipboardTextFn_DefaultImpl))
11138 g.PlatformIO.Platform_GetClipboardTextFn = [](ImGuiContext* ctx) { return ctx->IO.GetClipboardTextFn(ctx->IO.ClipboardUserData); };
11139 if (g.IO.SetClipboardTextFn != NULL && (g.PlatformIO.Platform_SetClipboardTextFn == NULL || g.PlatformIO.Platform_SetClipboardTextFn == Platform_SetClipboardTextFn_DefaultImpl))
11140 g.PlatformIO.Platform_SetClipboardTextFn = [](ImGuiContext* ctx, const char* text) { return ctx->IO.SetClipboardTextFn(ctx->IO.ClipboardUserData, text); };
11141#endif
11142
11143 // 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.
11144 if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_DockingEnable) == 0)
11145 IM_ASSERT(0 && "Please set DockingEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!");
11146 if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable) == 0)
11147 IM_ASSERT(0 && "Please set ViewportsEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!");
11148
11149 // Perform simple checks: multi-viewport and platform windows support
11150 if (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
11151 {
11152 if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports))
11153 {
11154 IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()? Check examples/ applications for reference.");
11155 IM_ASSERT(g.PlatformIO.Platform_CreateWindow != NULL && "Platform init didn't install handlers?");
11156 IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?");
11157 IM_ASSERT(g.PlatformIO.Platform_GetWindowPos != NULL && "Platform init didn't install handlers?");
11158 IM_ASSERT(g.PlatformIO.Platform_SetWindowPos != NULL && "Platform init didn't install handlers?");
11159 IM_ASSERT(g.PlatformIO.Platform_GetWindowSize != NULL && "Platform init didn't install handlers?");
11160 IM_ASSERT(g.PlatformIO.Platform_SetWindowSize != NULL && "Platform init didn't install handlers?");
11161 IM_ASSERT(g.PlatformIO.Monitors.Size > 0 && "Platform init didn't setup Monitors list?");
11162 IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport.");
11163 if (g.IO.ConfigDockingTransparentPayload && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
11164 IM_ASSERT(g.PlatformIO.Platform_SetWindowAlpha != NULL && "Platform_SetWindowAlpha handler is required to use io.ConfigDockingTransparent!");
11165 }
11166 else
11167 {
11168 // Disable feature, our backends do not support it
11169 g.IO.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable;
11170 }
11171
11172 // Perform simple checks on platform monitor data + compute a total bounding box for quick early outs
11173 for (ImGuiPlatformMonitor& mon : g.PlatformIO.Monitors)
11174 {
11175 IM_UNUSED(mon);
11176 IM_ASSERT(mon.MainSize.x > 0.0f && mon.MainSize.y > 0.0f && "Monitor main bounds not setup properly.");
11177 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.");
11178 IM_ASSERT(mon.DpiScale != 0.0f);
11179 }
11180 }
11181}
11182
11183static void ImGui::ErrorCheckEndFrameSanityChecks()
11184{
11185 // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
11186 // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
11187 // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
11188 // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
11189 // We silently accommodate for this case by ignoring the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
11190 // while still correctly asserting on mid-frame key press events.
11191 ImGuiContext& g = *GImGui;
11192 const ImGuiKeyChord key_mods = GetMergedModsFromKeys();
11193 IM_UNUSED(g);
11194 IM_UNUSED(key_mods);
11195 IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
11196 IM_UNUSED(key_mods);
11197
11198 IM_ASSERT(g.CurrentWindowStack.Size == 1);
11199 IM_ASSERT(g.CurrentWindowStack[0].Window->IsFallbackWindow);
11200}
11201
11202// Save current stack sizes. Called e.g. by NewFrame() and by Begin() but may be called for manual recovery.
11203void ImGui::ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out)
11204{
11205 ImGuiContext& g = *GImGui;
11206 state_out->SizeOfWindowStack = (short)g.CurrentWindowStack.Size;
11207 state_out->SizeOfIDStack = (short)g.CurrentWindow->IDStack.Size;
11208 state_out->SizeOfTreeStack = (short)g.CurrentWindow->DC.TreeDepth; // NOT g.TreeNodeStack.Size which is a partial stack!
11209 state_out->SizeOfColorStack = (short)g.ColorStack.Size;
11210 state_out->SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
11211 state_out->SizeOfFontStack = (short)g.FontStack.Size;
11212 state_out->SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
11213 state_out->SizeOfGroupStack = (short)g.GroupStack.Size;
11214 state_out->SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size;
11215 state_out->SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
11216 state_out->SizeOfDisabledStack = (short)g.DisabledStackSize;
11217}
11218
11219// Chosen name "Try to recover" over e.g. "Restore" to suggest this is not a 100% guaranteed recovery.
11220// Called by e.g. EndFrame() but may be called for manual recovery.
11221// Attempt to recover full window stack.
11222void ImGui::ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in)
11223{
11224 // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
11225 ImGuiContext& g = *GImGui;
11226 while (g.CurrentWindowStack.Size > state_in->SizeOfWindowStack) //-V1044
11227 {
11228 // Recap:
11229 // - Begin()/BeginChild() return false to indicate the window is collapsed or fully clipped.
11230 // - Always call a matching End() for each Begin() call, regardless of its return value!
11231 // - Begin/End and BeginChild/EndChild logic is KNOWN TO BE INCONSISTENT WITH ALL OTHER BEGIN/END FUNCTIONS.
11232 // - We will fix that in a future major update.
11233 ImGuiWindow* window = g.CurrentWindow;
11234 if (window->Flags & ImGuiWindowFlags_ChildWindow)
11235 {
11236 IM_ASSERT_USER_ERROR(0, "Missing EndChild()");
11237 EndChild();
11238 }
11239 else
11240 {
11241 IM_ASSERT_USER_ERROR(0, "Missing End()");
11242 End();
11243 }
11244 }
11245 if (g.CurrentWindowStack.Size == state_in->SizeOfWindowStack)
11246 ErrorRecoveryTryToRecoverWindowState(state_in);
11247}
11248
11249// Called by e.g. End() but may be called for manual recovery.
11250// Read '// Error Handling [BETA]' block in imgui_internal.h for details.
11251// Attempt to recover from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
11252void ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in)
11253{
11254 ImGuiContext& g = *GImGui;
11255
11256 while (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow) //-V1044
11257 {
11258 IM_ASSERT_USER_ERROR(0, "Missing EndTable()");
11259 EndTable();
11260 }
11261
11262 ImGuiWindow* window = g.CurrentWindow;
11263
11264 // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
11265 while (g.CurrentTabBar != NULL && g.CurrentTabBar->Window == window) //-V1044
11266 {
11267 IM_ASSERT_USER_ERROR(0, "Missing EndTabBar()");
11268 EndTabBar();
11269 }
11270 while (g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Storage->Window == window) //-V1044
11271 {
11272 IM_ASSERT_USER_ERROR(0, "Missing EndMultiSelect()");
11273 EndMultiSelect();
11274 }
11275 while (window->DC.TreeDepth > state_in->SizeOfTreeStack) //-V1044
11276 {
11277 IM_ASSERT_USER_ERROR(0, "Missing TreePop()");
11278 TreePop();
11279 }
11280 while (g.GroupStack.Size > state_in->SizeOfGroupStack) //-V1044
11281 {
11282 IM_ASSERT_USER_ERROR(0, "Missing EndGroup()");
11283 EndGroup();
11284 }
11285 IM_ASSERT(g.GroupStack.Size == state_in->SizeOfGroupStack);
11286 while (window->IDStack.Size > state_in->SizeOfIDStack) //-V1044
11287 {
11288 IM_ASSERT_USER_ERROR(0, "Missing PopID()");
11289 PopID();
11290 }
11291 while (g.DisabledStackSize > state_in->SizeOfDisabledStack) //-V1044
11292 {
11293 IM_ASSERT_USER_ERROR(0, "Missing EndDisabled()");
11294 if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
11295 EndDisabled();
11296 else
11297 {
11298 EndDisabledOverrideReenable();
11299 g.CurrentWindowStack.back().DisabledOverrideReenable = false;
11300 }
11301 }
11302 IM_ASSERT(g.DisabledStackSize == state_in->SizeOfDisabledStack);
11303 while (g.ColorStack.Size > state_in->SizeOfColorStack) //-V1044
11304 {
11305 IM_ASSERT_USER_ERROR(0, "Missing PopStyleColor()");
11306 PopStyleColor();
11307 }
11308 while (g.ItemFlagsStack.Size > state_in->SizeOfItemFlagsStack) //-V1044
11309 {
11310 IM_ASSERT_USER_ERROR(0, "Missing PopItemFlag()");
11311 PopItemFlag();
11312 }
11313 while (g.StyleVarStack.Size > state_in->SizeOfStyleVarStack) //-V1044
11314 {
11315 IM_ASSERT_USER_ERROR(0, "Missing PopStyleVar()");
11316 PopStyleVar();
11317 }
11318 while (g.FontStack.Size > state_in->SizeOfFontStack) //-V1044
11319 {
11320 IM_ASSERT_USER_ERROR(0, "Missing PopFont()");
11321 PopFont();
11322 }
11323 while (g.FocusScopeStack.Size > state_in->SizeOfFocusScopeStack) //-V1044
11324 {
11325 IM_ASSERT_USER_ERROR(0, "Missing PopFocusScope()");
11326 PopFocusScope();
11327 }
11328 //IM_ASSERT(g.FocusScopeStack.Size == state_in->SizeOfFocusScopeStack);
11329}
11330
11331bool ImGui::ErrorLog(const char* msg)
11332{
11333 ImGuiContext& g = *GImGui;
11334
11335 // Output to debug log
11336#ifndef IMGUI_DISABLE_DEBUG_TOOLS
11337 ImGuiWindow* window = g.CurrentWindow;
11338
11339 if (g.IO.ConfigErrorRecoveryEnableDebugLog)
11340 {
11341 if (g.ErrorFirst)
11342 IMGUI_DEBUG_LOG_ERROR("[imgui-error] (current settings: Assert=%d, Log=%d, Tooltip=%d)\n",
11343 g.IO.ConfigErrorRecoveryEnableAssert, g.IO.ConfigErrorRecoveryEnableDebugLog, g.IO.ConfigErrorRecoveryEnableTooltip);
11344 IMGUI_DEBUG_LOG_ERROR("[imgui-error] In window '%s': %s\n", window ? window->Name : "NULL", msg);
11345 }
11346 g.ErrorFirst = false;
11347
11348 // Output to tooltip
11349 if (g.IO.ConfigErrorRecoveryEnableTooltip)
11350 {
11351 if (BeginErrorTooltip())
11352 {
11353 if (g.ErrorCountCurrentFrame < 20)
11354 {
11355 Text("In window '%s': %s", window ? window->Name : "NULL", msg);
11356 if (window && (!window->IsFallbackWindow || window->WasActive))
11357 GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 0, 0, 255));
11358 }
11359 if (g.ErrorCountCurrentFrame == 20)
11360 Text("(and more errors)");
11361 // EndFrame() will amend debug buttons to this window, after all errors have been submitted.
11362 EndErrorTooltip();
11363 }
11364 g.ErrorCountCurrentFrame++;
11365 }
11366#endif
11367
11368 // Output to callback
11369 if (g.ErrorCallback != NULL)
11370 g.ErrorCallback(&g, g.ErrorCallbackUserData, msg);
11371
11372 // Return whether we should assert
11373 return g.IO.ConfigErrorRecoveryEnableAssert;
11374}
11375
11376void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip()
11377{
11378#ifndef IMGUI_DISABLE_DEBUG_TOOLS
11379 ImGuiContext& g = *GImGui;
11380 if (g.DebugDrawIdConflicts != 0 && g.IO.KeyCtrl == false)
11381 g.DebugDrawIdConflictsCount = g.HoveredIdPreviousFrameItemCount;
11382 if (g.DebugDrawIdConflicts != 0 && g.DebugItemPickerActive == false && BeginErrorTooltip())
11383 {
11384 Text("Programmer error: %d visible items with conflicting ID!", g.DebugDrawIdConflictsCount);
11385 BulletText("Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!");
11386 BulletText("Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!");
11387 //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.
11388 BulletText("Set io.ConfigDebugDetectIdConflicts=false to disable this warning in non-programmers builds.");
11389 Separator();
11390 Text("(Hold CTRL to: use");
11391 SameLine();
11392 if (SmallButton("Item Picker"))
11393 DebugStartItemPicker();
11394 SameLine();
11395 Text("to break in item call-stack, or");
11396 SameLine();
11397 if (SmallButton("Open FAQ->About ID Stack System") && g.PlatformIO.Platform_OpenInShellFn != NULL)
11398 g.PlatformIO.Platform_OpenInShellFn(&g, "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage");
11399 EndErrorTooltip();
11400 }
11401
11402 if (g.ErrorCountCurrentFrame > 0 && BeginErrorTooltip()) // Amend at end of frame
11403 {
11404 Separator();
11405 Text("(Hold CTRL to:");
11406 SameLine();
11407 if (SmallButton("Enable Asserts"))
11408 g.IO.ConfigErrorRecoveryEnableAssert = true;
11409 //SameLine();
11410 //if (SmallButton("Hide Error Tooltips"))
11411 // g.IO.ConfigErrorRecoveryEnableTooltip = false; // Too dangerous
11412 SameLine(0, 0);
11413 Text(")");
11414 EndErrorTooltip();
11415 }
11416#endif
11417}
11418
11419// Pseudo-tooltip. Follow mouse until CTRL is held. When CTRL is held we lock position, allowing to click it.
11420bool ImGui::BeginErrorTooltip()
11421{
11422 ImGuiContext& g = *GImGui;
11423 ImGuiWindow* window = FindWindowByName("##Tooltip_Error");
11424 const bool use_locked_pos = (g.IO.KeyCtrl && window && window->WasActive);
11425 PushStyleColor(ImGuiCol_PopupBg, ImLerp(g.Style.Colors[ImGuiCol_PopupBg], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.15f));
11426 if (use_locked_pos)
11427 SetNextWindowPos(g.ErrorTooltipLockedPos);
11428 bool is_visible = Begin("##Tooltip_Error", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
11429 PopStyleColor();
11430 if (is_visible && g.CurrentWindow->BeginCount == 1)
11431 {
11432 SeparatorText("MESSAGE FROM DEAR IMGUI");
11433 BringWindowToDisplayFront(g.CurrentWindow);
11434 BringWindowToFocusFront(g.CurrentWindow);
11435 g.ErrorTooltipLockedPos = GetWindowPos();
11436 }
11437 else if (!is_visible)
11438 {
11439 End();
11440 }
11441 return is_visible;
11442}
11443
11444void ImGui::EndErrorTooltip()
11445{
11446 End();
11447}
11448
11449//-----------------------------------------------------------------------------
11450// [SECTION] ITEM SUBMISSION
11451//-----------------------------------------------------------------------------
11452// - KeepAliveID()
11453// - ItemAdd()
11454//-----------------------------------------------------------------------------
11455
11456// 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().
11457void ImGui::KeepAliveID(ImGuiID id)
11458{
11459 ImGuiContext& g = *GImGui;
11460 if (g.ActiveId == id)
11461 g.ActiveIdIsAlive = id;
11462 if (g.ActiveIdPreviousFrame == id)
11463 g.ActiveIdPreviousFrameIsAlive = true;
11464}
11465
11466// Declare item bounding box for clipping and interaction.
11467// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
11468// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
11469// THIS IS IN THE PERFORMANCE CRITICAL PATH (UNTIL THE CLIPPING TEST AND EARLY-RETURN)
11470IM_MSVC_RUNTIME_CHECKS_OFF
11471bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags)
11472{
11473 ImGuiContext& g = *GImGui;
11474 ImGuiWindow* window = g.CurrentWindow;
11475
11476 // Set item data
11477 // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set)
11478 g.LastItemData.ID = id;
11479 g.LastItemData.Rect = bb;
11480 g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb;
11481 g.LastItemData.ItemFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags;
11482 g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;
11483 // Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared.
11484
11485 if (id != 0)
11486 {
11487 KeepAliveID(id);
11488
11489 // Directional navigation processing
11490 // Runs prior to clipping early-out
11491 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
11492 // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
11493 // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
11494 // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
11495 // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
11496 // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
11497 // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
11498 // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
11499 if (!(g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))
11500 {
11501 // FIMXE-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test.
11502 window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
11503 if (g.NavId == id || g.NavAnyRequest)
11504 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
11505 if (window == g.NavWindow || ((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened))
11506 NavProcessItem();
11507 }
11508
11509 if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasShortcut)
11510 ItemHandleShortcut(id);
11511 }
11512
11513 // Lightweight clear of SetNextItemXXX data.
11514 g.NextItemData.HasFlags = ImGuiNextItemDataFlags_None;
11515 g.NextItemData.ItemFlags = ImGuiItemFlags_None;
11516
11517#ifdef IMGUI_ENABLE_TEST_ENGINE
11518 if (id != 0)
11519 IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData);
11520#endif
11521
11522 // Clipping test
11523 // (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')
11524 // g.NavActivateId is not necessarily == g.NavId, in the case of remote activation (e.g. shortcuts)
11525 const bool is_rect_visible = bb.Overlaps(window->ClipRect);
11526 if (!is_rect_visible)
11527 if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
11528 if (!g.ItemUnclipByLog)
11529 return false;
11530
11531 // [DEBUG]
11532#ifndef IMGUI_DISABLE_DEBUG_TOOLS
11533 if (id != 0)
11534 {
11535 if (id == g.DebugLocateId)
11536 DebugLocateItemResolveWithLastItem();
11537
11538 // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something".
11539 // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something".
11540 // READ THE FAQ: https://dearimgui.com/faq
11541 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!");
11542 }
11543 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
11544 //if ((g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav) == 0)
11545 // window->DrawList->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, IM_COL32(255,255,0,255)); // [DEBUG]
11546#endif
11547
11548 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
11549 if (is_rect_visible)
11550 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Visible;
11551 if (IsMouseHoveringRect(bb.Min, bb.Max))
11552 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect;
11553 return true;
11554}
11555IM_MSVC_RUNTIME_CHECKS_RESTORE
11556
11557//-----------------------------------------------------------------------------
11558// [SECTION] LAYOUT
11559//-----------------------------------------------------------------------------
11560// - ItemSize()
11561// - SameLine()
11562// - GetCursorScreenPos()
11563// - SetCursorScreenPos()
11564// - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
11565// - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
11566// - GetCursorStartPos()
11567// - Indent()
11568// - Unindent()
11569// - SetNextItemWidth()
11570// - PushItemWidth()
11571// - PushMultiItemsWidths()
11572// - PopItemWidth()
11573// - CalcItemWidth()
11574// - CalcItemSize()
11575// - GetTextLineHeight()
11576// - GetTextLineHeightWithSpacing()
11577// - GetFrameHeight()
11578// - GetFrameHeightWithSpacing()
11579// - GetContentRegionMax()
11580// - GetContentRegionAvail(),
11581// - BeginGroup()
11582// - EndGroup()
11583// Also see in imgui_widgets: tab bars, and in imgui_tables: tables, columns.
11584//-----------------------------------------------------------------------------
11585
11586// Advance cursor given item size for layout.
11587// Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
11588// See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.
11589// THIS IS IN THE PERFORMANCE CRITICAL PATH.
11590IM_MSVC_RUNTIME_CHECKS_OFF
11591void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
11592{
11593 ImGuiContext& g = *GImGui;
11594 ImGuiWindow* window = g.CurrentWindow;
11595 if (window->SkipItems)
11596 return;
11597
11598 // We increase the height in this function to accommodate for baseline offset.
11599 // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
11600 // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
11601 const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
11602
11603 const float line_y1 = window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y;
11604 const float line_height = ImMax(window->DC.CurrLineSize.y, /*ImMax(*/window->DC.CursorPos.y - line_y1/*, 0.0f)*/ + size.y + offset_to_match_baseline_y);
11605
11606 // Always align ourselves on pixel boundaries
11607 //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]
11608 window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
11609 window->DC.CursorPosPrevLine.y = line_y1;
11610 window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line
11611 window->DC.CursorPos.y = IM_TRUNC(line_y1 + line_height + g.Style.ItemSpacing.y); // Next line
11612 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
11613 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
11614 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
11615
11616 window->DC.PrevLineSize.y = line_height;
11617 window->DC.CurrLineSize.y = 0.0f;
11618 window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
11619 window->DC.CurrLineTextBaseOffset = 0.0f;
11620 window->DC.IsSameLine = window->DC.IsSetPos = false;
11621
11622 // Horizontal layout mode
11623 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
11624 SameLine();
11625}
11626IM_MSVC_RUNTIME_CHECKS_RESTORE
11627
11628// Gets back to previous line and continue with horizontal layout
11629// offset_from_start_x == 0 : follow right after previous item
11630// offset_from_start_x != 0 : align to specified x position (relative to window/group left)
11631// spacing_w < 0 : use default spacing if offset_from_start_x == 0, no spacing if offset_from_start_x != 0
11632// spacing_w >= 0 : enforce spacing amount
11633void ImGui::SameLine(float offset_from_start_x, float spacing_w)
11634{
11635 ImGuiContext& g = *GImGui;
11636 ImGuiWindow* window = g.CurrentWindow;
11637 if (window->SkipItems)
11638 return;
11639
11640 if (offset_from_start_x != 0.0f)
11641 {
11642 if (spacing_w < 0.0f)
11643 spacing_w = 0.0f;
11644 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
11645 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
11646 }
11647 else
11648 {
11649 if (spacing_w < 0.0f)
11650 spacing_w = g.Style.ItemSpacing.x;
11651 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
11652 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
11653 }
11654 window->DC.CurrLineSize = window->DC.PrevLineSize;
11655 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
11656 window->DC.IsSameLine = true;
11657}
11658
11659ImVec2 ImGui::GetCursorScreenPos()
11660{
11661 ImGuiWindow* window = GetCurrentWindowRead();
11662 return window->DC.CursorPos;
11663}
11664
11665void ImGui::SetCursorScreenPos(const ImVec2& pos)
11666{
11667 ImGuiWindow* window = GetCurrentWindow();
11668 window->DC.CursorPos = pos;
11669 //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
11670 window->DC.IsSetPos = true;
11671}
11672
11673// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
11674// 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'.
11675ImVec2 ImGui::GetCursorPos()
11676{
11677 ImGuiWindow* window = GetCurrentWindowRead();
11678 return window->DC.CursorPos - window->Pos + window->Scroll;
11679}
11680
11681float ImGui::GetCursorPosX()
11682{
11683 ImGuiWindow* window = GetCurrentWindowRead();
11684 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
11685}
11686
11687float ImGui::GetCursorPosY()
11688{
11689 ImGuiWindow* window = GetCurrentWindowRead();
11690 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
11691}
11692
11693void ImGui::SetCursorPos(const ImVec2& local_pos)
11694{
11695 ImGuiWindow* window = GetCurrentWindow();
11696 window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
11697 //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
11698 window->DC.IsSetPos = true;
11699}
11700
11701void ImGui::SetCursorPosX(float x)
11702{
11703 ImGuiWindow* window = GetCurrentWindow();
11704 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
11705 //window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
11706 window->DC.IsSetPos = true;
11707}
11708
11709void ImGui::SetCursorPosY(float y)
11710{
11711 ImGuiWindow* window = GetCurrentWindow();
11712 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
11713 //window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
11714 window->DC.IsSetPos = true;
11715}
11716
11717ImVec2 ImGui::GetCursorStartPos()
11718{
11719 ImGuiWindow* window = GetCurrentWindowRead();
11720 return window->DC.CursorStartPos - window->Pos;
11721}
11722
11723void ImGui::Indent(float indent_w)
11724{
11725 ImGuiContext& g = *GImGui;
11726 ImGuiWindow* window = GetCurrentWindow();
11727 window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
11728 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
11729}
11730
11731void ImGui::Unindent(float indent_w)
11732{
11733 ImGuiContext& g = *GImGui;
11734 ImGuiWindow* window = GetCurrentWindow();
11735 window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
11736 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
11737}
11738
11739// Affect large frame+labels widgets only.
11740void ImGui::SetNextItemWidth(float item_width)
11741{
11742 ImGuiContext& g = *GImGui;
11743 g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasWidth;
11744 g.NextItemData.Width = item_width;
11745}
11746
11747// FIXME: Remove the == 0.0f behavior?
11748void ImGui::PushItemWidth(float item_width)
11749{
11750 ImGuiContext& g = *GImGui;
11751 ImGuiWindow* window = g.CurrentWindow;
11752 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
11753 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
11754 g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth;
11755}
11756
11757void ImGui::PushMultiItemsWidths(int components, float w_full)
11758{
11759 ImGuiContext& g = *GImGui;
11760 ImGuiWindow* window = g.CurrentWindow;
11761 IM_ASSERT(components > 0);
11762 const ImGuiStyle& style = g.Style;
11763 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
11764 float w_items = w_full - style.ItemInnerSpacing.x * (components - 1);
11765 float prev_split = w_items;
11766 for (int i = components - 1; i > 0; i--)
11767 {
11768 float next_split = IM_TRUNC(w_items * i / components);
11769 window->DC.ItemWidthStack.push_back(ImMax(prev_split - next_split, 1.0f));
11770 prev_split = next_split;
11771 }
11772 window->DC.ItemWidth = ImMax(prev_split, 1.0f);
11773 g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth;
11774}
11775
11776void ImGui::PopItemWidth()
11777{
11778 ImGuiContext& g = *GImGui;
11779 ImGuiWindow* window = g.CurrentWindow;
11780 if (window->DC.ItemWidthStack.Size <= 0)
11781 {
11782 IM_ASSERT_USER_ERROR(0, "Calling PopItemWidth() too many times!");
11783 return;
11784 }
11785 window->DC.ItemWidth = window->DC.ItemWidthStack.back();
11786 window->DC.ItemWidthStack.pop_back();
11787}
11788
11789// Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
11790// The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
11791float ImGui::CalcItemWidth()
11792{
11793 ImGuiContext& g = *GImGui;
11794 ImGuiWindow* window = g.CurrentWindow;
11795 float w;
11796 if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth)
11797 w = g.NextItemData.Width;
11798 else
11799 w = window->DC.ItemWidth;
11800 if (w < 0.0f)
11801 {
11802 float region_avail_x = GetContentRegionAvail().x;
11803 w = ImMax(1.0f, region_avail_x + w);
11804 }
11805 w = IM_TRUNC(w);
11806 return w;
11807}
11808
11809// [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
11810// Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
11811// Note that only CalcItemWidth() is publicly exposed.
11812// The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)
11813ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
11814{
11815 ImVec2 avail;
11816 if (size.x < 0.0f || size.y < 0.0f)
11817 avail = GetContentRegionAvail();
11818
11819 if (size.x == 0.0f)
11820 size.x = default_w;
11821 else if (size.x < 0.0f)
11822 size.x = ImMax(4.0f, avail.x + size.x); // <-- size.x is negative here so we are subtracting
11823
11824 if (size.y == 0.0f)
11825 size.y = default_h;
11826 else if (size.y < 0.0f)
11827 size.y = ImMax(4.0f, avail.y + size.y); // <-- size.y is negative here so we are subtracting
11828
11829 return size;
11830}
11831
11832float ImGui::GetTextLineHeight()
11833{
11834 ImGuiContext& g = *GImGui;
11835 return g.FontSize;
11836}
11837
11838float ImGui::GetTextLineHeightWithSpacing()
11839{
11840 ImGuiContext& g = *GImGui;
11841 return g.FontSize + g.Style.ItemSpacing.y;
11842}
11843
11844float ImGui::GetFrameHeight()
11845{
11846 ImGuiContext& g = *GImGui;
11847 return g.FontSize + g.Style.FramePadding.y * 2.0f;
11848}
11849
11850float ImGui::GetFrameHeightWithSpacing()
11851{
11852 ImGuiContext& g = *GImGui;
11853 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
11854}
11855
11856ImVec2 ImGui::GetContentRegionAvail()
11857{
11858 ImGuiContext& g = *GImGui;
11859 ImGuiWindow* window = g.CurrentWindow;
11860 ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max;
11861 return mx - window->DC.CursorPos;
11862}
11863
11864#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
11865
11866// You should never need those functions. Always use GetCursorScreenPos() and GetContentRegionAvail()!
11867// They are bizarre local-coordinates which don't play well with scrolling.
11868ImVec2 ImGui::GetContentRegionMax()
11869{
11870 return GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos();
11871}
11872
11873ImVec2 ImGui::GetWindowContentRegionMin()
11874{
11875 ImGuiWindow* window = GImGui->CurrentWindow;
11876 return window->ContentRegionRect.Min - window->Pos;
11877}
11878
11879ImVec2 ImGui::GetWindowContentRegionMax()
11880{
11881 ImGuiWindow* window = GImGui->CurrentWindow;
11882 return window->ContentRegionRect.Max - window->Pos;
11883}
11884#endif
11885
11886// 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.)
11887// Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
11888// FIXME-OPT: Could we safely early out on ->SkipItems?
11889void ImGui::BeginGroup()
11890{
11891 ImGuiContext& g = *GImGui;
11892 ImGuiWindow* window = g.CurrentWindow;
11893
11894 g.GroupStack.resize(g.GroupStack.Size + 1);
11895 ImGuiGroupData& group_data = g.GroupStack.back();
11896 group_data.WindowID = window->ID;
11897 group_data.BackupCursorPos = window->DC.CursorPos;
11898 group_data.BackupCursorPosPrevLine = window->DC.CursorPosPrevLine;
11899 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
11900 group_data.BackupIndent = window->DC.Indent;
11901 group_data.BackupGroupOffset = window->DC.GroupOffset;
11902 group_data.BackupCurrLineSize = window->DC.CurrLineSize;
11903 group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
11904 group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
11905 group_data.BackupHoveredIdIsAlive = g.HoveredId != 0;
11906 group_data.BackupIsSameLine = window->DC.IsSameLine;
11907 group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
11908 group_data.EmitItem = true;
11909
11910 window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
11911 window->DC.Indent = window->DC.GroupOffset;
11912 window->DC.CursorMaxPos = window->DC.CursorPos;
11913 window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
11914 if (g.LogEnabled)
11915 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
11916}
11917
11918void ImGui::EndGroup()
11919{
11920 ImGuiContext& g = *GImGui;
11921 ImGuiWindow* window = g.CurrentWindow;
11922 IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls
11923
11924 ImGuiGroupData& group_data = g.GroupStack.back();
11925 IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window?
11926
11927 if (window->DC.IsSetPos)
11928 ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
11929
11930 // Include LastItemData.Rect.Max as a workaround for e.g. EndTable() undershooting with CursorMaxPos report. (#7543)
11931 ImRect group_bb(group_data.BackupCursorPos, ImMax(ImMax(window->DC.CursorMaxPos, g.LastItemData.Rect.Max), group_data.BackupCursorPos));
11932 window->DC.CursorPos = group_data.BackupCursorPos;
11933 window->DC.CursorPosPrevLine = group_data.BackupCursorPosPrevLine;
11934 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, group_bb.Max);
11935 window->DC.Indent = group_data.BackupIndent;
11936 window->DC.GroupOffset = group_data.BackupGroupOffset;
11937 window->DC.CurrLineSize = group_data.BackupCurrLineSize;
11938 window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
11939 window->DC.IsSameLine = group_data.BackupIsSameLine;
11940 if (g.LogEnabled)
11941 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
11942
11943 if (!group_data.EmitItem)
11944 {
11945 g.GroupStack.pop_back();
11946 return;
11947 }
11948
11949 window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.
11950 ItemSize(group_bb.GetSize());
11951 ItemAdd(group_bb, 0, NULL, ImGuiItemFlags_NoTabStop);
11952
11953 // 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.
11954 // 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.
11955 // Also if you grep for LastItemId you'll notice it is only used in that context.
11956 // (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.)
11957 const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
11958 const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true);
11959 if (group_contains_curr_active_id)
11960 g.LastItemData.ID = g.ActiveId;
11961 else if (group_contains_prev_active_id)
11962 g.LastItemData.ID = g.ActiveIdPreviousFrame;
11963 g.LastItemData.Rect = group_bb;
11964
11965 // Forward Hovered flag
11966 const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0;
11967 if (group_contains_curr_hovered_id)
11968 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
11969
11970 // Forward Edited flag
11971 if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
11972 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
11973
11974 // Forward Deactivated flag
11975 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
11976 if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
11977 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated;
11978
11979 g.GroupStack.pop_back();
11980 if (g.DebugShowGroupRects)
11981 window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
11982}
11983
11984
11985//-----------------------------------------------------------------------------
11986// [SECTION] SCROLLING
11987//-----------------------------------------------------------------------------
11988
11989// Helper to snap on edges when aiming at an item very close to the edge,
11990// So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
11991// When we refactor the scrolling API this may be configurable with a flag?
11992// Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.
11993static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
11994{
11995 if (target <= snap_min + snap_threshold)
11996 return ImLerp(snap_min, target, center_ratio);
11997 if (target >= snap_max - snap_threshold)
11998 return ImLerp(target, snap_max, center_ratio);
11999 return target;
12000}
12001
12002static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
12003{
12004 ImVec2 scroll = window->Scroll;
12005 ImVec2 decoration_size(window->DecoOuterSizeX1 + window->DecoInnerSizeX1 + window->DecoOuterSizeX2, window->DecoOuterSizeY1 + window->DecoInnerSizeY1 + window->DecoOuterSizeY2);
12006 for (int axis = 0; axis < 2; axis++)
12007 {
12008 if (window->ScrollTarget[axis] < FLT_MAX)
12009 {
12010 float center_ratio = window->ScrollTargetCenterRatio[axis];
12011 float scroll_target = window->ScrollTarget[axis];
12012 if (window->ScrollTargetEdgeSnapDist[axis] > 0.0f)
12013 {
12014 float snap_min = 0.0f;
12015 float snap_max = window->ScrollMax[axis] + window->SizeFull[axis] - decoration_size[axis];
12016 scroll_target = CalcScrollEdgeSnap(scroll_target, snap_min, snap_max, window->ScrollTargetEdgeSnapDist[axis], center_ratio);
12017 }
12018 scroll[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]);
12019 }
12020 scroll[axis] = IM_ROUND(ImMax(scroll[axis], 0.0f));
12021 if (!window->Collapsed && !window->SkipItems)
12022 scroll[axis] = ImMin(scroll[axis], window->ScrollMax[axis]);
12023 }
12024 return scroll;
12025}
12026
12027void ImGui::ScrollToItem(ImGuiScrollFlags flags)
12028{
12029 ImGuiContext& g = *GImGui;
12030 ImGuiWindow* window = g.CurrentWindow;
12031 ScrollToRectEx(window, g.LastItemData.NavRect, flags);
12032}
12033
12034void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
12035{
12036 ScrollToRectEx(window, item_rect, flags);
12037}
12038
12039// Scroll to keep newly navigated item fully into view
12040ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
12041{
12042 ImGuiContext& g = *GImGui;
12043 ImRect scroll_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
12044 scroll_rect.Min.x = ImMin(scroll_rect.Min.x + window->DecoInnerSizeX1, scroll_rect.Max.x);
12045 scroll_rect.Min.y = ImMin(scroll_rect.Min.y + window->DecoInnerSizeY1, scroll_rect.Max.y);
12046 //GetForegroundDrawList(window)->AddRect(item_rect.Min, item_rect.Max, IM_COL32(255,0,0,255), 0.0f, 0, 5.0f); // [DEBUG]
12047 //GetForegroundDrawList(window)->AddRect(scroll_rect.Min, scroll_rect.Max, IM_COL32_WHITE); // [DEBUG]
12048
12049 // Check that only one behavior is selected per axis
12050 IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_));
12051 IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_));
12052
12053 // Defaults
12054 ImGuiScrollFlags in_flags = flags;
12055 if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX)
12056 flags |= ImGuiScrollFlags_KeepVisibleEdgeX;
12057 if ((flags & ImGuiScrollFlags_MaskY_) == 0)
12058 flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY;
12059
12060 const bool fully_visible_x = item_rect.Min.x >= scroll_rect.Min.x && item_rect.Max.x <= scroll_rect.Max.x;
12061 const bool fully_visible_y = item_rect.Min.y >= scroll_rect.Min.y && item_rect.Max.y <= scroll_rect.Max.y;
12062 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;
12063 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;
12064
12065 if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x)
12066 {
12067 if (item_rect.Min.x < scroll_rect.Min.x || !can_be_fully_visible_x)
12068 SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f);
12069 else if (item_rect.Max.x >= scroll_rect.Max.x)
12070 SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f);
12071 }
12072 else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX))
12073 {
12074 if (can_be_fully_visible_x)
12075 SetScrollFromPosX(window, ImTrunc((item_rect.Min.x + item_rect.Max.x) * 0.5f) - window->Pos.x, 0.5f);
12076 else
12077 SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x, 0.0f);
12078 }
12079
12080 if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y)
12081 {
12082 if (item_rect.Min.y < scroll_rect.Min.y || !can_be_fully_visible_y)
12083 SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f);
12084 else if (item_rect.Max.y >= scroll_rect.Max.y)
12085 SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f);
12086 }
12087 else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY))
12088 {
12089 if (can_be_fully_visible_y)
12090 SetScrollFromPosY(window, ImTrunc((item_rect.Min.y + item_rect.Max.y) * 0.5f) - window->Pos.y, 0.5f);
12091 else
12092 SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y, 0.0f);
12093 }
12094
12095 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
12096 ImVec2 delta_scroll = next_scroll - window->Scroll;
12097
12098 // Also scroll parent window to keep us into view if necessary
12099 if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow))
12100 {
12101 // FIXME-SCROLL: May be an option?
12102 if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0)
12103 in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX;
12104 if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0)
12105 in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY;
12106 delta_scroll += ScrollToRectEx(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags);
12107 }
12108
12109 return delta_scroll;
12110}
12111
12112float ImGui::GetScrollX()
12113{
12114 ImGuiWindow* window = GImGui->CurrentWindow;
12115 return window->Scroll.x;
12116}
12117
12118float ImGui::GetScrollY()
12119{
12120 ImGuiWindow* window = GImGui->CurrentWindow;
12121 return window->Scroll.y;
12122}
12123
12124float ImGui::GetScrollMaxX()
12125{
12126 ImGuiWindow* window = GImGui->CurrentWindow;
12127 return window->ScrollMax.x;
12128}
12129
12130float ImGui::GetScrollMaxY()
12131{
12132 ImGuiWindow* window = GImGui->CurrentWindow;
12133 return window->ScrollMax.y;
12134}
12135
12136void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)
12137{
12138 window->ScrollTarget.x = scroll_x;
12139 window->ScrollTargetCenterRatio.x = 0.0f;
12140 window->ScrollTargetEdgeSnapDist.x = 0.0f;
12141}
12142
12143void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)
12144{
12145 window->ScrollTarget.y = scroll_y;
12146 window->ScrollTargetCenterRatio.y = 0.0f;
12147 window->ScrollTargetEdgeSnapDist.y = 0.0f;
12148}
12149
12150void ImGui::SetScrollX(float scroll_x)
12151{
12152 ImGuiContext& g = *GImGui;
12153 SetScrollX(g.CurrentWindow, scroll_x);
12154}
12155
12156void ImGui::SetScrollY(float scroll_y)
12157{
12158 ImGuiContext& g = *GImGui;
12159 SetScrollY(g.CurrentWindow, scroll_y);
12160}
12161
12162// Note that a local position will vary depending on initial scroll value,
12163// This is a little bit confusing so bear with us:
12164// - local_pos = (absolution_pos - window->Pos)
12165// - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,
12166// and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.
12167// - They mostly exist because of legacy API.
12168// Following the rules above, when trying to work with scrolling code, consider that:
12169// - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!
12170// - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense
12171// 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
12172void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
12173{
12174 IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
12175 window->ScrollTarget.x = IM_TRUNC(local_x - window->DecoOuterSizeX1 - window->DecoInnerSizeX1 + window->Scroll.x); // Convert local position to scroll offset
12176 window->ScrollTargetCenterRatio.x = center_x_ratio;
12177 window->ScrollTargetEdgeSnapDist.x = 0.0f;
12178}
12179
12180void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
12181{
12182 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
12183 window->ScrollTarget.y = IM_TRUNC(local_y - window->DecoOuterSizeY1 - window->DecoInnerSizeY1 + window->Scroll.y); // Convert local position to scroll offset
12184 window->ScrollTargetCenterRatio.y = center_y_ratio;
12185 window->ScrollTargetEdgeSnapDist.y = 0.0f;
12186}
12187
12188void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
12189{
12190 ImGuiContext& g = *GImGui;
12191 SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
12192}
12193
12194void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
12195{
12196 ImGuiContext& g = *GImGui;
12197 SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
12198}
12199
12200// center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item.
12201void ImGui::SetScrollHereX(float center_x_ratio)
12202{
12203 ImGuiContext& g = *GImGui;
12204 ImGuiWindow* window = g.CurrentWindow;
12205 float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x);
12206 float target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio);
12207 SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos
12208
12209 // Tweak: snap on edges when aiming at an item very close to the edge
12210 window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x);
12211}
12212
12213// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
12214void ImGui::SetScrollHereY(float center_y_ratio)
12215{
12216 ImGuiContext& g = *GImGui;
12217 ImGuiWindow* window = g.CurrentWindow;
12218 float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y);
12219 float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);
12220 SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos
12221
12222 // Tweak: snap on edges when aiming at an item very close to the edge
12223 window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y);
12224}
12225
12226//-----------------------------------------------------------------------------
12227// [SECTION] TOOLTIPS
12228//-----------------------------------------------------------------------------
12229
12230bool ImGui::BeginTooltip()
12231{
12232 return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
12233}
12234
12235bool ImGui::BeginItemTooltip()
12236{
12237 if (!IsItemHovered(ImGuiHoveredFlags_ForTooltip))
12238 return false;
12239 return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
12240}
12241
12242bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags)
12243{
12244 ImGuiContext& g = *GImGui;
12245
12246 const bool is_dragdrop_tooltip = g.DragDropWithinSource || g.DragDropWithinTarget;
12247 if (is_dragdrop_tooltip)
12248 {
12249 // Drag and Drop tooltips are positioning differently than other tooltips:
12250 // - offset visibility to increase visibility around mouse.
12251 // - never clamp within outer viewport boundary.
12252 // We call SetNextWindowPos() to enforce position and disable clamping.
12253 // See FindBestWindowPosForPopup() for positionning logic of other tooltips (not drag and drop ones).
12254 //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
12255 const bool is_touchscreen = (g.IO.MouseSource == ImGuiMouseSource_TouchScreen);
12256 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
12257 {
12258 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);
12259 ImVec2 tooltip_pivot = is_touchscreen ? TOOLTIP_DEFAULT_PIVOT_TOUCH : ImVec2(0.0f, 0.0f);
12260 SetNextWindowPos(tooltip_pos, ImGuiCond_None, tooltip_pivot);
12261 }
12262
12263 SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
12264 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
12265 tooltip_flags |= ImGuiTooltipFlags_OverridePrevious;
12266 }
12267
12268 const char* window_name_template = is_dragdrop_tooltip ? "##Tooltip_DragDrop_%02d" : "##Tooltip_%02d";
12269 char window_name[32];
12270 ImFormatString(window_name, IM_ARRAYSIZE(window_name), window_name_template, g.TooltipOverrideCount);
12271 if ((tooltip_flags & ImGuiTooltipFlags_OverridePrevious) && g.TooltipPreviousWindow != NULL && g.TooltipPreviousWindow->Active)
12272 {
12273 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
12274 //IMGUI_DEBUG_LOG("[tooltip] '%s' already active, using +1 for this frame\n", window_name);
12275 SetWindowHiddenAndSkipItemsForCurrentFrame(g.TooltipPreviousWindow);
12276 ImFormatString(window_name, IM_ARRAYSIZE(window_name), window_name_template, ++g.TooltipOverrideCount);
12277 }
12278
12279 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking;
12280 Begin(window_name, NULL, flags | extra_window_flags);
12281 // 2023-03-09: Added bool return value to the API, but currently always returning true.
12282 // If this ever returns false we need to update BeginDragDropSource() accordingly.
12283 //if (!ret)
12284 // End();
12285 //return ret;
12286 return true;
12287}
12288
12289void ImGui::EndTooltip()
12290{
12291 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
12292 End();
12293}
12294
12295void ImGui::SetTooltip(const char* fmt, ...)
12296{
12297 va_list args;
12298 va_start(args, fmt);
12299 SetTooltipV(fmt, args);
12300 va_end(args);
12301}
12302
12303void ImGui::SetTooltipV(const char* fmt, va_list args)
12304{
12305 if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))
12306 return;
12307 TextV(fmt, args);
12308 EndTooltip();
12309}
12310
12311// Shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'.
12312// Defaults to == ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort when using the mouse.
12313void ImGui::SetItemTooltip(const char* fmt, ...)
12314{
12315 va_list args;
12316 va_start(args, fmt);
12317 if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
12318 SetTooltipV(fmt, args);
12319 va_end(args);
12320}
12321
12322void ImGui::SetItemTooltipV(const char* fmt, va_list args)
12323{
12324 if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
12325 SetTooltipV(fmt, args);
12326}
12327
12328
12329//-----------------------------------------------------------------------------
12330// [SECTION] POPUPS
12331//-----------------------------------------------------------------------------
12332
12333// Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
12334bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
12335{
12336 ImGuiContext& g = *GImGui;
12337 if (popup_flags & ImGuiPopupFlags_AnyPopupId)
12338 {
12339 // Return true if any popup is open at the current BeginPopup() level of the popup stack
12340 // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
12341 IM_ASSERT(id == 0);
12342 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
12343 return g.OpenPopupStack.Size > 0;
12344 else
12345 return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
12346 }
12347 else
12348 {
12349 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
12350 {
12351 // Return true if the popup is open anywhere in the popup stack
12352 for (ImGuiPopupData& popup_data : g.OpenPopupStack)
12353 if (popup_data.PopupId == id)
12354 return true;
12355 return false;
12356 }
12357 else
12358 {
12359 // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
12360 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
12361 }
12362 }
12363}
12364
12365bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
12366{
12367 ImGuiContext& g = *GImGui;
12368 ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);
12369 if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
12370 IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
12371 return IsPopupOpen(id, popup_flags);
12372}
12373
12374// Also see FindBlockingModal(NULL)
12375ImGuiWindow* ImGui::GetTopMostPopupModal()
12376{
12377 ImGuiContext& g = *GImGui;
12378 for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
12379 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
12380 if (popup->Flags & ImGuiWindowFlags_Modal)
12381 return popup;
12382 return NULL;
12383}
12384
12385// See Demo->Stacked Modal to confirm what this is for.
12386ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal()
12387{
12388 ImGuiContext& g = *GImGui;
12389 for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
12390 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
12391 if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup))
12392 return popup;
12393 return NULL;
12394}
12395
12396void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
12397{
12398 ImGuiContext& g = *GImGui;
12399 ImGuiID id = g.CurrentWindow->GetID(str_id);
12400 IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopup(\"%s\" -> 0x%08X)\n", str_id, id);
12401 OpenPopupEx(id, popup_flags);
12402}
12403
12404void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags)
12405{
12406 OpenPopupEx(id, popup_flags);
12407}
12408
12409// Mark popup as open (toggle toward open state).
12410// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
12411// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
12412// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
12413void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
12414{
12415 ImGuiContext& g = *GImGui;
12416 ImGuiWindow* parent_window = g.CurrentWindow;
12417 const int current_stack_size = g.BeginPopupStack.Size;
12418
12419 if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
12420 if (IsPopupOpen((ImGuiID)0, ImGuiPopupFlags_AnyPopupId))
12421 return;
12422
12423 ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
12424 popup_ref.PopupId = id;
12425 popup_ref.Window = NULL;
12426 popup_ref.RestoreNavWindow = g.NavWindow; // When popup closes focus may be restored to NavWindow (depend on window type).
12427 popup_ref.OpenFrameCount = g.FrameCount;
12428 popup_ref.OpenParentId = parent_window->IDStack.back();
12429 popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
12430 popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
12431
12432 IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopupEx(0x%08X)\n", id);
12433 if (g.OpenPopupStack.Size < current_stack_size + 1)
12434 {
12435 g.OpenPopupStack.push_back(popup_ref);
12436 }
12437 else
12438 {
12439 // Gently handle the user mistakenly calling OpenPopup() every frames: it is likely a programming mistake!
12440 // However, if we were to run the regular code path, the ui would become completely unusable because the popup will always be
12441 // in hidden-while-calculating-size state _while_ claiming focus. Which is extremely confusing situation for the programmer.
12442 // Instead, for successive frames calls to OpenPopup(), we silently avoid reopening even if ImGuiPopupFlags_NoReopen is not specified.
12443 bool keep_existing = false;
12444 if (g.OpenPopupStack[current_stack_size].PopupId == id)
12445 if ((g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) || (popup_flags & ImGuiPopupFlags_NoReopen))
12446 keep_existing = true;
12447 if (keep_existing)
12448 {
12449 // No reopen
12450 g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
12451 }
12452 else
12453 {
12454 // Reopen: close child popups if any, then flag popup for open/reopen (set position, focus, init navigation)
12455 ClosePopupToLevel(current_stack_size, true);
12456 g.OpenPopupStack.push_back(popup_ref);
12457 }
12458
12459 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
12460 // This is equivalent to what ClosePopupToLevel() does.
12461 //if (g.OpenPopupStack[current_stack_size].PopupId == id)
12462 // FocusWindow(parent_window);
12463 }
12464}
12465
12466// When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
12467// This function closes any popups that are over 'ref_window'.
12468void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
12469{
12470 ImGuiContext& g = *GImGui;
12471 if (g.OpenPopupStack.Size == 0)
12472 return;
12473
12474 // Don't close our own child popup windows.
12475 //IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\") restore_under=%d\n", ref_window ? ref_window->Name : "<NULL>", restore_focus_to_window_under_popup);
12476 int popup_count_to_keep = 0;
12477 if (ref_window)
12478 {
12479 // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
12480 for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
12481 {
12482 ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
12483 if (!popup.Window)
12484 continue;
12485 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
12486
12487 // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
12488 // - Clicking/Focusing Window2 won't close Popup1:
12489 // Window -> Popup1 -> Window2(Ref)
12490 // - Clicking/focusing Popup1 will close Popup2 and Popup3:
12491 // Window -> Popup1(Ref) -> Popup2 -> Popup3
12492 // - Each popups may contain child windows, which is why we compare ->RootWindowDockTree!
12493 // Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
12494 // We step through every popup from bottom to top to validate their position relative to reference window.
12495 bool ref_window_is_descendent_of_popup = false;
12496 for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
12497 if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
12498 //if (popup_window->RootWindowDockTree == ref_window->RootWindowDockTree) // FIXME-MERGE
12499 if (IsWindowWithinBeginStackOf(ref_window, popup_window))
12500 {
12501 ref_window_is_descendent_of_popup = true;
12502 break;
12503 }
12504 if (!ref_window_is_descendent_of_popup)
12505 break;
12506 }
12507 }
12508 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
12509 {
12510 IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\")\n", ref_window ? ref_window->Name : "<NULL>");
12511 ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
12512 }
12513}
12514
12515void ImGui::ClosePopupsExceptModals()
12516{
12517 ImGuiContext& g = *GImGui;
12518
12519 int popup_count_to_keep;
12520 for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--)
12521 {
12522 ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window;
12523 if (!window || (window->Flags & ImGuiWindowFlags_Modal))
12524 break;
12525 }
12526 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
12527 ClosePopupToLevel(popup_count_to_keep, true);
12528}
12529
12530void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
12531{
12532 ImGuiContext& g = *GImGui;
12533 IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupToLevel(%d), restore_under=%d\n", remaining, restore_focus_to_window_under_popup);
12534 IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
12535 if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup)
12536 for (int n = remaining; n < g.OpenPopupStack.Size; n++)
12537 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);
12538
12539 // Trim open popup stack
12540 ImGuiPopupData prev_popup = g.OpenPopupStack[remaining];
12541 g.OpenPopupStack.resize(remaining);
12542
12543 // 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)
12544 if (restore_focus_to_window_under_popup && prev_popup.Window)
12545 {
12546 ImGuiWindow* popup_window = prev_popup.Window;
12547 ImGuiWindow* focus_window = (popup_window->Flags & ImGuiWindowFlags_ChildMenu) ? popup_window->ParentWindow : prev_popup.RestoreNavWindow;
12548 if (focus_window && !focus_window->WasActive)
12549 FocusTopMostWindowUnderOne(popup_window, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild); // Fallback
12550 else
12551 FocusWindow(focus_window, (g.NavLayer == ImGuiNavLayer_Main) ? ImGuiFocusRequestFlags_RestoreFocusedChild : ImGuiFocusRequestFlags_None);
12552 }
12553}
12554
12555// Close the popup we have begin-ed into.
12556void ImGui::CloseCurrentPopup()
12557{
12558 ImGuiContext& g = *GImGui;
12559 int popup_idx = g.BeginPopupStack.Size - 1;
12560 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
12561 return;
12562
12563 // Closing a menu closes its top-most parent popup (unless a modal)
12564 while (popup_idx > 0)
12565 {
12566 ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
12567 ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
12568 bool close_parent = false;
12569 if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
12570 if (parent_popup_window && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar))
12571 close_parent = true;
12572 if (!close_parent)
12573 break;
12574 popup_idx--;
12575 }
12576 IMGUI_DEBUG_LOG_POPUP("[popup] CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
12577 ClosePopupToLevel(popup_idx, true);
12578
12579 // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
12580 // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
12581 // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
12582 if (ImGuiWindow* window = g.NavWindow)
12583 window->DC.NavHideHighlightOneFrame = true;
12584}
12585
12586// Attention! BeginPopup() adds default flags when calling BeginPopupEx()!
12587bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags)
12588{
12589 ImGuiContext& g = *GImGui;
12590 if (!IsPopupOpen(id, ImGuiPopupFlags_None))
12591 {
12592 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12593 return false;
12594 }
12595
12596 char name[20];
12597 if (extra_window_flags & ImGuiWindowFlags_ChildMenu)
12598 ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginMenuDepth); // Recycle windows based on depth
12599 else
12600 ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
12601
12602 bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoDocking);
12603 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
12604 EndPopup();
12605
12606 //g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack;
12607
12608 return is_open;
12609}
12610
12611bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
12612{
12613 ImGuiContext& g = *GImGui;
12614 if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
12615 {
12616 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12617 return false;
12618 }
12619 flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
12620 ImGuiID id = g.CurrentWindow->GetID(str_id);
12621 return BeginPopupEx(id, flags);
12622}
12623
12624// If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
12625// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup).
12626// - *p_open set back to false in BeginPopupModal() when popup is not open.
12627// - if you set *p_open to false before calling BeginPopupModal(), it will close the popup.
12628bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
12629{
12630 ImGuiContext& g = *GImGui;
12631 ImGuiWindow* window = g.CurrentWindow;
12632 const ImGuiID id = window->GetID(name);
12633 if (!IsPopupOpen(id, ImGuiPopupFlags_None))
12634 {
12635 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12636 if (p_open && *p_open)
12637 *p_open = false;
12638 return false;
12639 }
12640
12641 // Center modal windows by default for increased visibility
12642 // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
12643 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
12644 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
12645 {
12646 const ImGuiViewport* viewport = window->WasActive ? window->Viewport : GetMainViewport(); // FIXME-VIEWPORT: What may be our reference viewport?
12647 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
12648 }
12649
12650 flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking;
12651 const bool is_open = Begin(name, p_open, flags);
12652 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
12653 {
12654 EndPopup();
12655 if (is_open)
12656 ClosePopupToLevel(g.BeginPopupStack.Size, true);
12657 return false;
12658 }
12659 return is_open;
12660}
12661
12662void ImGui::EndPopup()
12663{
12664 ImGuiContext& g = *GImGui;
12665 ImGuiWindow* window = g.CurrentWindow;
12666 IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
12667 IM_ASSERT(g.BeginPopupStack.Size > 0);
12668
12669 // 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)
12670 if (g.NavWindow == window)
12671 NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
12672
12673 // Child-popups don't need to be laid out
12674 IM_ASSERT(g.WithinEndChild == false);
12675 if (window->Flags & ImGuiWindowFlags_ChildWindow)
12676 g.WithinEndChild = true;
12677 End();
12678 g.WithinEndChild = false;
12679}
12680
12681// Helper to open a popup if mouse button is released over the item
12682// - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()
12683void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)
12684{
12685 ImGuiContext& g = *GImGui;
12686 ImGuiWindow* window = g.CurrentWindow;
12687 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12688 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
12689 {
12690 ImGuiID id = str_id ? window->GetID(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!
12691 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
12692 OpenPopupEx(id, popup_flags);
12693 }
12694}
12695
12696// This is a helper to handle the simplest case of associating one named popup to one given widget.
12697// - To create a popup associated to the last item, you generally want to pass a NULL value to str_id.
12698// - To create a popup with a specific identifier, pass it in str_id.
12699// - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call.
12700// - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id.
12701// - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
12702// This is essentially the same as:
12703// id = str_id ? GetID(str_id) : GetItemID();
12704// OpenPopupOnItemClick(str_id, ImGuiPopupFlags_MouseButtonRight);
12705// return BeginPopup(id);
12706// Which is essentially the same as:
12707// id = str_id ? GetID(str_id) : GetItemID();
12708// if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
12709// OpenPopup(id);
12710// return BeginPopup(id);
12711// The main difference being that this is tweaked to avoid computing the ID twice.
12712bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
12713{
12714 ImGuiContext& g = *GImGui;
12715 ImGuiWindow* window = g.CurrentWindow;
12716 if (window->SkipItems)
12717 return false;
12718 ImGuiID id = str_id ? window->GetID(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!
12719 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
12720 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12721 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
12722 OpenPopupEx(id, popup_flags);
12723 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12724}
12725
12726bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
12727{
12728 ImGuiContext& g = *GImGui;
12729 ImGuiWindow* window = g.CurrentWindow;
12730 if (!str_id)
12731 str_id = "window_context";
12732 ImGuiID id = window->GetID(str_id);
12733 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12734 if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
12735 if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
12736 OpenPopupEx(id, popup_flags);
12737 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12738}
12739
12740bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
12741{
12742 ImGuiContext& g = *GImGui;
12743 ImGuiWindow* window = g.CurrentWindow;
12744 if (!str_id)
12745 str_id = "void_context";
12746 ImGuiID id = window->GetID(str_id);
12747 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12748 if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
12749 if (GetTopMostPopupModal() == NULL)
12750 OpenPopupEx(id, popup_flags);
12751 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12752}
12753
12754// 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.)
12755// 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.
12756// (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor
12757// information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.
12758// this allows us to have tooltips/popups displayed out of the parent viewport.)
12759ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
12760{
12761 ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
12762 //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
12763 //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
12764
12765 // Combo Box policy (we want a connecting edge)
12766 if (policy == ImGuiPopupPositionPolicy_ComboBox)
12767 {
12768 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
12769 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
12770 {
12771 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
12772 if (n != -1 && dir == *last_dir) // Already tried this direction?
12773 continue;
12774 ImVec2 pos;
12775 if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
12776 if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
12777 if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
12778 if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
12779 if (!r_outer.Contains(ImRect(pos, pos + size)))
12780 continue;
12781 *last_dir = dir;
12782 return pos;
12783 }
12784 }
12785
12786 // Tooltip and Default popup policy
12787 // (Always first try the direction we used on the last frame, if any)
12788 if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default)
12789 {
12790 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
12791 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
12792 {
12793 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
12794 if (n != -1 && dir == *last_dir) // Already tried this direction?
12795 continue;
12796
12797 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);
12798 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);
12799
12800 // 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)
12801 if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right))
12802 continue;
12803 if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down))
12804 continue;
12805
12806 ImVec2 pos;
12807 pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
12808 pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
12809
12810 // Clamp top-left corner of popup
12811 pos.x = ImMax(pos.x, r_outer.Min.x);
12812 pos.y = ImMax(pos.y, r_outer.Min.y);
12813
12814 *last_dir = dir;
12815 return pos;
12816 }
12817 }
12818
12819 // Fallback when not enough room:
12820 *last_dir = ImGuiDir_None;
12821
12822 // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
12823 if (policy == ImGuiPopupPositionPolicy_Tooltip)
12824 return ref_pos + ImVec2(2, 2);
12825
12826 // Otherwise try to keep within display
12827 ImVec2 pos = ref_pos;
12828 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
12829 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
12830 return pos;
12831}
12832
12833// Note that this is used for popups, which can overlap the non work-area of individual viewports.
12834ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window)
12835{
12836 ImGuiContext& g = *GImGui;
12837 ImRect r_screen;
12838 if (window->ViewportAllowPlatformMonitorExtend >= 0)
12839 {
12840 // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here)
12841 const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportAllowPlatformMonitorExtend];
12842 r_screen.Min = monitor.WorkPos;
12843 r_screen.Max = monitor.WorkPos + monitor.WorkSize;
12844 }
12845 else
12846 {
12847 // Use the full viewport area (not work area) for popups
12848 r_screen = window->Viewport->GetMainRect();
12849 }
12850 ImVec2 padding = g.Style.DisplaySafeAreaPadding;
12851 r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
12852 return r_screen;
12853}
12854
12855ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
12856{
12857 ImGuiContext& g = *GImGui;
12858
12859 ImRect r_outer = GetPopupAllowedExtentRect(window);
12860 if (window->Flags & ImGuiWindowFlags_ChildMenu)
12861 {
12862 // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
12863 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
12864 ImGuiWindow* parent_window = window->ParentWindow;
12865 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).
12866 ImRect r_avoid;
12867 if (parent_window->DC.MenuBarAppending)
12868 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
12869 else
12870 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);
12871 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
12872 }
12873 if (window->Flags & ImGuiWindowFlags_Popup)
12874 {
12875 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, ImRect(window->Pos, window->Pos), ImGuiPopupPositionPolicy_Default); // Ideally we'd disable r_avoid here
12876 }
12877 if (window->Flags & ImGuiWindowFlags_Tooltip)
12878 {
12879 // Position tooltip (always follows mouse + clamp within outer boundaries)
12880 // FIXME:
12881 // - 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).
12882 // - Drag and drop tooltips are not using this path either: BeginTooltipEx() manually sets their position.
12883 // - Require some tidying up. In theory we could handle both cases in same location, but requires a bit of shuffling
12884 // as drag and drop tooltips are calling SetNextWindowPos() leading to 'window_pos_set_by_api' being set in Begin().
12885 IM_ASSERT(g.CurrentWindow == window);
12886 const float scale = g.Style.MouseCursorScale;
12887 const ImVec2 ref_pos = NavCalcPreferredRefPos();
12888
12889 if (g.IO.MouseSource == ImGuiMouseSource_TouchScreen && NavCalcPreferredRefPosSource() == ImGuiInputSource_Mouse)
12890 {
12891 ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_TOUCH * scale - (TOOLTIP_DEFAULT_PIVOT_TOUCH * window->Size);
12892 if (r_outer.Contains(ImRect(tooltip_pos, tooltip_pos + window->Size)))
12893 return tooltip_pos;
12894 }
12895
12896 ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_MOUSE * scale;
12897 ImRect r_avoid;
12898 if (g.NavCursorVisible && g.NavHighlightItemUnderNav && !g.IO.ConfigNavMoveSetMousePos)
12899 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
12900 else
12901 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.
12902 //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255));
12903
12904 return FindBestWindowPosForPopupEx(tooltip_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
12905 }
12906 IM_ASSERT(0);
12907 return window->Pos;
12908}
12909
12910//-----------------------------------------------------------------------------
12911// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
12912//-----------------------------------------------------------------------------
12913
12914// FIXME-NAV: The existence of SetNavID vs SetFocusID vs FocusWindow() needs to be clarified/reworked.
12915// In our terminology those should be interchangeable, yet right now this is super confusing.
12916// Those two functions are merely a legacy artifact, so at minimum naming should be clarified.
12917
12918void ImGui::SetNavCursorVisible(bool visible)
12919{
12920 ImGuiContext& g = *GImGui;
12921 if (g.IO.ConfigNavCursorVisibleAlways)
12922 visible = true;
12923 g.NavCursorVisible = visible;
12924}
12925
12926// (was called NavRestoreHighlightAfterMove() before 1.91.4)
12927void ImGui::SetNavCursorVisibleAfterMove()
12928{
12929 ImGuiContext& g = *GImGui;
12930 if (g.IO.ConfigNavCursorVisibleAuto)
12931 g.NavCursorVisible = true;
12932 g.NavHighlightItemUnderNav = g.NavMousePosDirty = true;
12933}
12934
12935void ImGui::SetNavWindow(ImGuiWindow* window)
12936{
12937 ImGuiContext& g = *GImGui;
12938 if (g.NavWindow != window)
12939 {
12940 IMGUI_DEBUG_LOG_FOCUS("[focus] SetNavWindow(\"%s\")\n", window ? window->Name : "<NULL>");
12941 g.NavWindow = window;
12942 g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
12943 }
12944 g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;
12945 NavUpdateAnyRequestFlag();
12946}
12947
12948void ImGui::NavHighlightActivated(ImGuiID id)
12949{
12950 ImGuiContext& g = *GImGui;
12951 g.NavHighlightActivatedId = id;
12952 g.NavHighlightActivatedTimer = NAV_ACTIVATE_HIGHLIGHT_TIMER;
12953}
12954
12955void ImGui::NavClearPreferredPosForAxis(ImGuiAxis axis)
12956{
12957 ImGuiContext& g = *GImGui;
12958 g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer][axis] = FLT_MAX;
12959}
12960
12961void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
12962{
12963 ImGuiContext& g = *GImGui;
12964 IM_ASSERT(g.NavWindow != NULL);
12965 IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu);
12966 g.NavId = id;
12967 g.NavLayer = nav_layer;
12968 SetNavFocusScope(focus_scope_id);
12969 g.NavWindow->NavLastIds[nav_layer] = id;
12970 g.NavWindow->NavRectRel[nav_layer] = rect_rel;
12971
12972 // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
12973 NavClearPreferredPosForAxis(ImGuiAxis_X);
12974 NavClearPreferredPosForAxis(ImGuiAxis_Y);
12975}
12976
12977void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
12978{
12979 ImGuiContext& g = *GImGui;
12980 IM_ASSERT(id != 0);
12981
12982 if (g.NavWindow != window)
12983 SetNavWindow(window);
12984
12985 // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and g.CurrentFocusScopeId are valid.
12986 // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
12987 const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
12988 g.NavId = id;
12989 g.NavLayer = nav_layer;
12990 SetNavFocusScope(g.CurrentFocusScopeId);
12991 window->NavLastIds[nav_layer] = id;
12992 if (g.LastItemData.ID == id)
12993 window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect);
12994
12995 if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
12996 g.NavHighlightItemUnderNav = true;
12997 else if (g.IO.ConfigNavCursorVisibleAuto)
12998 g.NavCursorVisible = false;
12999
13000 // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
13001 NavClearPreferredPosForAxis(ImGuiAxis_X);
13002 NavClearPreferredPosForAxis(ImGuiAxis_Y);
13003}
13004
13005static ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
13006{
13007 if (ImFabs(dx) > ImFabs(dy))
13008 return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
13009 return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
13010}
13011
13012static float inline NavScoreItemDistInterval(float cand_min, float cand_max, float curr_min, float curr_max)
13013{
13014 if (cand_max < curr_min)
13015 return cand_max - curr_min;
13016 if (curr_max < cand_min)
13017 return cand_min - curr_max;
13018 return 0.0f;
13019}
13020
13021// Scoring function for keyboard/gamepad directional navigation. Based on https://gist.github.com/rygorous/6981057
13022static bool ImGui::NavScoreItem(ImGuiNavItemData* result)
13023{
13024 ImGuiContext& g = *GImGui;
13025 ImGuiWindow* window = g.CurrentWindow;
13026 if (g.NavLayer != window->DC.NavLayerCurrent)
13027 return false;
13028
13029 // FIXME: Those are not good variables names
13030 ImRect cand = g.LastItemData.NavRect; // Current item nav rectangle
13031 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)
13032 g.NavScoringDebugCount++;
13033
13034 // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
13035 if (window->ParentWindow == g.NavWindow)
13036 {
13037 IM_ASSERT((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened);
13038 if (!window->ClipRect.Overlaps(cand))
13039 return false;
13040 cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
13041 }
13042
13043 // Compute distance between boxes
13044 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
13045 float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
13046 float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items
13047 if (dby != 0.0f && dbx != 0.0f)
13048 dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
13049 float dist_box = ImFabs(dbx) + ImFabs(dby);
13050
13051 // 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)
13052 float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
13053 float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
13054 float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
13055
13056 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
13057 ImGuiDir quadrant;
13058 float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
13059 if (dbx != 0.0f || dby != 0.0f)
13060 {
13061 // For non-overlapping boxes, use distance between boxes
13062 // 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
13063 // One typical case where this happens, with style.WindowMenuButtonPosition == ImGuiDir_Right, pressing Left to navigate from Close to Collapse tends to fail.
13064 // Also see #6344. Calling ImGetDirQuadrantFromDelta() with unbiased values may be good but side-effects are plenty.
13065 dax = dbx;
13066 day = dby;
13067 dist_axial = dist_box;
13068 quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
13069 }
13070 else if (dcx != 0.0f || dcy != 0.0f)
13071 {
13072 // For overlapping boxes with different centers, use distance between centers
13073 dax = dcx;
13074 day = dcy;
13075 dist_axial = dist_center;
13076 quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
13077 }
13078 else
13079 {
13080 // 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)
13081 quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
13082 }
13083
13084 const ImGuiDir move_dir = g.NavMoveDir;
13085#if IMGUI_DEBUG_NAV_SCORING
13086 char buf[200];
13087 if (g.IO.KeyCtrl) // Hold CTRL to preview score in matching quadrant. CTRL+Arrow to rotate.
13088 {
13089 if (quadrant == move_dir)
13090 {
13091 ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
13092 ImDrawList* draw_list = GetForegroundDrawList(window);
13093 draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 80));
13094 draw_list->AddRectFilled(cand.Min, cand.Min + CalcTextSize(buf), IM_COL32(255, 0, 0, 200));
13095 draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf);
13096 }
13097 }
13098 const bool debug_hovering = IsMouseHoveringRect(cand.Min, cand.Max);
13099 const bool debug_tty = (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_Space));
13100 if (debug_hovering || debug_tty)
13101 {
13102 ImFormatString(buf, IM_ARRAYSIZE(buf),
13103 "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",
13104 dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "-WENS"[move_dir+1], "-WENS"[quadrant+1]);
13105 if (debug_hovering)
13106 {
13107 ImDrawList* draw_list = GetForegroundDrawList(window);
13108 draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100));
13109 draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255, 255, 0, 200));
13110 draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40, 0, 0, 200));
13111 draw_list->AddText(cand.Max, ~0U, buf);
13112 }
13113 if (debug_tty) { IMGUI_DEBUG_LOG_NAV("id 0x%08X\n%s\n", g.LastItemData.ID, buf); }
13114 }
13115#endif
13116
13117 // Is it in the quadrant we're interested in moving to?
13118 bool new_best = false;
13119 if (quadrant == move_dir)
13120 {
13121 // Does it beat the current best candidate?
13122 if (dist_box < result->DistBox)
13123 {
13124 result->DistBox = dist_box;
13125 result->DistCenter = dist_center;
13126 return true;
13127 }
13128 if (dist_box == result->DistBox)
13129 {
13130 // Try using distance between center points to break ties
13131 if (dist_center < result->DistCenter)
13132 {
13133 result->DistCenter = dist_center;
13134 new_best = true;
13135 }
13136 else if (dist_center == result->DistCenter)
13137 {
13138 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
13139 // (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),
13140 // 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.
13141 if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
13142 new_best = true;
13143 }
13144 }
13145 }
13146
13147 // 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
13148 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
13149 // 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.
13150 // 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.
13151 // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
13152 if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
13153 if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
13154 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))
13155 {
13156 result->DistAxial = dist_axial;
13157 new_best = true;
13158 }
13159
13160 return new_best;
13161}
13162
13163static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)
13164{
13165 ImGuiContext& g = *GImGui;
13166 ImGuiWindow* window = g.CurrentWindow;
13167 result->Window = window;
13168 result->ID = g.LastItemData.ID;
13169 result->FocusScopeId = g.CurrentFocusScopeId;
13170 result->ItemFlags = g.LastItemData.ItemFlags;
13171 result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect);
13172 if (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData)
13173 {
13174 IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
13175 result->SelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
13176 }
13177}
13178
13179// True when current work location may be scrolled horizontally when moving left / right.
13180// This is generally always true UNLESS within a column. We don't have a vertical equivalent.
13181void ImGui::NavUpdateCurrentWindowIsScrollPushableX()
13182{
13183 ImGuiContext& g = *GImGui;
13184 ImGuiWindow* window = g.CurrentWindow;
13185 window->DC.NavIsScrollPushableX = (g.CurrentTable == NULL && window->DC.CurrentColumns == NULL);
13186}
13187
13188// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
13189// This is called after LastItemData is set, but NextItemData is also still valid.
13190static void ImGui::NavProcessItem()
13191{
13192 ImGuiContext& g = *GImGui;
13193 ImGuiWindow* window = g.CurrentWindow;
13194 const ImGuiID id = g.LastItemData.ID;
13195 const ImGuiItemFlags item_flags = g.LastItemData.ItemFlags;
13196
13197 // When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221)
13198 if (window->DC.NavIsScrollPushableX == false)
13199 {
13200 g.LastItemData.NavRect.Min.x = ImClamp(g.LastItemData.NavRect.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
13201 g.LastItemData.NavRect.Max.x = ImClamp(g.LastItemData.NavRect.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
13202 }
13203 const ImRect nav_bb = g.LastItemData.NavRect;
13204
13205 // Process Init Request
13206 if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent && (item_flags & ImGuiItemFlags_Disabled) == 0)
13207 {
13208 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
13209 const bool candidate_for_nav_default_focus = (item_flags & ImGuiItemFlags_NoNavDefaultFocus) == 0;
13210 if (candidate_for_nav_default_focus || g.NavInitResult.ID == 0)
13211 {
13212 NavApplyItemToResult(&g.NavInitResult);
13213 }
13214 if (candidate_for_nav_default_focus)
13215 {
13216 g.NavInitRequest = false; // Found a match, clear request
13217 NavUpdateAnyRequestFlag();
13218 }
13219 }
13220
13221 // Process Move Request (scoring for navigation)
13222 // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)
13223 if (g.NavMoveScoringItems && (item_flags & ImGuiItemFlags_Disabled) == 0)
13224 {
13225 if ((g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi) || (window->Flags & ImGuiWindowFlags_NoNavInputs) == 0)
13226 {
13227 const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;
13228 if (is_tabbing)
13229 {
13230 NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags);
13231 }
13232 else if (g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId))
13233 {
13234 ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
13235 if (NavScoreItem(result))
13236 NavApplyItemToResult(result);
13237
13238 // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
13239 const float VISIBLE_RATIO = 0.70f;
13240 if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
13241 if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)
13242 if (NavScoreItem(&g.NavMoveResultLocalVisible))
13243 NavApplyItemToResult(&g.NavMoveResultLocalVisible);
13244 }
13245 }
13246 }
13247
13248 // Update information for currently focused/navigated item
13249 if (g.NavId == id)
13250 {
13251 if (g.NavWindow != window)
13252 SetNavWindow(window); // Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window.
13253 g.NavLayer = window->DC.NavLayerCurrent;
13254 SetNavFocusScope(g.CurrentFocusScopeId); // Will set g.NavFocusScopeId AND store g.NavFocusScopePath
13255 g.NavFocusScopeId = g.CurrentFocusScopeId;
13256 g.NavIdIsAlive = true;
13257 if (g.LastItemData.ItemFlags & ImGuiItemFlags_HasSelectionUserData)
13258 {
13259 IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
13260 g.NavLastValidSelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
13261 }
13262 window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position)
13263 }
13264}
13265
13266// Handle "scoring" of an item for a tabbing/focusing request initiated by NavUpdateCreateTabbingRequest().
13267// Note that SetKeyboardFocusHere() API calls are considered tabbing requests!
13268// - Case 1: no nav/active id: set result to first eligible item, stop storing.
13269// - Case 2: tab forward: on ref id set counter, on counter elapse store result
13270// - 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
13271// - Case 4: tab backward: store all results, on ref id pick prev, stop storing
13272// - 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
13273void ImGui::NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags)
13274{
13275 ImGuiContext& g = *GImGui;
13276
13277 if ((move_flags & ImGuiNavMoveFlags_FocusApi) == 0)
13278 {
13279 if (g.NavLayer != g.CurrentWindow->DC.NavLayerCurrent)
13280 return;
13281 if (g.NavFocusScopeId != g.CurrentFocusScopeId)
13282 return;
13283 }
13284
13285 // - Can always land on an item when using API call.
13286 // - Tabbing with _NavEnableKeyboard (space/enter/arrows): goes through every item.
13287 // - Tabbing without _NavEnableKeyboard: goes through inputable items only.
13288 bool can_stop;
13289 if (move_flags & ImGuiNavMoveFlags_FocusApi)
13290 can_stop = true;
13291 else
13292 can_stop = (item_flags & ImGuiItemFlags_NoTabStop) == 0 && ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) || (item_flags & ImGuiItemFlags_Inputable));
13293
13294 // Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows)
13295 ImGuiNavItemData* result = &g.NavMoveResultLocal;
13296 if (g.NavTabbingDir == +1)
13297 {
13298 // Tab Forward or SetKeyboardFocusHere() with >= 0
13299 if (can_stop && g.NavTabbingResultFirst.ID == 0)
13300 NavApplyItemToResult(&g.NavTabbingResultFirst);
13301 if (can_stop && g.NavTabbingCounter > 0 && --g.NavTabbingCounter == 0)
13302 NavMoveRequestResolveWithLastItem(result);
13303 else if (g.NavId == id)
13304 g.NavTabbingCounter = 1;
13305 }
13306 else if (g.NavTabbingDir == -1)
13307 {
13308 // Tab Backward
13309 if (g.NavId == id)
13310 {
13311 if (result->ID)
13312 {
13313 g.NavMoveScoringItems = false;
13314 NavUpdateAnyRequestFlag();
13315 }
13316 }
13317 else if (can_stop)
13318 {
13319 // Keep applying until reaching NavId
13320 NavApplyItemToResult(result);
13321 }
13322 }
13323 else if (g.NavTabbingDir == 0)
13324 {
13325 if (can_stop && g.NavId == id)
13326 NavMoveRequestResolveWithLastItem(result);
13327 if (can_stop && g.NavTabbingResultFirst.ID == 0) // Tab init
13328 NavApplyItemToResult(&g.NavTabbingResultFirst);
13329 }
13330}
13331
13332bool ImGui::NavMoveRequestButNoResultYet()
13333{
13334 ImGuiContext& g = *GImGui;
13335 return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
13336}
13337
13338// FIXME: ScoringRect is not set
13339void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
13340{
13341 ImGuiContext& g = *GImGui;
13342 IM_ASSERT(g.NavWindow != NULL);
13343 //IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestSubmit: dir %c, window \"%s\"\n", "-WENS"[move_dir + 1], g.NavWindow->Name);
13344
13345 if (move_flags & ImGuiNavMoveFlags_IsTabbing)
13346 move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId;
13347
13348 g.NavMoveSubmitted = g.NavMoveScoringItems = true;
13349 g.NavMoveDir = move_dir;
13350 g.NavMoveDirForDebug = move_dir;
13351 g.NavMoveClipDir = clip_dir;
13352 g.NavMoveFlags = move_flags;
13353 g.NavMoveScrollFlags = scroll_flags;
13354 g.NavMoveForwardToNextFrame = false;
13355 g.NavMoveKeyMods = (move_flags & ImGuiNavMoveFlags_FocusApi) ? 0 : g.IO.KeyMods;
13356 g.NavMoveResultLocal.Clear();
13357 g.NavMoveResultLocalVisible.Clear();
13358 g.NavMoveResultOther.Clear();
13359 g.NavTabbingCounter = 0;
13360 g.NavTabbingResultFirst.Clear();
13361 NavUpdateAnyRequestFlag();
13362}
13363
13364void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result)
13365{
13366 ImGuiContext& g = *GImGui;
13367 g.NavMoveScoringItems = false; // Ensure request doesn't need more processing
13368 NavApplyItemToResult(result);
13369 NavUpdateAnyRequestFlag();
13370}
13371
13372// Called by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere
13373void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiTreeNodeStackData* tree_node_data)
13374{
13375 ImGuiContext& g = *GImGui;
13376 g.NavMoveScoringItems = false;
13377 g.LastItemData.ID = tree_node_data->ID;
13378 g.LastItemData.ItemFlags = tree_node_data->ItemFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper).
13379 g.LastItemData.NavRect = tree_node_data->NavRect;
13380 NavApplyItemToResult(result); // Result this instead of implementing a NavApplyPastTreeNodeToResult()
13381 NavClearPreferredPosForAxis(ImGuiAxis_Y);
13382 NavUpdateAnyRequestFlag();
13383}
13384
13385void ImGui::NavMoveRequestCancel()
13386{
13387 ImGuiContext& g = *GImGui;
13388 g.NavMoveSubmitted = g.NavMoveScoringItems = false;
13389 NavUpdateAnyRequestFlag();
13390}
13391
13392// Forward will reuse the move request again on the next frame (generally with modifications done to it)
13393void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
13394{
13395 ImGuiContext& g = *GImGui;
13396 IM_ASSERT(g.NavMoveForwardToNextFrame == false);
13397 NavMoveRequestCancel();
13398 g.NavMoveForwardToNextFrame = true;
13399 g.NavMoveDir = move_dir;
13400 g.NavMoveClipDir = clip_dir;
13401 g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded;
13402 g.NavMoveScrollFlags = scroll_flags;
13403}
13404
13405// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
13406// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
13407void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags)
13408{
13409 ImGuiContext& g = *GImGui;
13410 IM_ASSERT((wrap_flags & ImGuiNavMoveFlags_WrapMask_ ) != 0 && (wrap_flags & ~ImGuiNavMoveFlags_WrapMask_) == 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY
13411
13412 // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it:
13413 // as NavEndFrame() will do the same test. It will end up calling NavUpdateCreateWrappingRequest().
13414 if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main)
13415 g.NavMoveFlags = (g.NavMoveFlags & ~ImGuiNavMoveFlags_WrapMask_) | wrap_flags;
13416}
13417
13418// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
13419// This way we could find the last focused window among our children. It would be much less confusing this way?
13420static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
13421{
13422 ImGuiWindow* parent = nav_window;
13423 while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
13424 parent = parent->ParentWindow;
13425 if (parent && parent != nav_window)
13426 parent->NavLastChildNavWindow = nav_window;
13427}
13428
13429// Restore the last focused child.
13430// Call when we are expected to land on the Main Layer (0) after FocusWindow()
13431static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
13432{
13433 if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)
13434 return window->NavLastChildNavWindow;
13435 if (window->DockNodeAsHost && window->DockNodeAsHost->TabBar)
13436 if (ImGuiTabItem* tab = TabBarFindMostRecentlySelectedTabForActiveWindow(window->DockNodeAsHost->TabBar))
13437 return tab->Window;
13438 return window;
13439}
13440
13441void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
13442{
13443 ImGuiContext& g = *GImGui;
13444 if (layer == ImGuiNavLayer_Main)
13445 {
13446 ImGuiWindow* prev_nav_window = g.NavWindow;
13447 g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); // FIXME-NAV: Should clear ongoing nav requests?
13448 g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
13449 if (prev_nav_window)
13450 IMGUI_DEBUG_LOG_FOCUS("[focus] NavRestoreLayer: from \"%s\" to SetNavWindow(\"%s\")\n", prev_nav_window->Name, g.NavWindow->Name);
13451 }
13452 ImGuiWindow* window = g.NavWindow;
13453 if (window->NavLastIds[layer] != 0)
13454 {
13455 SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
13456 }
13457 else
13458 {
13459 g.NavLayer = layer;
13460 NavInitWindow(window, true);
13461 }
13462}
13463
13464static inline void ImGui::NavUpdateAnyRequestFlag()
13465{
13466 ImGuiContext& g = *GImGui;
13467 g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
13468 if (g.NavAnyRequest)
13469 IM_ASSERT(g.NavWindow != NULL);
13470}
13471
13472// This needs to be called before we submit any widget (aka in or before Begin)
13473void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
13474{
13475 // FIXME: ChildWindow test here is wrong for docking
13476 ImGuiContext& g = *GImGui;
13477 IM_ASSERT(window == g.NavWindow);
13478
13479 if (window->Flags & ImGuiWindowFlags_NoNavInputs)
13480 {
13481 g.NavId = 0;
13482 SetNavFocusScope(window->NavRootFocusScopeId);
13483 return;
13484 }
13485
13486 bool init_for_nav = false;
13487 if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
13488 init_for_nav = true;
13489 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
13490 if (init_for_nav)
13491 {
13492 SetNavID(0, g.NavLayer, window->NavRootFocusScopeId, ImRect());
13493 g.NavInitRequest = true;
13494 g.NavInitRequestFromMove = false;
13495 g.NavInitResult.ID = 0;
13496 NavUpdateAnyRequestFlag();
13497 }
13498 else
13499 {
13500 g.NavId = window->NavLastIds[0];
13501 SetNavFocusScope(window->NavRootFocusScopeId);
13502 }
13503}
13504
13505static ImGuiInputSource ImGui::NavCalcPreferredRefPosSource()
13506{
13507 ImGuiContext& g = *GImGui;
13508 ImGuiWindow* window = g.NavWindow;
13509 const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
13510
13511 // 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.
13512 if ((!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !window) && !activated_shortcut)
13513 return ImGuiInputSource_Mouse;
13514 else
13515 return ImGuiInputSource_Keyboard; // or Nav in general
13516}
13517
13518static ImVec2 ImGui::NavCalcPreferredRefPos()
13519{
13520 ImGuiContext& g = *GImGui;
13521 ImGuiWindow* window = g.NavWindow;
13522 ImGuiInputSource source = NavCalcPreferredRefPosSource();
13523
13524 const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
13525
13526 // 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.
13527 if (source == ImGuiInputSource_Mouse)
13528 {
13529 // Mouse (we need a fallback in case the mouse becomes invalid after being used)
13530 // 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.
13531 // In theory we could move that +1.0f offset in OpenPopupEx()
13532 ImVec2 p = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : g.MouseLastValidPos;
13533 return ImVec2(p.x + 1.0f, p.y);
13534 }
13535 else
13536 {
13537 // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item
13538 ImRect ref_rect;
13539 if (activated_shortcut)
13540 ref_rect = g.LastItemData.NavRect;
13541 else
13542 ref_rect = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]);
13543
13544 // Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?)
13545 if (window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX))
13546 {
13547 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
13548 ref_rect.Translate(window->Scroll - next_scroll);
13549 }
13550 ImVec2 pos = ImVec2(ref_rect.Min.x + ImMin(g.Style.FramePadding.x * 4, ref_rect.GetWidth()), ref_rect.Max.y - ImMin(g.Style.FramePadding.y, ref_rect.GetHeight()));
13551 ImGuiViewport* viewport = window->Viewport;
13552 return ImTrunc(ImClamp(pos, viewport->Pos, 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.
13553 }
13554}
13555
13556float ImGui::GetNavTweakPressedAmount(ImGuiAxis axis)
13557{
13558 ImGuiContext& g = *GImGui;
13559 float repeat_delay, repeat_rate;
13560 GetTypematicRepeatRate(ImGuiInputFlags_RepeatRateNavTweak, &repeat_delay, &repeat_rate);
13561
13562 ImGuiKey key_less, key_more;
13563 if (g.NavInputSource == ImGuiInputSource_Gamepad)
13564 {
13565 key_less = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadLeft : ImGuiKey_GamepadDpadUp;
13566 key_more = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadRight : ImGuiKey_GamepadDpadDown;
13567 }
13568 else
13569 {
13570 key_less = (axis == ImGuiAxis_X) ? ImGuiKey_LeftArrow : ImGuiKey_UpArrow;
13571 key_more = (axis == ImGuiAxis_X) ? ImGuiKey_RightArrow : ImGuiKey_DownArrow;
13572 }
13573 float amount = (float)GetKeyPressedAmount(key_more, repeat_delay, repeat_rate) - (float)GetKeyPressedAmount(key_less, repeat_delay, repeat_rate);
13574 if (amount != 0.0f && IsKeyDown(key_less) && IsKeyDown(key_more)) // Cancel when opposite directions are held, regardless of repeat phase
13575 amount = 0.0f;
13576 return amount;
13577}
13578
13579static void ImGui::NavUpdate()
13580{
13581 ImGuiContext& g = *GImGui;
13582 ImGuiIO& io = g.IO;
13583
13584 io.WantSetMousePos = false;
13585 //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);
13586
13587 // Set input source based on which keys are last pressed (as some features differs when used with Gamepad vs Keyboard)
13588 // FIXME-NAV: Now that keys are separated maybe we can get rid of NavInputSource?
13589 const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
13590 const ImGuiKey nav_gamepad_keys_to_change_source[] = { ImGuiKey_GamepadFaceRight, ImGuiKey_GamepadFaceLeft, ImGuiKey_GamepadFaceUp, ImGuiKey_GamepadFaceDown, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown };
13591 if (nav_gamepad_active)
13592 for (ImGuiKey key : nav_gamepad_keys_to_change_source)
13593 if (IsKeyDown(key))
13594 g.NavInputSource = ImGuiInputSource_Gamepad;
13595 const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13596 const ImGuiKey nav_keyboard_keys_to_change_source[] = { ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow };
13597 if (nav_keyboard_active)
13598 for (ImGuiKey key : nav_keyboard_keys_to_change_source)
13599 if (IsKeyDown(key))
13600 g.NavInputSource = ImGuiInputSource_Keyboard;
13601
13602 // Process navigation init request (select first/default focus)
13603 g.NavJustMovedToId = 0;
13604 g.NavJustMovedToFocusScopeId = g.NavJustMovedFromFocusScopeId = 0;
13605 if (g.NavInitResult.ID != 0)
13606 NavInitRequestApplyResult();
13607 g.NavInitRequest = false;
13608 g.NavInitRequestFromMove = false;
13609 g.NavInitResult.ID = 0;
13610
13611 // Process navigation move request
13612 if (g.NavMoveSubmitted)
13613 NavMoveRequestApplyResult();
13614 g.NavTabbingCounter = 0;
13615 g.NavMoveSubmitted = g.NavMoveScoringItems = false;
13616 if (g.NavCursorHideFrames > 0)
13617 if (--g.NavCursorHideFrames == 0)
13618 g.NavCursorVisible = true;
13619
13620 // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling)
13621 bool set_mouse_pos = false;
13622 if (g.NavMousePosDirty && g.NavIdIsAlive)
13623 if (g.NavCursorVisible && g.NavHighlightItemUnderNav && g.NavWindow)
13624 set_mouse_pos = true;
13625 g.NavMousePosDirty = false;
13626 IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu);
13627
13628 // 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
13629 if (g.NavWindow)
13630 NavSaveLastChildNavWindowIntoParent(g.NavWindow);
13631 if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
13632 g.NavWindow->NavLastChildNavWindow = NULL;
13633
13634 // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
13635 NavUpdateWindowing();
13636
13637 // Set output flags for user application
13638 io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
13639 io.NavVisible = (io.NavActive && g.NavId != 0 && g.NavCursorVisible) || (g.NavWindowingTarget != NULL);
13640
13641 // Process NavCancel input (to close a popup, get back to parent, clear focus)
13642 NavUpdateCancelRequest();
13643
13644 // Process manual activation request
13645 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = 0;
13646 g.NavActivateFlags = ImGuiActivateFlags_None;
13647 if (g.NavId != 0 && g.NavCursorVisible && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
13648 {
13649 const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_NoOwner));
13650 const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, 0, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, 0, ImGuiKeyOwner_NoOwner)));
13651 const bool input_down = (nav_keyboard_active && (IsKeyDown(ImGuiKey_Enter, ImGuiKeyOwner_NoOwner) || IsKeyDown(ImGuiKey_KeypadEnter, ImGuiKeyOwner_NoOwner))) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadInput, ImGuiKeyOwner_NoOwner));
13652 const bool input_pressed = input_down && ((nav_keyboard_active && (IsKeyPressed(ImGuiKey_Enter, 0, ImGuiKeyOwner_NoOwner) || IsKeyPressed(ImGuiKey_KeypadEnter, 0, ImGuiKeyOwner_NoOwner))) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadInput, 0, ImGuiKeyOwner_NoOwner)));
13653 if (g.ActiveId == 0 && activate_pressed)
13654 {
13655 g.NavActivateId = g.NavId;
13656 g.NavActivateFlags = ImGuiActivateFlags_PreferTweak;
13657 }
13658 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed)
13659 {
13660 g.NavActivateId = g.NavId;
13661 g.NavActivateFlags = ImGuiActivateFlags_PreferInput;
13662 }
13663 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_down || input_down))
13664 g.NavActivateDownId = g.NavId;
13665 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_pressed || input_pressed))
13666 {
13667 g.NavActivatePressedId = g.NavId;
13668 NavHighlightActivated(g.NavId);
13669 }
13670 }
13671 if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
13672 g.NavCursorVisible = false;
13673 else if (g.IO.ConfigNavCursorVisibleAlways && g.NavCursorHideFrames == 0)
13674 g.NavCursorVisible = true;
13675 if (g.NavActivateId != 0)
13676 IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
13677
13678 // Highlight
13679 if (g.NavHighlightActivatedTimer > 0.0f)
13680 g.NavHighlightActivatedTimer = ImMax(0.0f, g.NavHighlightActivatedTimer - io.DeltaTime);
13681 if (g.NavHighlightActivatedTimer == 0.0f)
13682 g.NavHighlightActivatedId = 0;
13683
13684 // Process programmatic activation request
13685 // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others)
13686 if (g.NavNextActivateId != 0)
13687 {
13688 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId;
13689 g.NavActivateFlags = g.NavNextActivateFlags;
13690 }
13691 g.NavNextActivateId = 0;
13692
13693 // Process move requests
13694 NavUpdateCreateMoveRequest();
13695 if (g.NavMoveDir == ImGuiDir_None)
13696 NavUpdateCreateTabbingRequest();
13697 NavUpdateAnyRequestFlag();
13698 g.NavIdIsAlive = false;
13699
13700 // Scrolling
13701 if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
13702 {
13703 // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
13704 ImGuiWindow* window = g.NavWindow;
13705 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.
13706 const ImGuiDir move_dir = g.NavMoveDir;
13707 if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY && move_dir != ImGuiDir_None)
13708 {
13709 if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
13710 SetScrollX(window, ImTrunc(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
13711 if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down)
13712 SetScrollY(window, ImTrunc(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
13713 }
13714
13715 // *Normal* Manual scroll with LStick
13716 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
13717 if (nav_gamepad_active)
13718 {
13719 const ImVec2 scroll_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
13720 const float tweak_factor = IsKeyDown(ImGuiKey_NavGamepadTweakSlow) ? 1.0f / 10.0f : IsKeyDown(ImGuiKey_NavGamepadTweakFast) ? 10.0f : 1.0f;
13721 if (scroll_dir.x != 0.0f && window->ScrollbarX)
13722 SetScrollX(window, ImTrunc(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor));
13723 if (scroll_dir.y != 0.0f)
13724 SetScrollY(window, ImTrunc(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor));
13725 }
13726 }
13727
13728 // Always prioritize mouse highlight if navigation is disabled
13729 if (!nav_keyboard_active && !nav_gamepad_active)
13730 {
13731 g.NavCursorVisible = false;
13732 g.NavHighlightItemUnderNav = set_mouse_pos = false;
13733 }
13734
13735 // Update mouse position if requested
13736 // (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)
13737 if (set_mouse_pos && io.ConfigNavMoveSetMousePos && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
13738 TeleportMousePos(NavCalcPreferredRefPos());
13739
13740 // [DEBUG]
13741 g.NavScoringDebugCount = 0;
13742#if IMGUI_DEBUG_NAV_RECTS
13743 if (ImGuiWindow* debug_window = g.NavWindow)
13744 {
13745 ImDrawList* draw_list = GetForegroundDrawList(debug_window);
13746 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)); }
13747 //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); }
13748 }
13749#endif
13750}
13751
13752void ImGui::NavInitRequestApplyResult()
13753{
13754 // 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)
13755 ImGuiContext& g = *GImGui;
13756 if (!g.NavWindow)
13757 return;
13758
13759 ImGuiNavItemData* result = &g.NavInitResult;
13760 if (g.NavId != result->ID)
13761 {
13762 g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId;
13763 g.NavJustMovedToId = result->ID;
13764 g.NavJustMovedToFocusScopeId = result->FocusScopeId;
13765 g.NavJustMovedToKeyMods = 0;
13766 g.NavJustMovedToIsTabbing = false;
13767 g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
13768 }
13769
13770 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
13771 // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently.
13772 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: ApplyResult: NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
13773 SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
13774 g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result
13775 if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
13776 g.NavLastValidSelectionUserData = result->SelectionUserData;
13777 if (g.NavInitRequestFromMove)
13778 SetNavCursorVisibleAfterMove();
13779}
13780
13781// Bias scoring rect ahead of scoring + update preferred pos (if missing) using source position
13782static void NavBiasScoringRect(ImRect& r, ImVec2& preferred_pos_rel, ImGuiDir move_dir, ImGuiNavMoveFlags move_flags)
13783{
13784 // Bias initial rect
13785 ImGuiContext& g = *GImGui;
13786 const ImVec2 rel_to_abs_offset = g.NavWindow->DC.CursorStartPos;
13787
13788 // Initialize bias on departure if we don't have any. So mouse-click + arrow will record bias.
13789 // - We default to L/U bias, so moving down from a large source item into several columns will land on left-most column.
13790 // - But each successful move sets new bias on one axis, only cleared when using mouse.
13791 if ((move_flags & ImGuiNavMoveFlags_Forwarded) == 0)
13792 {
13793 if (preferred_pos_rel.x == FLT_MAX)
13794 preferred_pos_rel.x = ImMin(r.Min.x + 1.0f, r.Max.x) - rel_to_abs_offset.x;
13795 if (preferred_pos_rel.y == FLT_MAX)
13796 preferred_pos_rel.y = r.GetCenter().y - rel_to_abs_offset.y;
13797 }
13798
13799 // Apply general bias on the other axis
13800 if ((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) && preferred_pos_rel.x != FLT_MAX)
13801 r.Min.x = r.Max.x = preferred_pos_rel.x + rel_to_abs_offset.x;
13802 else if ((move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) && preferred_pos_rel.y != FLT_MAX)
13803 r.Min.y = r.Max.y = preferred_pos_rel.y + rel_to_abs_offset.y;
13804}
13805
13806void ImGui::NavUpdateCreateMoveRequest()
13807{
13808 ImGuiContext& g = *GImGui;
13809 ImGuiIO& io = g.IO;
13810 ImGuiWindow* window = g.NavWindow;
13811 const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
13812 const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13813
13814 if (g.NavMoveForwardToNextFrame && window != NULL)
13815 {
13816 // 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)
13817 // (preserve most state, which were already set by the NavMoveRequestForward() function)
13818 IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
13819 IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded);
13820 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
13821 }
13822 else
13823 {
13824 // Initiate directional inputs request
13825 g.NavMoveDir = ImGuiDir_None;
13826 g.NavMoveFlags = ImGuiNavMoveFlags_None;
13827 g.NavMoveScrollFlags = ImGuiScrollFlags_None;
13828 if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
13829 {
13830 const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateNavMove;
13831 if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadLeft, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_LeftArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Left; }
13832 if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadRight, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_RightArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Right; }
13833 if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadUp, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_UpArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Up; }
13834 if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadDown, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_DownArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Down; }
13835 }
13836 g.NavMoveClipDir = g.NavMoveDir;
13837 g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
13838 }
13839
13840 // Update PageUp/PageDown/Home/End scroll
13841 // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
13842 float scoring_rect_offset_y = 0.0f;
13843 if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active)
13844 scoring_rect_offset_y = NavUpdatePageUpPageDown();
13845 if (scoring_rect_offset_y != 0.0f)
13846 {
13847 g.NavScoringNoClipRect = window->InnerRect;
13848 g.NavScoringNoClipRect.TranslateY(scoring_rect_offset_y);
13849 }
13850
13851 // [DEBUG] Always send a request when holding CTRL. Hold CTRL + Arrow change the direction.
13852#if IMGUI_DEBUG_NAV_SCORING
13853 //if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C))
13854 // g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3);
13855 if (io.KeyCtrl)
13856 {
13857 if (g.NavMoveDir == ImGuiDir_None)
13858 g.NavMoveDir = g.NavMoveDirForDebug;
13859 g.NavMoveClipDir = g.NavMoveDir;
13860 g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult;
13861 }
13862#endif
13863
13864 // Submit
13865 g.NavMoveForwardToNextFrame = false;
13866 if (g.NavMoveDir != ImGuiDir_None)
13867 NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags);
13868
13869 // Moving with no reference triggers an init request (will be used as a fallback if the direction fails to find a match)
13870 if (g.NavMoveSubmitted && g.NavId == 0)
13871 {
13872 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", window ? window->Name : "<NULL>", g.NavLayer);
13873 g.NavInitRequest = g.NavInitRequestFromMove = true;
13874 g.NavInitResult.ID = 0;
13875 if (g.IO.ConfigNavCursorVisibleAuto)
13876 g.NavCursorVisible = true;
13877 }
13878
13879 // When using gamepad, we project the reference nav bounding box into window visible area.
13880 // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling,
13881 // since with gamepad all movements are relative (can't focus a visible object like we can with the mouse).
13882 if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded))
13883 {
13884 bool clamp_x = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapX)) == 0;
13885 bool clamp_y = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapY)) == 0;
13886 ImRect inner_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)));
13887
13888 // Take account of changing scroll to handle triggering a new move request on a scrolling frame. (#6171)
13889 // Otherwise 'inner_rect_rel' would be off on the move result frame.
13890 inner_rect_rel.Translate(CalcNextScrollFromScrollTargetAndClamp(window) - window->Scroll);
13891
13892 if ((clamp_x || clamp_y) && !inner_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
13893 {
13894 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel for gamepad move\n");
13895 float pad_x = ImMin(inner_rect_rel.GetWidth(), window->CalcFontSize() * 0.5f);
13896 float pad_y = ImMin(inner_rect_rel.GetHeight(), window->CalcFontSize() * 0.5f); // Terrible approximation for the intent of starting navigation from first fully visible item
13897 inner_rect_rel.Min.x = clamp_x ? (inner_rect_rel.Min.x + pad_x) : -FLT_MAX;
13898 inner_rect_rel.Max.x = clamp_x ? (inner_rect_rel.Max.x - pad_x) : +FLT_MAX;
13899 inner_rect_rel.Min.y = clamp_y ? (inner_rect_rel.Min.y + pad_y) : -FLT_MAX;
13900 inner_rect_rel.Max.y = clamp_y ? (inner_rect_rel.Max.y - pad_y) : +FLT_MAX;
13901 window->NavRectRel[g.NavLayer].ClipWithFull(inner_rect_rel);
13902 g.NavId = 0;
13903 }
13904 }
13905
13906 // 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)
13907 ImRect scoring_rect;
13908 if (window != NULL)
13909 {
13910 ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
13911 scoring_rect = WindowRectRelToAbs(window, nav_rect_rel);
13912 scoring_rect.TranslateY(scoring_rect_offset_y);
13913 if (g.NavMoveSubmitted)
13914 NavBiasScoringRect(scoring_rect, window->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer], g.NavMoveDir, g.NavMoveFlags);
13915 IM_ASSERT(!scoring_rect.IsInverted()); // Ensure we have a non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem().
13916 //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG]
13917 //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG]
13918 }
13919 g.NavScoringRect = scoring_rect;
13920 g.NavScoringNoClipRect.Add(scoring_rect);
13921}
13922
13923void ImGui::NavUpdateCreateTabbingRequest()
13924{
13925 ImGuiContext& g = *GImGui;
13926 ImGuiWindow* window = g.NavWindow;
13927 IM_ASSERT(g.NavMoveDir == ImGuiDir_None);
13928 if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs))
13929 return;
13930
13931 const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner) && !g.IO.KeyCtrl && !g.IO.KeyAlt;
13932 if (!tab_pressed)
13933 return;
13934
13935 // Initiate tabbing request
13936 // (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!)
13937 // See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping.
13938 const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13939 if (nav_keyboard_active)
13940 g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavCursorVisible == false && g.ActiveId == 0) ? 0 : +1;
13941 else
13942 g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1;
13943 ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate;
13944 ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
13945 ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down;
13946 NavMoveRequestSubmit(ImGuiDir_None, clip_dir, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
13947 g.NavTabbingCounter = -1;
13948}
13949
13950// Apply result from previous frame navigation directional move request. Always called from NavUpdate()
13951void ImGui::NavMoveRequestApplyResult()
13952{
13953 ImGuiContext& g = *GImGui;
13954#if IMGUI_DEBUG_NAV_SCORING
13955 if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times
13956 return;
13957#endif
13958
13959 // Select which result to use
13960 ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL;
13961
13962 // Tabbing forward wrap
13963 if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && result == NULL)
13964 if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID)
13965 result = &g.NavTabbingResultFirst;
13966
13967 // 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)
13968 const ImGuiAxis axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
13969 if (result == NULL)
13970 {
13971 if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
13972 g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavCursorVisible;
13973 if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0)
13974 SetNavCursorVisibleAfterMove();
13975 NavClearPreferredPosForAxis(axis); // On a failed move, clear preferred pos for this axis.
13976 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveSubmitted but not led to a result!\n");
13977 return;
13978 }
13979
13980 // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
13981 if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
13982 if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId)
13983 result = &g.NavMoveResultLocalVisible;
13984
13985 // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
13986 if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
13987 if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
13988 result = &g.NavMoveResultOther;
13989 IM_ASSERT(g.NavWindow && result->Window);
13990
13991 // Scroll to keep newly navigated item fully into view.
13992 if (g.NavLayer == ImGuiNavLayer_Main)
13993 {
13994 ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel);
13995 ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags);
13996
13997 if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY)
13998 {
13999 // FIXME: Should remove this? Or make more precise: use ScrollToRectEx() with edge?
14000 float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
14001 SetScrollY(result->Window, scroll_target);
14002 }
14003 }
14004
14005 if (g.NavWindow != result->Window)
14006 {
14007 IMGUI_DEBUG_LOG_FOCUS("[focus] NavMoveRequest: SetNavWindow(\"%s\")\n", result->Window->Name);
14008 g.NavWindow = result->Window;
14009 g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
14010 }
14011
14012 // Clear active id unless requested not to
14013 // FIXME: ImGuiNavMoveFlags_NoClearActiveId is currently unused as we don't have a clear strategy to preserve active id after interaction,
14014 // so this is mostly provided as a gateway for further experiments (see #1418, #2890)
14015 if (g.ActiveId != result->ID && (g.NavMoveFlags & ImGuiNavMoveFlags_NoClearActiveId) == 0)
14016 ClearActiveID();
14017
14018 // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
14019 // PageUp/PageDown however sets always set NavJustMovedTo (vs Home/End which doesn't) mimicking Windows behavior.
14020 if ((g.NavId != result->ID || (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)) && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSelect) == 0)
14021 {
14022 g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId;
14023 g.NavJustMovedToId = result->ID;
14024 g.NavJustMovedToFocusScopeId = result->FocusScopeId;
14025 g.NavJustMovedToKeyMods = g.NavMoveKeyMods;
14026 g.NavJustMovedToIsTabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;
14027 g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
14028 //IMGUI_DEBUG_LOG_NAV("[nav] NavJustMovedFromFocusScopeId = 0x%08X, NavJustMovedToFocusScopeId = 0x%08X\n", g.NavJustMovedFromFocusScopeId, g.NavJustMovedToFocusScopeId);
14029 }
14030
14031 // Apply new NavID/Focus
14032 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
14033 ImVec2 preferred_scoring_pos_rel = g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer];
14034 SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
14035 if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
14036 g.NavLastValidSelectionUserData = result->SelectionUserData;
14037
14038 // Restore last preferred position for current axis
14039 // (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..)
14040 if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) == 0)
14041 {
14042 preferred_scoring_pos_rel[axis] = result->RectRel.GetCenter()[axis];
14043 g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer] = preferred_scoring_pos_rel;
14044 }
14045
14046 // Tabbing: Activates Inputable, otherwise only Focus
14047 if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && (result->ItemFlags & ImGuiItemFlags_Inputable) == 0)
14048 g.NavMoveFlags &= ~ImGuiNavMoveFlags_Activate;
14049
14050 // Activate
14051 if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate)
14052 {
14053 g.NavNextActivateId = result->ID;
14054 g.NavNextActivateFlags = ImGuiActivateFlags_None;
14055 if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
14056 g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState | ImGuiActivateFlags_FromTabbing;
14057 }
14058
14059 // Make nav cursor visible
14060 if ((g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0)
14061 SetNavCursorVisibleAfterMove();
14062}
14063
14064// Process Escape/NavCancel input (to close a popup, get back to parent, clear focus)
14065// FIXME: In order to support e.g. Escape to clear a selection we'll need:
14066// - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it.
14067// - 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
14068static void ImGui::NavUpdateCancelRequest()
14069{
14070 ImGuiContext& g = *GImGui;
14071 const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
14072 const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
14073 if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, 0, ImGuiKeyOwner_NoOwner)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, 0, ImGuiKeyOwner_NoOwner)))
14074 return;
14075
14076 IMGUI_DEBUG_LOG_NAV("[nav] NavUpdateCancelRequest()\n");
14077 if (g.ActiveId != 0)
14078 {
14079 ClearActiveID();
14080 }
14081 else if (g.NavLayer != ImGuiNavLayer_Main)
14082 {
14083 // Leave the "menu" layer
14084 NavRestoreLayer(ImGuiNavLayer_Main);
14085 SetNavCursorVisibleAfterMove();
14086 }
14087 else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow)
14088 {
14089 // Exit child window
14090 ImGuiWindow* child_window = g.NavWindow->RootWindowForNav;
14091 ImGuiWindow* parent_window = child_window->ParentWindow;
14092 IM_ASSERT(child_window->ChildId != 0);
14093 FocusWindow(parent_window);
14094 SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_window->Rect()));
14095 SetNavCursorVisibleAfterMove();
14096 }
14097 else if (g.OpenPopupStack.Size > 0 && g.OpenPopupStack.back().Window != NULL && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
14098 {
14099 // Close open popup/menu
14100 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
14101 }
14102 else
14103 {
14104 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
14105 // FIXME-NAV: This should happen on window appearing.
14106 if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow)
14107 if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup)))// || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
14108 g.NavWindow->NavLastIds[0] = 0;
14109
14110 // Clear nav focus
14111 if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow)
14112 g.NavId = 0;
14113 if (g.IO.ConfigNavEscapeClearFocusWindow)
14114 FocusWindow(NULL);
14115 }
14116}
14117
14118// Handle PageUp/PageDown/Home/End keys
14119// Called from NavUpdateCreateMoveRequest() which will use our output to create a move request
14120// FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference
14121// FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid?
14122static float ImGui::NavUpdatePageUpPageDown()
14123{
14124 ImGuiContext& g = *GImGui;
14125 ImGuiWindow* window = g.NavWindow;
14126 if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL)
14127 return 0.0f;
14128
14129 const bool page_up_held = IsKeyDown(ImGuiKey_PageUp, ImGuiKeyOwner_NoOwner);
14130 const bool page_down_held = IsKeyDown(ImGuiKey_PageDown, ImGuiKeyOwner_NoOwner);
14131 const bool home_pressed = IsKeyPressed(ImGuiKey_Home, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
14132 const bool end_pressed = IsKeyPressed(ImGuiKey_End, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
14133 if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out
14134 return 0.0f;
14135
14136 if (g.NavLayer != ImGuiNavLayer_Main)
14137 NavRestoreLayer(ImGuiNavLayer_Main);
14138
14139 if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY)
14140 {
14141 // Fallback manual-scroll when window has no navigable item
14142 if (IsKeyPressed(ImGuiKey_PageUp, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
14143 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
14144 else if (IsKeyPressed(ImGuiKey_PageDown, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
14145 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
14146 else if (home_pressed)
14147 SetScrollY(window, 0.0f);
14148 else if (end_pressed)
14149 SetScrollY(window, window->ScrollMax.y);
14150 }
14151 else
14152 {
14153 ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
14154 const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
14155 float nav_scoring_rect_offset_y = 0.0f;
14156 if (IsKeyPressed(ImGuiKey_PageUp, true))
14157 {
14158 nav_scoring_rect_offset_y = -page_offset_y;
14159 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)
14160 g.NavMoveClipDir = ImGuiDir_Up;
14161 g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;
14162 }
14163 else if (IsKeyPressed(ImGuiKey_PageDown, true))
14164 {
14165 nav_scoring_rect_offset_y = +page_offset_y;
14166 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)
14167 g.NavMoveClipDir = ImGuiDir_Down;
14168 g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;
14169 }
14170 else if (home_pressed)
14171 {
14172 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
14173 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result.
14174 // Preserve current horizontal position if we have any.
14175 nav_rect_rel.Min.y = nav_rect_rel.Max.y = 0.0f;
14176 if (nav_rect_rel.IsInverted())
14177 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
14178 g.NavMoveDir = ImGuiDir_Down;
14179 g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
14180 // FIXME-NAV: MoveClipDir left to _None, intentional?
14181 }
14182 else if (end_pressed)
14183 {
14184 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ContentSize.y;
14185 if (nav_rect_rel.IsInverted())
14186 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
14187 g.NavMoveDir = ImGuiDir_Up;
14188 g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
14189 // FIXME-NAV: MoveClipDir left to _None, intentional?
14190 }
14191 return nav_scoring_rect_offset_y;
14192 }
14193 return 0.0f;
14194}
14195
14196static void ImGui::NavEndFrame()
14197{
14198 ImGuiContext& g = *GImGui;
14199
14200 // Show CTRL+TAB list window
14201 if (g.NavWindowingTarget != NULL)
14202 NavUpdateWindowingOverlay();
14203
14204 // Perform wrap-around in menus
14205 // 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.
14206 // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame.
14207 if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & ImGuiNavMoveFlags_WrapMask_) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)
14208 NavUpdateCreateWrappingRequest();
14209}
14210
14211static void ImGui::NavUpdateCreateWrappingRequest()
14212{
14213 ImGuiContext& g = *GImGui;
14214 ImGuiWindow* window = g.NavWindow;
14215
14216 bool do_forward = false;
14217 ImRect bb_rel = window->NavRectRel[g.NavLayer];
14218 ImGuiDir clip_dir = g.NavMoveDir;
14219
14220 const ImGuiNavMoveFlags move_flags = g.NavMoveFlags;
14221 //const ImGuiAxis move_axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
14222 if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
14223 {
14224 bb_rel.Min.x = bb_rel.Max.x = window->ContentSize.x + window->WindowPadding.x;
14225 if (move_flags & ImGuiNavMoveFlags_WrapX)
14226 {
14227 bb_rel.TranslateY(-bb_rel.GetHeight()); // Previous row
14228 clip_dir = ImGuiDir_Up;
14229 }
14230 do_forward = true;
14231 }
14232 if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
14233 {
14234 bb_rel.Min.x = bb_rel.Max.x = -window->WindowPadding.x;
14235 if (move_flags & ImGuiNavMoveFlags_WrapX)
14236 {
14237 bb_rel.TranslateY(+bb_rel.GetHeight()); // Next row
14238 clip_dir = ImGuiDir_Down;
14239 }
14240 do_forward = true;
14241 }
14242 if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
14243 {
14244 bb_rel.Min.y = bb_rel.Max.y = window->ContentSize.y + window->WindowPadding.y;
14245 if (move_flags & ImGuiNavMoveFlags_WrapY)
14246 {
14247 bb_rel.TranslateX(-bb_rel.GetWidth()); // Previous column
14248 clip_dir = ImGuiDir_Left;
14249 }
14250 do_forward = true;
14251 }
14252 if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
14253 {
14254 bb_rel.Min.y = bb_rel.Max.y = -window->WindowPadding.y;
14255 if (move_flags & ImGuiNavMoveFlags_WrapY)
14256 {
14257 bb_rel.TranslateX(+bb_rel.GetWidth()); // Next column
14258 clip_dir = ImGuiDir_Right;
14259 }
14260 do_forward = true;
14261 }
14262 if (!do_forward)
14263 return;
14264 window->NavRectRel[g.NavLayer] = bb_rel;
14265 NavClearPreferredPosForAxis(ImGuiAxis_X);
14266 NavClearPreferredPosForAxis(ImGuiAxis_Y);
14267 NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags);
14268}
14269
14270static int ImGui::FindWindowFocusIndex(ImGuiWindow* window)
14271{
14272 ImGuiContext& g = *GImGui;
14273 IM_UNUSED(g);
14274 int order = window->FocusOrder;
14275 IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking)
14276 IM_ASSERT(g.WindowsFocusOrder[order] == window);
14277 return order;
14278}
14279
14280static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
14281{
14282 ImGuiContext& g = *GImGui;
14283 for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
14284 if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
14285 return g.WindowsFocusOrder[i];
14286 return NULL;
14287}
14288
14289static void NavUpdateWindowingTarget(int focus_change_dir)
14290{
14291 ImGuiContext& g = *GImGui;
14292 IM_ASSERT(g.NavWindowingTarget);
14293 if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
14294 return;
14295
14296 const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
14297 ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
14298 if (!window_target)
14299 window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
14300 if (window_target) // Don't reset windowing target if there's a single window in the list
14301 {
14302 g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
14303 g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
14304 }
14305 g.NavWindowingToggleLayer = false;
14306}
14307
14308// Windowing management mode
14309// Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
14310// Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
14311static void ImGui::NavUpdateWindowing()
14312{
14313 ImGuiContext& g = *GImGui;
14314 ImGuiIO& io = g.IO;
14315
14316 ImGuiWindow* apply_focus_window = NULL;
14317 bool apply_toggle_layer = false;
14318
14319 ImGuiWindow* modal_window = GetTopMostPopupModal();
14320 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.
14321 if (!allow_windowing)
14322 g.NavWindowingTarget = NULL;
14323
14324 // Fade out
14325 if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
14326 {
14327 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, 0.0f);
14328 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
14329 g.NavWindowingTargetAnim = NULL;
14330 }
14331
14332 // Start CTRL+Tab or Square+L/R window selection
14333 // (g.ConfigNavWindowingKeyNext/g.ConfigNavWindowingKeyPrev defaults are ImGuiMod_Ctrl|ImGuiKey_Tab and ImGuiMod_Ctrl|ImGuiMod_Shift|ImGuiKey_Tab)
14334 const ImGuiID owner_id = ImHashStr("###NavUpdateWindowing");
14335 const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
14336 const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
14337 const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
14338 const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
14339 const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, ImGuiInputFlags_None);
14340 const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard!
14341 bool just_started_windowing_from_null_focus = false;
14342 if (start_windowing_with_gamepad || start_windowing_with_keyboard)
14343 if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
14344 {
14345 g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // Current location
14346 g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
14347 g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
14348 g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer
14349 g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;
14350 if (g.NavWindow == NULL)
14351 just_started_windowing_from_null_focus = true;
14352
14353 // 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.
14354 if (keyboard_next_window || keyboard_prev_window)
14355 SetKeyOwnersForKeyChord((g.ConfigNavWindowingKeyNext | g.ConfigNavWindowingKeyPrev) & ImGuiMod_Mask_, owner_id);
14356 }
14357
14358 // Gamepad update
14359 g.NavWindowingTimer += io.DeltaTime;
14360 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Gamepad)
14361 {
14362 // 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
14363 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
14364
14365 // Select window to focus
14366 const int focus_change_dir = (int)IsKeyPressed(ImGuiKey_GamepadL1) - (int)IsKeyPressed(ImGuiKey_GamepadR1);
14367 if (focus_change_dir != 0 && !just_started_windowing_from_null_focus)
14368 {
14369 NavUpdateWindowingTarget(focus_change_dir);
14370 g.NavWindowingHighlightAlpha = 1.0f;
14371 }
14372
14373 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
14374 if (!IsKeyDown(ImGuiKey_NavGamepadMenu))
14375 {
14376 g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
14377 if (g.NavWindowingToggleLayer && g.NavWindow)
14378 apply_toggle_layer = true;
14379 else if (!g.NavWindowingToggleLayer)
14380 apply_focus_window = g.NavWindowingTarget;
14381 g.NavWindowingTarget = NULL;
14382 }
14383 }
14384
14385 // Keyboard: Focus
14386 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Keyboard)
14387 {
14388 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
14389 ImGuiKeyChord shared_mods = ((g.ConfigNavWindowingKeyNext ? g.ConfigNavWindowingKeyNext : ImGuiMod_Mask_) & (g.ConfigNavWindowingKeyPrev ? g.ConfigNavWindowingKeyPrev : ImGuiMod_Mask_)) & ImGuiMod_Mask_;
14390 IM_ASSERT(shared_mods != 0); // Next/Prev shortcut currently needs a shared modifier to "hold", otherwise Prev actions would keep cycling between two windows.
14391 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
14392 if ((keyboard_next_window || keyboard_prev_window) && !just_started_windowing_from_null_focus)
14393 NavUpdateWindowingTarget(keyboard_next_window ? -1 : +1);
14394 else if ((io.KeyMods & shared_mods) != shared_mods)
14395 apply_focus_window = g.NavWindowingTarget;
14396 }
14397
14398 // Keyboard: Press and Release ALT to toggle menu layer
14399 const ImGuiKey windowing_toggle_keys[] = { ImGuiKey_LeftAlt, ImGuiKey_RightAlt };
14400 bool windowing_toggle_layer_start = false;
14401 for (ImGuiKey windowing_toggle_key : windowing_toggle_keys)
14402 if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, 0, ImGuiKeyOwner_NoOwner))
14403 {
14404 windowing_toggle_layer_start = true;
14405 g.NavWindowingToggleLayer = true;
14406 g.NavWindowingToggleKey = windowing_toggle_key;
14407 g.NavInputSource = ImGuiInputSource_Keyboard;
14408 break;
14409 }
14410 if (g.NavWindowingToggleLayer && g.NavInputSource == ImGuiInputSource_Keyboard)
14411 {
14412 // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)
14413 // We cancel toggling nav layer when other modifiers are pressed. (See #4439)
14414 // - AltGR is Alt+Ctrl on some layout but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl).
14415 // We cancel toggling nav layer if an owner has claimed the key.
14416 if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper)
14417 g.NavWindowingToggleLayer = false;
14418 else if (windowing_toggle_layer_start == false && g.LastKeyboardKeyPressTime == g.Time)
14419 g.NavWindowingToggleLayer = false;
14420 else if (TestKeyOwner(g.NavWindowingToggleKey, ImGuiKeyOwner_NoOwner) == false || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_NoOwner) == false)
14421 g.NavWindowingToggleLayer = false;
14422
14423 // Apply layer toggle on Alt release
14424 // 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.
14425 if (IsKeyReleased(g.NavWindowingToggleKey) && g.NavWindowingToggleLayer)
14426 if (g.ActiveId == 0 || g.ActiveIdAllowOverlap)
14427 if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev))
14428 apply_toggle_layer = true;
14429 if (!IsKeyDown(g.NavWindowingToggleKey))
14430 g.NavWindowingToggleLayer = false;
14431 }
14432
14433 // Move window
14434 if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
14435 {
14436 ImVec2 nav_move_dir;
14437 if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift)
14438 nav_move_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);
14439 if (g.NavInputSource == ImGuiInputSource_Gamepad)
14440 nav_move_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
14441 if (nav_move_dir.x != 0.0f || nav_move_dir.y != 0.0f)
14442 {
14443 const float NAV_MOVE_SPEED = 800.0f;
14444 const float move_step = NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
14445 g.NavWindowingAccumDeltaPos += nav_move_dir * move_step;
14446 g.NavHighlightItemUnderNav = true;
14447 ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaPos);
14448 if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
14449 {
14450 ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindowDockTree;
14451 SetWindowPos(moving_window, moving_window->Pos + accum_floored, ImGuiCond_Always);
14452 g.NavWindowingAccumDeltaPos -= accum_floored;
14453 }
14454 }
14455 }
14456
14457 // Apply final focus
14458 if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
14459 {
14460 // FIXME: Many actions here could be part of a higher-level/reused function. Why aren't they in FocusWindow()
14461 // Investigate for each of them: ClearActiveID(), NavRestoreHighlightAfterMove(), NavRestoreLastChildNavWindow(), ClosePopupsOverWindow(), NavInitWindow()
14462 ImGuiViewport* previous_viewport = g.NavWindow ? g.NavWindow->Viewport : NULL;
14463 ClearActiveID();
14464 SetNavCursorVisibleAfterMove();
14465 ClosePopupsOverWindow(apply_focus_window, false);
14466 FocusWindow(apply_focus_window, ImGuiFocusRequestFlags_RestoreFocusedChild);
14467 apply_focus_window = g.NavWindow;
14468 if (apply_focus_window->NavLastIds[0] == 0)
14469 NavInitWindow(apply_focus_window, false);
14470
14471 // If the window has ONLY a menu layer (no main layer), select it directly
14472 // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame,
14473 // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since
14474 // the target window as already been previewed once.
14475 // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases,
14476 // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask*
14477 // won't be valid.
14478 if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu))
14479 g.NavLayer = ImGuiNavLayer_Menu;
14480
14481 // Request OS level focus
14482 if (apply_focus_window->Viewport != previous_viewport && g.PlatformIO.Platform_SetWindowFocus)
14483 g.PlatformIO.Platform_SetWindowFocus(apply_focus_window->Viewport);
14484 }
14485 if (apply_focus_window)
14486 g.NavWindowingTarget = NULL;
14487
14488 // Apply menu/layer toggle
14489 if (apply_toggle_layer && g.NavWindow)
14490 {
14491 ClearActiveID();
14492
14493 // Move to parent menu if necessary
14494 ImGuiWindow* new_nav_window = g.NavWindow;
14495 while (new_nav_window->ParentWindow
14496 && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
14497 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
14498 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
14499 new_nav_window = new_nav_window->ParentWindow;
14500 if (new_nav_window != g.NavWindow)
14501 {
14502 ImGuiWindow* old_nav_window = g.NavWindow;
14503 FocusWindow(new_nav_window);
14504 new_nav_window->NavLastChildNavWindow = old_nav_window;
14505 }
14506
14507 // Toggle layer
14508 const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
14509 if (new_nav_layer != g.NavLayer)
14510 {
14511 // Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?)
14512 const bool preserve_layer_1_nav_id = (new_nav_window->DockNodeAsHost != NULL);
14513 if (new_nav_layer == ImGuiNavLayer_Menu && !preserve_layer_1_nav_id)
14514 g.NavWindow->NavLastIds[new_nav_layer] = 0;
14515 NavRestoreLayer(new_nav_layer);
14516 SetNavCursorVisibleAfterMove();
14517 }
14518 }
14519}
14520
14521// Window has already passed the IsWindowNavFocusable()
14522static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
14523{
14524 if (window->Flags & ImGuiWindowFlags_Popup)
14525 return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingPopup);
14526 if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
14527 return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingMainMenuBar);
14528 if (window->DockNodeAsHost)
14529 return "(Dock node)"; // Not normally shown to user.
14530 return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingUntitled);
14531}
14532
14533// Overlay displayed when using CTRL+TAB. Called by EndFrame().
14534void ImGui::NavUpdateWindowingOverlay()
14535{
14536 ImGuiContext& g = *GImGui;
14537 IM_ASSERT(g.NavWindowingTarget != NULL);
14538
14539 if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
14540 return;
14541
14542 if (g.NavWindowingListWindow == NULL)
14543 g.NavWindowingListWindow = FindWindowByName("###NavWindowingList");
14544 const ImGuiViewport* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ GetMainViewport();
14545 SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
14546 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
14547 PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
14548 Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
14549 if (g.ContextName[0] != 0)
14550 SeparatorText(g.ContextName);
14551 for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
14552 {
14553 ImGuiWindow* window = g.WindowsFocusOrder[n];
14554 IM_ASSERT(window != NULL); // Fix static analyzers
14555 if (!IsWindowNavFocusable(window))
14556 continue;
14557 const char* label = window->Name;
14558 if (label == FindRenderedTextEnd(label))
14559 label = GetFallbackWindowNameForWindowingList(window);
14560 Selectable(label, g.NavWindowingTarget == window);
14561 }
14562 End();
14563 PopStyleVar();
14564}
14565
14566
14567//-----------------------------------------------------------------------------
14568// [SECTION] DRAG AND DROP
14569//-----------------------------------------------------------------------------
14570
14571bool ImGui::IsDragDropActive()
14572{
14573 ImGuiContext& g = *GImGui;
14574 return g.DragDropActive;
14575}
14576
14577void ImGui::ClearDragDrop()
14578{
14579 ImGuiContext& g = *GImGui;
14580 if (g.DragDropActive)
14581 IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] ClearDragDrop()\n");
14582 g.DragDropActive = false;
14583 g.DragDropPayload.Clear();
14584 g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
14585 g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
14586 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
14587 g.DragDropAcceptFrameCount = -1;
14588
14589 g.DragDropPayloadBufHeap.clear();
14590 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
14591}
14592
14593bool ImGui::BeginTooltipHidden()
14594{
14595 ImGuiContext& g = *GImGui;
14596 bool ret = Begin("##Tooltip_Hidden", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
14597 SetWindowHiddenAndSkipItemsForCurrentFrame(g.CurrentWindow);
14598 return ret;
14599}
14600
14601// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
14602// If the item has an identifier:
14603// - This assume/require the item to be activated (typically via ButtonBehavior).
14604// - 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.
14605// - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag.
14606// If the item has no identifier:
14607// - Currently always assume left mouse button.
14608bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
14609{
14610 ImGuiContext& g = *GImGui;
14611 ImGuiWindow* window = g.CurrentWindow;
14612
14613 // FIXME-DRAGDROP: While in the common-most "drag from non-zero active id" case we can tell the mouse button,
14614 // in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic).
14615 ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
14616
14617 bool source_drag_active = false;
14618 ImGuiID source_id = 0;
14619 ImGuiID source_parent_id = 0;
14620 if ((flags & ImGuiDragDropFlags_SourceExtern) == 0)
14621 {
14622 source_id = g.LastItemData.ID;
14623 if (source_id != 0)
14624 {
14625 // Common path: items with ID
14626 if (g.ActiveId != source_id)
14627 return false;
14628 if (g.ActiveIdMouseButton != -1)
14629 mouse_button = g.ActiveIdMouseButton;
14630 if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
14631 return false;
14632 g.ActiveIdAllowOverlap = false;
14633 }
14634 else
14635 {
14636 // Uncommon path: items without ID
14637 if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
14638 return false;
14639 if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
14640 return false;
14641
14642 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
14643 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag.
14644 if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
14645 {
14646 IM_ASSERT(0);
14647 return false;
14648 }
14649
14650 // Magic fallback to handle items with no assigned ID, e.g. Text(), Image()
14651 // We build a throwaway ID based on current ID stack + relative AABB of items in window.
14652 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING/RESIZINGG OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
14653 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
14654 // Rely on keeping other window->LastItemXXX fields intact.
14655 source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect);
14656 KeepAliveID(source_id);
14657 bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.ItemFlags);
14658 if (is_hovered && g.IO.MouseClicked[mouse_button])
14659 {
14660 SetActiveID(source_id, window);
14661 FocusWindow(window);
14662 }
14663 if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
14664 g.ActiveIdAllowOverlap = is_hovered;
14665 }
14666 if (g.ActiveId != source_id)
14667 return false;
14668 source_parent_id = window->IDStack.back();
14669 source_drag_active = IsMouseDragging(mouse_button);
14670
14671 // Disable navigation and key inputs while dragging + cancel existing request if any
14672 SetActiveIdUsingAllKeyboardKeys();
14673 }
14674 else
14675 {
14676 // When ImGuiDragDropFlags_SourceExtern is set:
14677 window = NULL;
14678 source_id = ImHashStr("#SourceExtern");
14679 source_drag_active = true;
14680 mouse_button = g.IO.MouseDown[0] ? 0 : -1;
14681 KeepAliveID(source_id);
14682 SetActiveID(source_id, NULL);
14683 }
14684
14685 IM_ASSERT(g.DragDropWithinTarget == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14686 if (!source_drag_active)
14687 return false;
14688
14689 // Activate drag and drop
14690 if (!g.DragDropActive)
14691 {
14692 IM_ASSERT(source_id != 0);
14693 ClearDragDrop();
14694 IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] BeginDragDropSource() DragDropActive = true, source_id = 0x%08X%s\n",
14695 source_id, (flags & ImGuiDragDropFlags_SourceExtern) ? " (EXTERN)" : "");
14696 ImGuiPayload& payload = g.DragDropPayload;
14697 payload.SourceId = source_id;
14698 payload.SourceParentId = source_parent_id;
14699 g.DragDropActive = true;
14700 g.DragDropSourceFlags = flags;
14701 g.DragDropMouseButton = mouse_button;
14702 if (payload.SourceId == g.ActiveId)
14703 g.ActiveIdNoClearOnFocusLoss = true;
14704 }
14705 g.DragDropSourceFrameCount = g.FrameCount;
14706 g.DragDropWithinSource = true;
14707
14708 if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
14709 {
14710 // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
14711 // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
14712 bool ret;
14713 if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
14714 ret = BeginTooltipHidden();
14715 else
14716 ret = BeginTooltip();
14717 IM_ASSERT(ret); // FIXME-NEWBEGIN: If this ever becomes false, we need to Begin("##Hidden", NULL, ImGuiWindowFlags_NoSavedSettings) + SetWindowHiddendAndSkipItemsForCurrentFrame().
14718 IM_UNUSED(ret);
14719 }
14720
14721 if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
14722 g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
14723
14724 return true;
14725}
14726
14727void ImGui::EndDragDropSource()
14728{
14729 ImGuiContext& g = *GImGui;
14730 IM_ASSERT(g.DragDropActive);
14731 IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?");
14732
14733 if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
14734 EndTooltip();
14735
14736 // Discard the drag if have not called SetDragDropPayload()
14737 if (g.DragDropPayload.DataFrameCount == -1)
14738 ClearDragDrop();
14739 g.DragDropWithinSource = false;
14740}
14741
14742// Use 'cond' to choose to submit payload on drag start or every frame
14743bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
14744{
14745 ImGuiContext& g = *GImGui;
14746 ImGuiPayload& payload = g.DragDropPayload;
14747 if (cond == 0)
14748 cond = ImGuiCond_Always;
14749
14750 IM_ASSERT(type != NULL);
14751 IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
14752 IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
14753 IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
14754 IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
14755
14756 if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
14757 {
14758 // Copy payload
14759 ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
14760 g.DragDropPayloadBufHeap.resize(0);
14761 if (data_size > sizeof(g.DragDropPayloadBufLocal))
14762 {
14763 // Store in heap
14764 g.DragDropPayloadBufHeap.resize((int)data_size);
14765 payload.Data = g.DragDropPayloadBufHeap.Data;
14766 memcpy(payload.Data, data, data_size);
14767 }
14768 else if (data_size > 0)
14769 {
14770 // Store locally
14771 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
14772 payload.Data = g.DragDropPayloadBufLocal;
14773 memcpy(payload.Data, data, data_size);
14774 }
14775 else
14776 {
14777 payload.Data = NULL;
14778 }
14779 payload.DataSize = (int)data_size;
14780 }
14781 payload.DataFrameCount = g.FrameCount;
14782
14783 // Return whether the payload has been accepted
14784 return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
14785}
14786
14787bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
14788{
14789 ImGuiContext& g = *GImGui;
14790 if (!g.DragDropActive)
14791 return false;
14792
14793 ImGuiWindow* window = g.CurrentWindow;
14794 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
14795 if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree)
14796 return false;
14797 IM_ASSERT(id != 0);
14798 if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
14799 return false;
14800 if (window->SkipItems)
14801 return false;
14802
14803 IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14804 g.DragDropTargetRect = bb;
14805 g.DragDropTargetClipRect = window->ClipRect; // May want to be overridden by user depending on use case?
14806 g.DragDropTargetId = id;
14807 g.DragDropWithinTarget = true;
14808 return true;
14809}
14810
14811// We don't use BeginDragDropTargetCustom() and duplicate its code because:
14812// 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.
14813// 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
14814// Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
14815bool ImGui::BeginDragDropTarget()
14816{
14817 ImGuiContext& g = *GImGui;
14818 if (!g.DragDropActive)
14819 return false;
14820
14821 ImGuiWindow* window = g.CurrentWindow;
14822 if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect))
14823 return false;
14824 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
14825 if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree || window->SkipItems)
14826 return false;
14827
14828 const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect;
14829 ImGuiID id = g.LastItemData.ID;
14830 if (id == 0)
14831 {
14832 id = window->GetIDFromRectangle(display_rect);
14833 KeepAliveID(id);
14834 }
14835 if (g.DragDropPayload.SourceId == id)
14836 return false;
14837
14838 IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14839 g.DragDropTargetRect = display_rect;
14840 g.DragDropTargetClipRect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect : window->ClipRect;
14841 g.DragDropTargetId = id;
14842 g.DragDropWithinTarget = true;
14843 return true;
14844}
14845
14846bool ImGui::IsDragDropPayloadBeingAccepted()
14847{
14848 ImGuiContext& g = *GImGui;
14849 return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
14850}
14851
14852const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
14853{
14854 ImGuiContext& g = *GImGui;
14855 ImGuiPayload& payload = g.DragDropPayload;
14856 IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
14857 IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
14858 if (type != NULL && !payload.IsDataType(type))
14859 return NULL;
14860
14861 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
14862 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
14863 const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
14864 ImRect r = g.DragDropTargetRect;
14865 float r_surface = r.GetWidth() * r.GetHeight();
14866 if (r_surface > g.DragDropAcceptIdCurrRectSurface)
14867 return NULL;
14868
14869 g.DragDropAcceptFlags = flags;
14870 g.DragDropAcceptIdCurr = g.DragDropTargetId;
14871 g.DragDropAcceptIdCurrRectSurface = r_surface;
14872 //IMGUI_DEBUG_LOG("AcceptDragDropPayload(): %08X: accept\n", g.DragDropTargetId);
14873
14874 // Render default drop visuals
14875 payload.Preview = was_accepted_previously;
14876 flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that live for 1 frame)
14877 if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
14878 RenderDragDropTargetRect(r, g.DragDropTargetClipRect);
14879
14880 g.DragDropAcceptFrameCount = g.FrameCount;
14881 if ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) && g.DragDropMouseButton == -1)
14882 payload.Delivery = was_accepted_previously && (g.DragDropSourceFrameCount < g.FrameCount);
14883 else
14884 payload.Delivery = was_accepted_previously && !IsMouseDown(g.DragDropMouseButton); // For extern drag sources affecting OS window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased()
14885 if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
14886 return NULL;
14887
14888 if (payload.Delivery)
14889 IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] AcceptDragDropPayload(): 0x%08X: payload delivery\n", g.DragDropTargetId);
14890 return &payload;
14891}
14892
14893// FIXME-STYLE FIXME-DRAGDROP: Settle on a proper default visuals for drop target.
14894void ImGui::RenderDragDropTargetRect(const ImRect& bb, const ImRect& item_clip_rect)
14895{
14896 ImGuiContext& g = *GImGui;
14897 ImGuiWindow* window = g.CurrentWindow;
14898 ImRect bb_display = bb;
14899 bb_display.ClipWith(item_clip_rect); // Clip THEN expand so we have a way to visualize that target is not entirely visible.
14900 bb_display.Expand(3.5f);
14901 bool push_clip_rect = !window->ClipRect.Contains(bb_display);
14902 if (push_clip_rect)
14903 window->DrawList->PushClipRectFullScreen();
14904 window->DrawList->AddRect(bb_display.Min, bb_display.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f);
14905 if (push_clip_rect)
14906 window->DrawList->PopClipRect();
14907}
14908
14909const ImGuiPayload* ImGui::GetDragDropPayload()
14910{
14911 ImGuiContext& g = *GImGui;
14912 return (g.DragDropActive && g.DragDropPayload.DataFrameCount != -1) ? &g.DragDropPayload : NULL;
14913}
14914
14915void ImGui::EndDragDropTarget()
14916{
14917 ImGuiContext& g = *GImGui;
14918 IM_ASSERT(g.DragDropActive);
14919 IM_ASSERT(g.DragDropWithinTarget);
14920 g.DragDropWithinTarget = false;
14921
14922 // Clear drag and drop state payload right after delivery
14923 if (g.DragDropPayload.Delivery)
14924 ClearDragDrop();
14925}
14926
14927//-----------------------------------------------------------------------------
14928// [SECTION] LOGGING/CAPTURING
14929//-----------------------------------------------------------------------------
14930// All text output from the interface can be captured into tty/file/clipboard.
14931// By default, tree nodes are automatically opened during logging.
14932//-----------------------------------------------------------------------------
14933
14934// Pass text data straight to log (without being displayed)
14935static inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args)
14936{
14937 if (g.LogFile)
14938 {
14939 g.LogBuffer.Buf.resize(0);
14940 g.LogBuffer.appendfv(fmt, args);
14941 ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
14942 }
14943 else
14944 {
14945 g.LogBuffer.appendfv(fmt, args);
14946 }
14947}
14948
14949void ImGui::LogText(const char* fmt, ...)
14950{
14951 ImGuiContext& g = *GImGui;
14952 if (!g.LogEnabled)
14953 return;
14954
14955 va_list args;
14956 va_start(args, fmt);
14957 LogTextV(g, fmt, args);
14958 va_end(args);
14959}
14960
14961void ImGui::LogTextV(const char* fmt, va_list args)
14962{
14963 ImGuiContext& g = *GImGui;
14964 if (!g.LogEnabled)
14965 return;
14966
14967 LogTextV(g, fmt, args);
14968}
14969
14970// Internal version that takes a position to decide on newline placement and pad items according to their depth.
14971// We split text into individual lines to add current tree level padding
14972// FIXME: This code is a little complicated perhaps, considering simplifying the whole system.
14973void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
14974{
14975 ImGuiContext& g = *GImGui;
14976 ImGuiWindow* window = g.CurrentWindow;
14977
14978 const char* prefix = g.LogNextPrefix;
14979 const char* suffix = g.LogNextSuffix;
14980 g.LogNextPrefix = g.LogNextSuffix = NULL;
14981
14982 if (!text_end)
14983 text_end = FindRenderedTextEnd(text, text_end);
14984
14985 const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1);
14986 if (ref_pos)
14987 g.LogLinePosY = ref_pos->y;
14988 if (log_new_line)
14989 {
14990 LogText(IM_NEWLINE);
14991 g.LogLineFirstItem = true;
14992 }
14993
14994 if (prefix)
14995 LogRenderedText(ref_pos, prefix, prefix + strlen(prefix)); // Calculate end ourself to ensure "##" are included here.
14996
14997 // Re-adjust padding if we have popped out of our starting depth
14998 if (g.LogDepthRef > window->DC.TreeDepth)
14999 g.LogDepthRef = window->DC.TreeDepth;
15000 const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
15001
15002 const char* text_remaining = text;
15003 for (;;)
15004 {
15005 // Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry.
15006 // We don't add a trailing \n yet to allow a subsequent item on the same line to be captured.
15007 const char* line_start = text_remaining;
15008 const char* line_end = ImStreolRange(line_start, text_end);
15009 const bool is_last_line = (line_end == text_end);
15010 if (line_start != line_end || !is_last_line)
15011 {
15012 const int line_length = (int)(line_end - line_start);
15013 const int indentation = g.LogLineFirstItem ? tree_depth * 4 : 1;
15014 LogText("%*s%.*s", indentation, "", line_length, line_start);
15015 g.LogLineFirstItem = false;
15016 if (*line_end == '\n')
15017 {
15018 LogText(IM_NEWLINE);
15019 g.LogLineFirstItem = true;
15020 }
15021 }
15022 if (is_last_line)
15023 break;
15024 text_remaining = line_end + 1;
15025 }
15026
15027 if (suffix)
15028 LogRenderedText(ref_pos, suffix, suffix + strlen(suffix));
15029}
15030
15031// Start logging/capturing text output
15032void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
15033{
15034 ImGuiContext& g = *GImGui;
15035 ImGuiWindow* window = g.CurrentWindow;
15036 IM_ASSERT(g.LogEnabled == false);
15037 IM_ASSERT(g.LogFile == NULL);
15038 IM_ASSERT(g.LogBuffer.empty());
15039 g.LogEnabled = g.ItemUnclipByLog = true;
15040 g.LogType = type;
15041 g.LogNextPrefix = g.LogNextSuffix = NULL;
15042 g.LogDepthRef = window->DC.TreeDepth;
15043 g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
15044 g.LogLinePosY = FLT_MAX;
15045 g.LogLineFirstItem = true;
15046}
15047
15048// Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText)
15049void ImGui::LogSetNextTextDecoration(const char* prefix, const char* suffix)
15050{
15051 ImGuiContext& g = *GImGui;
15052 g.LogNextPrefix = prefix;
15053 g.LogNextSuffix = suffix;
15054}
15055
15056void ImGui::LogToTTY(int auto_open_depth)
15057{
15058 ImGuiContext& g = *GImGui;
15059 if (g.LogEnabled)
15060 return;
15061 IM_UNUSED(auto_open_depth);
15062#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
15063 LogBegin(ImGuiLogType_TTY, auto_open_depth);
15064 g.LogFile = stdout;
15065#endif
15066}
15067
15068// Start logging/capturing text output to given file
15069void ImGui::LogToFile(int auto_open_depth, const char* filename)
15070{
15071 ImGuiContext& g = *GImGui;
15072 if (g.LogEnabled)
15073 return;
15074
15075 // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
15076 // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
15077 // By opening the file in binary mode "ab" we have consistent output everywhere.
15078 if (!filename)
15079 filename = g.IO.LogFilename;
15080 if (!filename || !filename[0])
15081 return;
15082 ImFileHandle f = ImFileOpen(filename, "ab");
15083 if (!f)
15084 {
15085 IM_ASSERT(0);
15086 return;
15087 }
15088
15089 LogBegin(ImGuiLogType_File, auto_open_depth);
15090 g.LogFile = f;
15091}
15092
15093// Start logging/capturing text output to clipboard
15094void ImGui::LogToClipboard(int auto_open_depth)
15095{
15096 ImGuiContext& g = *GImGui;
15097 if (g.LogEnabled)
15098 return;
15099 LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
15100}
15101
15102void ImGui::LogToBuffer(int auto_open_depth)
15103{
15104 ImGuiContext& g = *GImGui;
15105 if (g.LogEnabled)
15106 return;
15107 LogBegin(ImGuiLogType_Buffer, auto_open_depth);
15108}
15109
15110void ImGui::LogFinish()
15111{
15112 ImGuiContext& g = *GImGui;
15113 if (!g.LogEnabled)
15114 return;
15115
15116 LogText(IM_NEWLINE);
15117 switch (g.LogType)
15118 {
15119 case ImGuiLogType_TTY:
15120#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
15121 fflush(g.LogFile);
15122#endif
15123 break;
15124 case ImGuiLogType_File:
15125 ImFileClose(g.LogFile);
15126 break;
15127 case ImGuiLogType_Buffer:
15128 break;
15129 case ImGuiLogType_Clipboard:
15130 if (!g.LogBuffer.empty())
15131 SetClipboardText(g.LogBuffer.begin());
15132 break;
15133 case ImGuiLogType_None:
15134 IM_ASSERT(0);
15135 break;
15136 }
15137
15138 g.LogEnabled = g.ItemUnclipByLog = false;
15139 g.LogType = ImGuiLogType_None;
15140 g.LogFile = NULL;
15141 g.LogBuffer.clear();
15142}
15143
15144// Helper to display logging buttons
15145// FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
15146void ImGui::LogButtons()
15147{
15148 ImGuiContext& g = *GImGui;
15149
15150 PushID("LogButtons");
15151#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
15152 const bool log_to_tty = Button("Log To TTY"); SameLine();
15153#else
15154 const bool log_to_tty = false;
15155#endif
15156 const bool log_to_file = Button("Log To File"); SameLine();
15157 const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
15158 PushItemFlag(ImGuiItemFlags_NoTabStop, true);
15159 SetNextItemWidth(80.0f);
15160 SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
15161 PopItemFlag();
15162 PopID();
15163
15164 // Start logging at the end of the function so that the buttons don't appear in the log
15165 if (log_to_tty)
15166 LogToTTY();
15167 if (log_to_file)
15168 LogToFile();
15169 if (log_to_clipboard)
15170 LogToClipboard();
15171}
15172
15173
15174//-----------------------------------------------------------------------------
15175// [SECTION] SETTINGS
15176//-----------------------------------------------------------------------------
15177// - UpdateSettings() [Internal]
15178// - MarkIniSettingsDirty() [Internal]
15179// - FindSettingsHandler() [Internal]
15180// - ClearIniSettings() [Internal]
15181// - LoadIniSettingsFromDisk()
15182// - LoadIniSettingsFromMemory()
15183// - SaveIniSettingsToDisk()
15184// - SaveIniSettingsToMemory()
15185//-----------------------------------------------------------------------------
15186// - CreateNewWindowSettings() [Internal]
15187// - FindWindowSettingsByID() [Internal]
15188// - FindWindowSettingsByWindow() [Internal]
15189// - ClearWindowSettings() [Internal]
15190// - WindowSettingsHandler_***() [Internal]
15191//-----------------------------------------------------------------------------
15192
15193// Called by NewFrame()
15194void ImGui::UpdateSettings()
15195{
15196 // Load settings on first frame (if not explicitly loaded manually before)
15197 ImGuiContext& g = *GImGui;
15198 if (!g.SettingsLoaded)
15199 {
15200 IM_ASSERT(g.SettingsWindows.empty());
15201 if (g.IO.IniFilename)
15202 LoadIniSettingsFromDisk(g.IO.IniFilename);
15203 g.SettingsLoaded = true;
15204 }
15205
15206 // Save settings (with a delay after the last modification, so we don't spam disk too much)
15207 if (g.SettingsDirtyTimer > 0.0f)
15208 {
15209 g.SettingsDirtyTimer -= g.IO.DeltaTime;
15210 if (g.SettingsDirtyTimer <= 0.0f)
15211 {
15212 if (g.IO.IniFilename != NULL)
15213 SaveIniSettingsToDisk(g.IO.IniFilename);
15214 else
15215 g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
15216 g.SettingsDirtyTimer = 0.0f;
15217 }
15218 }
15219}
15220
15221void ImGui::MarkIniSettingsDirty()
15222{
15223 ImGuiContext& g = *GImGui;
15224 if (g.SettingsDirtyTimer <= 0.0f)
15225 g.SettingsDirtyTimer = g.IO.IniSavingRate;
15226}
15227
15228void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
15229{
15230 ImGuiContext& g = *GImGui;
15231 if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
15232 if (g.SettingsDirtyTimer <= 0.0f)
15233 g.SettingsDirtyTimer = g.IO.IniSavingRate;
15234}
15235
15236void ImGui::AddSettingsHandler(const ImGuiSettingsHandler* handler)
15237{
15238 ImGuiContext& g = *GImGui;
15239 IM_ASSERT(FindSettingsHandler(handler->TypeName) == NULL);
15240 g.SettingsHandlers.push_back(*handler);
15241}
15242
15243void ImGui::RemoveSettingsHandler(const char* type_name)
15244{
15245 ImGuiContext& g = *GImGui;
15246 if (ImGuiSettingsHandler* handler = FindSettingsHandler(type_name))
15247 g.SettingsHandlers.erase(handler);
15248}
15249
15250ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
15251{
15252 ImGuiContext& g = *GImGui;
15253 const ImGuiID type_hash = ImHashStr(type_name);
15254 for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15255 if (handler.TypeHash == type_hash)
15256 return &handler;
15257 return NULL;
15258}
15259
15260// Clear all settings (windows, tables, docking etc.)
15261void ImGui::ClearIniSettings()
15262{
15263 ImGuiContext& g = *GImGui;
15264 g.SettingsIniData.clear();
15265 for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15266 if (handler.ClearAllFn != NULL)
15267 handler.ClearAllFn(&g, &handler);
15268}
15269
15270void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
15271{
15272 size_t file_data_size = 0;
15273 char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
15274 if (!file_data)
15275 return;
15276 if (file_data_size > 0)
15277 LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
15278 IM_FREE(file_data);
15279}
15280
15281// Zero-tolerance, no error reporting, cheap .ini parsing
15282// Set ini_size==0 to let us use strlen(ini_data). Do not call this function with a 0 if your buffer is actually empty!
15283void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
15284{
15285 ImGuiContext& g = *GImGui;
15286 IM_ASSERT(g.Initialized);
15287 //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()");
15288 //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
15289
15290 // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
15291 // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy..
15292 if (ini_size == 0)
15293 ini_size = strlen(ini_data);
15294 g.SettingsIniData.Buf.resize((int)ini_size + 1);
15295 char* const buf = g.SettingsIniData.Buf.Data;
15296 char* const buf_end = buf + ini_size;
15297 memcpy(buf, ini_data, ini_size);
15298 buf_end[0] = 0;
15299
15300 // Call pre-read handlers
15301 // Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
15302 for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15303 if (handler.ReadInitFn != NULL)
15304 handler.ReadInitFn(&g, &handler);
15305
15306 void* entry_data = NULL;
15307 ImGuiSettingsHandler* entry_handler = NULL;
15308
15309 char* line_end = NULL;
15310 for (char* line = buf; line < buf_end; line = line_end + 1)
15311 {
15312 // Skip new lines markers, then find end of the line
15313 while (*line == '\n' || *line == '\r')
15314 line++;
15315 line_end = line;
15316 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
15317 line_end++;
15318 line_end[0] = 0;
15319 if (line[0] == ';')
15320 continue;
15321 if (line[0] == '[' && line_end > line && line_end[-1] == ']')
15322 {
15323 // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
15324 line_end[-1] = 0;
15325 const char* name_end = line_end - 1;
15326 const char* type_start = line + 1;
15327 char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
15328 const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
15329 if (!type_end || !name_start)
15330 continue;
15331 *type_end = 0; // Overwrite first ']'
15332 name_start++; // Skip second '['
15333 entry_handler = FindSettingsHandler(type_start);
15334 entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
15335 }
15336 else if (entry_handler != NULL && entry_data != NULL)
15337 {
15338 // Let type handler parse the line
15339 entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
15340 }
15341 }
15342 g.SettingsLoaded = true;
15343
15344 // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
15345 memcpy(buf, ini_data, ini_size);
15346
15347 // Call post-read handlers
15348 for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15349 if (handler.ApplyAllFn != NULL)
15350 handler.ApplyAllFn(&g, &handler);
15351}
15352
15353void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
15354{
15355 ImGuiContext& g = *GImGui;
15356 g.SettingsDirtyTimer = 0.0f;
15357 if (!ini_filename)
15358 return;
15359
15360 size_t ini_data_size = 0;
15361 const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
15362 ImFileHandle f = ImFileOpen(ini_filename, "wt");
15363 if (!f)
15364 return;
15365 ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
15366 ImFileClose(f);
15367}
15368
15369// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
15370const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
15371{
15372 ImGuiContext& g = *GImGui;
15373 g.SettingsDirtyTimer = 0.0f;
15374 g.SettingsIniData.Buf.resize(0);
15375 g.SettingsIniData.Buf.push_back(0);
15376 for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15377 handler.WriteAllFn(&g, &handler, &g.SettingsIniData);
15378 if (out_size)
15379 *out_size = (size_t)g.SettingsIniData.size();
15380 return g.SettingsIniData.c_str();
15381}
15382
15383ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
15384{
15385 ImGuiContext& g = *GImGui;
15386
15387 if (g.IO.ConfigDebugIniSettings == false)
15388 {
15389 // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
15390 // Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier.
15391 if (const char* p = strstr(name, "###"))
15392 name = p;
15393 }
15394 const size_t name_len = strlen(name);
15395
15396 // Allocate chunk
15397 const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
15398 ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
15399 IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
15400 settings->ID = ImHashStr(name, name_len);
15401 memcpy(settings->GetName(), name, name_len + 1); // Store with zero terminator
15402
15403 return settings;
15404}
15405
15406// We don't provide a FindWindowSettingsByName() because Docking system doesn't always hold on names.
15407// This is called once per window .ini entry + once per newly instantiated window.
15408ImGuiWindowSettings* ImGui::FindWindowSettingsByID(ImGuiID id)
15409{
15410 ImGuiContext& g = *GImGui;
15411 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15412 if (settings->ID == id && !settings->WantDelete)
15413 return settings;
15414 return NULL;
15415}
15416
15417// This is faster if you are holding on a Window already as we don't need to perform a search.
15418ImGuiWindowSettings* ImGui::FindWindowSettingsByWindow(ImGuiWindow* window)
15419{
15420 ImGuiContext& g = *GImGui;
15421 if (window->SettingsOffset != -1)
15422 return g.SettingsWindows.ptr_from_offset(window->SettingsOffset);
15423 return FindWindowSettingsByID(window->ID);
15424}
15425
15426// This will revert window to its initial state, including enabling the ImGuiCond_FirstUseEver/ImGuiCond_Once conditions once more.
15427void ImGui::ClearWindowSettings(const char* name)
15428{
15429 //IMGUI_DEBUG_LOG("ClearWindowSettings('%s')\n", name);
15430 ImGuiContext& g = *GImGui;
15431 ImGuiWindow* window = FindWindowByName(name);
15432 if (window != NULL)
15433 {
15434 window->Flags |= ImGuiWindowFlags_NoSavedSettings;
15435 InitOrLoadWindowSettings(window, NULL);
15436 if (window->DockId != 0)
15437 DockContextProcessUndockWindow(&g, window, true);
15438 }
15439 if (ImGuiWindowSettings* settings = window ? FindWindowSettingsByWindow(window) : FindWindowSettingsByID(ImHashStr(name)))
15440 settings->WantDelete = true;
15441}
15442
15443static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
15444{
15445 ImGuiContext& g = *ctx;
15446 for (ImGuiWindow* window : g.Windows)
15447 window->SettingsOffset = -1;
15448 g.SettingsWindows.clear();
15449}
15450
15451static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
15452{
15453 ImGuiID id = ImHashStr(name);
15454 ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByID(id);
15455 if (settings)
15456 *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry
15457 else
15458 settings = ImGui::CreateNewWindowSettings(name);
15459 settings->ID = id;
15460 settings->WantApply = true;
15461 return (void*)settings;
15462}
15463
15464static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
15465{
15466 ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
15467 int x, y;
15468 int i;
15469 ImU32 u1;
15470 if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); }
15471 else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); }
15472 else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1) { settings->ViewportId = u1; }
15473 else if (sscanf(line, "ViewportPos=%i,%i", &x, &y) == 2){ settings->ViewportPos = ImVec2ih((short)x, (short)y); }
15474 else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); }
15475 else if (sscanf(line, "IsChild=%d", &i) == 1) { settings->IsChild = (i != 0); }
15476 else if (sscanf(line, "DockId=0x%X,%d", &u1, &i) == 2) { settings->DockId = u1; settings->DockOrder = (short)i; }
15477 else if (sscanf(line, "DockId=0x%X", &u1) == 1) { settings->DockId = u1; settings->DockOrder = -1; }
15478 else if (sscanf(line, "ClassId=0x%X", &u1) == 1) { settings->ClassId = u1; }
15479}
15480
15481// Apply to existing windows (if any)
15482static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
15483{
15484 ImGuiContext& g = *ctx;
15485 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15486 if (settings->WantApply)
15487 {
15488 if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))
15489 ApplyWindowSettings(window, settings);
15490 settings->WantApply = false;
15491 }
15492}
15493
15494static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
15495{
15496 // Gather data from windows that were active during this session
15497 // (if a window wasn't opened in this session we preserve its settings)
15498 ImGuiContext& g = *ctx;
15499 for (ImGuiWindow* window : g.Windows)
15500 {
15501 if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
15502 continue;
15503
15504 ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByWindow(window);
15505 if (!settings)
15506 {
15507 settings = ImGui::CreateNewWindowSettings(window->Name);
15508 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
15509 }
15510 IM_ASSERT(settings->ID == window->ID);
15511 settings->Pos = ImVec2ih(window->Pos - window->ViewportPos);
15512 settings->Size = ImVec2ih(window->SizeFull);
15513 settings->ViewportId = window->ViewportId;
15514 settings->ViewportPos = ImVec2ih(window->ViewportPos);
15515 IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId);
15516 settings->DockId = window->DockId;
15517 settings->ClassId = window->WindowClass.ClassId;
15518 settings->DockOrder = window->DockOrder;
15519 settings->Collapsed = window->Collapsed;
15520 settings->IsChild = (window->RootWindow != window); // Cannot rely on ImGuiWindowFlags_ChildWindow here as docked windows have this set.
15521 settings->WantDelete = false;
15522 }
15523
15524 // Write to text buffer
15525 buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
15526 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15527 {
15528 if (settings->WantDelete)
15529 continue;
15530 const char* settings_name = settings->GetName();
15531 buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
15532 if (settings->IsChild)
15533 {
15534 buf->appendf("IsChild=1\n");
15535 buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
15536 }
15537 else
15538 {
15539 if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID)
15540 {
15541 buf->appendf("ViewportPos=%d,%d\n", settings->ViewportPos.x, settings->ViewportPos.y);
15542 buf->appendf("ViewportId=0x%08X\n", settings->ViewportId);
15543 }
15544 if (settings->Pos.x != 0 || settings->Pos.y != 0 || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID)
15545 buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
15546 if (settings->Size.x != 0 || settings->Size.y != 0)
15547 buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
15548 buf->appendf("Collapsed=%d\n", settings->Collapsed);
15549 if (settings->DockId != 0)
15550 {
15551 //buf->appendf("TabId=0x%08X\n", ImHashStr("#TAB", 4, settings->ID)); // window->TabId: this is not read back but writing it makes "debugging" the .ini data easier.
15552 if (settings->DockOrder == -1)
15553 buf->appendf("DockId=0x%08X\n", settings->DockId);
15554 else
15555 buf->appendf("DockId=0x%08X,%d\n", settings->DockId, settings->DockOrder);
15556 if (settings->ClassId != 0)
15557 buf->appendf("ClassId=0x%08X\n", settings->ClassId);
15558 }
15559 }
15560 buf->append("\n");
15561 }
15562}
15563
15564
15565//-----------------------------------------------------------------------------
15566// [SECTION] LOCALIZATION
15567//-----------------------------------------------------------------------------
15568
15569void ImGui::LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count)
15570{
15571 ImGuiContext& g = *GImGui;
15572 for (int n = 0; n < count; n++)
15573 g.LocalizationTable[entries[n].Key] = entries[n].Text;
15574}
15575
15576
15577//-----------------------------------------------------------------------------
15578// [SECTION] VIEWPORTS, PLATFORM WINDOWS
15579//-----------------------------------------------------------------------------
15580// - GetMainViewport()
15581// - FindViewportByID()
15582// - FindViewportByPlatformHandle()
15583// - SetCurrentViewport() [Internal]
15584// - SetWindowViewport() [Internal]
15585// - GetWindowAlwaysWantOwnViewport() [Internal]
15586// - UpdateTryMergeWindowIntoHostViewport() [Internal]
15587// - UpdateTryMergeWindowIntoHostViewports() [Internal]
15588// - TranslateWindowsInViewport() [Internal]
15589// - ScaleWindowsInViewport() [Internal]
15590// - FindHoveredViewportFromPlatformWindowStack() [Internal]
15591// - UpdateViewportsNewFrame() [Internal]
15592// - UpdateViewportsEndFrame() [Internal]
15593// - AddUpdateViewport() [Internal]
15594// - WindowSelectViewport() [Internal]
15595// - WindowSyncOwnedViewport() [Internal]
15596// - UpdatePlatformWindows()
15597// - RenderPlatformWindowsDefault()
15598// - FindPlatformMonitorForPos() [Internal]
15599// - FindPlatformMonitorForRect() [Internal]
15600// - UpdateViewportPlatformMonitor() [Internal]
15601// - DestroyPlatformWindow() [Internal]
15602// - DestroyPlatformWindows()
15603//-----------------------------------------------------------------------------
15604
15605ImGuiViewport* ImGui::GetMainViewport()
15606{
15607 ImGuiContext& g = *GImGui;
15608 return g.Viewports[0];
15609}
15610
15611// FIXME: This leaks access to viewports not listed in PlatformIO.Viewports[]. Problematic? (#4236)
15612ImGuiViewport* ImGui::FindViewportByID(ImGuiID id)
15613{
15614 ImGuiContext& g = *GImGui;
15615 for (ImGuiViewportP* viewport : g.Viewports)
15616 if (viewport->ID == id)
15617 return viewport;
15618 return NULL;
15619}
15620
15621ImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle)
15622{
15623 ImGuiContext& g = *GImGui;
15624 for (ImGuiViewportP* viewport : g.Viewports)
15625 if (viewport->PlatformHandle == platform_handle)
15626 return viewport;
15627 return NULL;
15628}
15629
15630void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* viewport)
15631{
15632 ImGuiContext& g = *GImGui;
15633 (void)current_window;
15634
15635 if (viewport)
15636 viewport->LastFrameActive = g.FrameCount;
15637 if (g.CurrentViewport == viewport)
15638 return;
15639 g.CurrentDpiScale = viewport ? viewport->DpiScale : 1.0f;
15640 g.CurrentViewport = viewport;
15641 //IMGUI_DEBUG_LOG_VIEWPORT("[viewport] SetCurrentViewport changed '%s' 0x%08X\n", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0);
15642
15643 // Notify platform layer of viewport changes
15644 // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI
15645 if (g.CurrentViewport && g.PlatformIO.Platform_OnChangedViewport)
15646 g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport);
15647}
15648
15649void ImGui::SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
15650{
15651 // Abandon viewport
15652 if (window->ViewportOwned && window->Viewport->Window == window)
15653 window->Viewport->Size = ImVec2(0.0f, 0.0f);
15654
15655 window->Viewport = viewport;
15656 window->ViewportId = viewport->ID;
15657 window->ViewportOwned = (viewport->Window == window);
15658}
15659
15660static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window)
15661{
15662 // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protrude and create their own.
15663 ImGuiContext& g = *GImGui;
15664 if (g.IO.ConfigViewportsNoAutoMerge || (window->WindowClass.ViewportFlagsOverrideSet & ImGuiViewportFlags_NoAutoMerge))
15665 if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
15666 if (!window->DockIsActive)
15667 if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0)
15668 if ((window->Flags & ImGuiWindowFlags_Popup) == 0 || (window->Flags & ImGuiWindowFlags_Modal) != 0)
15669 return true;
15670 return false;
15671}
15672
15673static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
15674{
15675 ImGuiContext& g = *GImGui;
15676 if (window->Viewport == viewport)
15677 return false;
15678 if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) == 0)
15679 return false;
15680 if ((viewport->Flags & ImGuiViewportFlags_IsMinimized) != 0)
15681 return false;
15682 if (!viewport->GetMainRect().Contains(window->Rect()))
15683 return false;
15684 if (GetWindowAlwaysWantOwnViewport(window))
15685 return false;
15686
15687 // FIXME: Can't use g.WindowsFocusOrder[] for root windows only as we care about Z order. If we maintained a DisplayOrder along with FocusOrder we could..
15688 for (ImGuiWindow* window_behind : g.Windows)
15689 {
15690 if (window_behind == window)
15691 break;
15692 if (window_behind->WasActive && window_behind->ViewportOwned && !(window_behind->Flags & ImGuiWindowFlags_ChildWindow))
15693 if (window_behind->Viewport->GetMainRect().Overlaps(window->Rect()))
15694 return false;
15695 }
15696
15697 // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child)
15698 ImGuiViewportP* old_viewport = window->Viewport;
15699 if (window->ViewportOwned)
15700 for (int n = 0; n < g.Windows.Size; n++)
15701 if (g.Windows[n]->Viewport == old_viewport)
15702 SetWindowViewport(g.Windows[n], viewport);
15703 SetWindowViewport(window, viewport);
15704 BringWindowToDisplayFront(window);
15705
15706 return true;
15707}
15708
15709// FIXME: handle 0 to N host viewports
15710static bool ImGui::UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window)
15711{
15712 ImGuiContext& g = *GImGui;
15713 return UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]);
15714}
15715
15716// Translate Dear ImGui windows when a Host Viewport has been moved
15717// (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!)
15718void ImGui::TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos, const ImVec2& old_size, const ImVec2& new_size)
15719{
15720 ImGuiContext& g = *GImGui;
15721 IM_ASSERT(viewport->Window == NULL && (viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows));
15722
15723 // 1) We test if ImGuiConfigFlags_ViewportsEnable was just toggled, which allows us to conveniently
15724 // translate imgui windows from OS-window-local to absolute coordinates or vice-versa.
15725 // 2) If it's not going to fit into the new size, keep it at same absolute position.
15726 // One problem with this is that most Win32 applications doesn't update their render while dragging,
15727 // and so the window will appear to teleport when releasing the mouse.
15728 const bool translate_all_windows = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable);
15729 ImRect test_still_fit_rect(old_pos, old_pos + viewport->Size);
15730 ImVec2 delta_pos = new_pos - old_pos;
15731 for (ImGuiWindow* window : g.Windows) // FIXME-OPT
15732 if (translate_all_windows || (window->Viewport == viewport && (old_size == new_size || test_still_fit_rect.Contains(window->Rect()))))
15733 TranslateWindow(window, delta_pos);
15734}
15735
15736// Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!)
15737void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale)
15738{
15739 ImGuiContext& g = *GImGui;
15740 if (viewport->Window)
15741 {
15742 ScaleWindow(viewport->Window, scale);
15743 }
15744 else
15745 {
15746 for (ImGuiWindow* window : g.Windows)
15747 if (window->Viewport == viewport)
15748 ScaleWindow(window, scale);
15749 }
15750}
15751
15752// If the backend doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves.
15753// A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window.
15754// B) It requires Platform_GetWindowFocus to be implemented by backend.
15755ImGuiViewportP* ImGui::FindHoveredViewportFromPlatformWindowStack(const ImVec2& mouse_platform_pos)
15756{
15757 ImGuiContext& g = *GImGui;
15758 ImGuiViewportP* best_candidate = NULL;
15759 for (ImGuiViewportP* viewport : g.Viewports)
15760 if (!(viewport->Flags & (ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_IsMinimized)) && viewport->GetMainRect().Contains(mouse_platform_pos))
15761 if (best_candidate == NULL || best_candidate->LastFocusedStampCount < viewport->LastFocusedStampCount)
15762 best_candidate = viewport;
15763 return best_candidate;
15764}
15765
15766// Update viewports and monitor infos
15767// Note that this is running even if 'ImGuiConfigFlags_ViewportsEnable' is not set, in order to clear unused viewports (if any) and update monitor info.
15768static void ImGui::UpdateViewportsNewFrame()
15769{
15770 ImGuiContext& g = *GImGui;
15771 IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size);
15772
15773 // Update Minimized status (we need it first in order to decide if we'll apply Pos/Size of the main viewport)
15774 // Update Focused status
15775 const bool viewports_enabled = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != 0;
15776 if (viewports_enabled)
15777 {
15778 ImGuiViewportP* focused_viewport = NULL;
15779 for (ImGuiViewportP* viewport : g.Viewports)
15780 {
15781 const bool platform_funcs_available = viewport->PlatformWindowCreated;
15782 if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available)
15783 {
15784 bool is_minimized = g.PlatformIO.Platform_GetWindowMinimized(viewport);
15785 if (is_minimized)
15786 viewport->Flags |= ImGuiViewportFlags_IsMinimized;
15787 else
15788 viewport->Flags &= ~ImGuiViewportFlags_IsMinimized;
15789 }
15790
15791 // Update our implicit z-order knowledge of platform windows, which is used when the backend cannot provide io.MouseHoveredViewport.
15792 // When setting Platform_GetWindowFocus, it is expected that the platform backend can handle calls without crashing if it doesn't have data stored.
15793 if (g.PlatformIO.Platform_GetWindowFocus && platform_funcs_available)
15794 {
15795 bool is_focused = g.PlatformIO.Platform_GetWindowFocus(viewport);
15796 if (is_focused)
15797 viewport->Flags |= ImGuiViewportFlags_IsFocused;
15798 else
15799 viewport->Flags &= ~ImGuiViewportFlags_IsFocused;
15800 if (is_focused)
15801 focused_viewport = viewport;
15802 }
15803 }
15804
15805 // Focused viewport has changed?
15806 if (focused_viewport && g.PlatformLastFocusedViewportId != focused_viewport->ID)
15807 {
15808 IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Focused viewport changed %08X -> %08X, attempting to apply our focus.\n", g.PlatformLastFocusedViewportId, focused_viewport->ID);
15809 const ImGuiViewport* prev_focused_viewport = FindViewportByID(g.PlatformLastFocusedViewportId);
15810 const bool prev_focused_has_been_destroyed = (prev_focused_viewport == NULL) || (prev_focused_viewport->PlatformWindowCreated == false);
15811
15812 // Store a tag so we can infer z-order easily from all our windows
15813 // We compare PlatformLastFocusedViewportId so newly created viewports with _NoFocusOnAppearing flag
15814 // will keep the front most stamp instead of losing it back to their parent viewport.
15815 if (focused_viewport->LastFocusedStampCount != g.ViewportFocusedStampCount)
15816 focused_viewport->LastFocusedStampCount = ++g.ViewportFocusedStampCount;
15817 g.PlatformLastFocusedViewportId = focused_viewport->ID;
15818
15819 // Focus associated dear imgui window
15820 // - if focus didn't happen with a click within imgui boundaries, e.g. Clicking platform title bar. (#6299)
15821 // - if focus didn't happen because we destroyed another window (#6462)
15822 // FIXME: perhaps 'FocusTopMostWindowUnderOne()' can handle the 'focused_window->Window != NULL' case as well.
15823 const bool apply_imgui_focus_on_focused_viewport = !IsAnyMouseDown() && !prev_focused_has_been_destroyed;
15824 if (apply_imgui_focus_on_focused_viewport)
15825 {
15826 focused_viewport->LastFocusedHadNavWindow |= (g.NavWindow != NULL) && (g.NavWindow->Viewport == focused_viewport); // Update so a window changing viewport won't lose focus.
15827 ImGuiFocusRequestFlags focus_request_flags = ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild;
15828 if (focused_viewport->Window != NULL)
15829 FocusWindow(focused_viewport->Window, focus_request_flags);
15830 else if (focused_viewport->LastFocusedHadNavWindow)
15831 FocusTopMostWindowUnderOne(NULL, NULL, focused_viewport, focus_request_flags); // Focus top most in viewport
15832 else
15833 FocusWindow(NULL, focus_request_flags); // No window had focus last time viewport was focused
15834 }
15835 }
15836 if (focused_viewport)
15837 focused_viewport->LastFocusedHadNavWindow = (g.NavWindow != NULL) && (g.NavWindow->Viewport == focused_viewport);
15838 }
15839
15840 // Create/update main viewport with current platform position.
15841 // FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent.
15842 ImGuiViewportP* main_viewport = g.Viewports[0];
15843 IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID);
15844 IM_ASSERT(main_viewport->Window == NULL);
15845 ImVec2 main_viewport_pos = viewports_enabled ? g.PlatformIO.Platform_GetWindowPos(main_viewport) : ImVec2(0.0f, 0.0f);
15846 ImVec2 main_viewport_size = g.IO.DisplaySize;
15847 if (viewports_enabled && (main_viewport->Flags & ImGuiViewportFlags_IsMinimized))
15848 {
15849 main_viewport_pos = main_viewport->Pos; // Preserve last pos/size when minimized (FIXME: We don't do the same for Size outside of the viewport path)
15850 main_viewport_size = main_viewport->Size;
15851 }
15852 AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_pos, main_viewport_size, ImGuiViewportFlags_OwnedByApp | ImGuiViewportFlags_CanHostOtherWindows);
15853
15854 g.CurrentDpiScale = 0.0f;
15855 g.CurrentViewport = NULL;
15856 g.MouseViewport = NULL;
15857 for (int n = 0; n < g.Viewports.Size; n++)
15858 {
15859 ImGuiViewportP* viewport = g.Viewports[n];
15860 viewport->Idx = n;
15861
15862 // Erase unused viewports
15863 if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2)
15864 {
15865 DestroyViewport(viewport);
15866 n--;
15867 continue;
15868 }
15869
15870 const bool platform_funcs_available = viewport->PlatformWindowCreated;
15871 if (viewports_enabled)
15872 {
15873 // Update Position and Size (from Platform Window to ImGui) if requested.
15874 // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities.
15875 if (!(viewport->Flags & ImGuiViewportFlags_IsMinimized) && platform_funcs_available)
15876 {
15877 // Viewport->WorkPos and WorkSize will be updated below
15878 if (viewport->PlatformRequestMove)
15879 viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport);
15880 if (viewport->PlatformRequestResize)
15881 viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport);
15882 }
15883 }
15884
15885 // Update/copy monitor info
15886 UpdateViewportPlatformMonitor(viewport);
15887
15888 // Lock down space taken by menu bars and status bars + query initial insets from backend
15889 // Setup initial value for functions like BeginMainMenuBar(), DockSpaceOverViewport() etc.
15890 viewport->WorkInsetMin = viewport->BuildWorkInsetMin;
15891 viewport->WorkInsetMax = viewport->BuildWorkInsetMax;
15892 viewport->BuildWorkInsetMin = viewport->BuildWorkInsetMax = ImVec2(0.0f, 0.0f);
15893 if (g.PlatformIO.Platform_GetWindowWorkAreaInsets != NULL && platform_funcs_available)
15894 {
15895 ImVec4 insets = g.PlatformIO.Platform_GetWindowWorkAreaInsets(viewport);
15896 IM_ASSERT(insets.x >= 0.0f && insets.y >= 0.0f && insets.z >= 0.0f && insets.w >= 0.0f);
15897 viewport->BuildWorkInsetMin = ImVec2(insets.x, insets.y);
15898 viewport->BuildWorkInsetMax = ImVec2(insets.z, insets.w);
15899 }
15900 viewport->UpdateWorkRect();
15901
15902 // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back.
15903 viewport->Alpha = 1.0f;
15904
15905 // Translate Dear ImGui windows when a Host Viewport has been moved
15906 // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!)
15907 const ImVec2 viewport_delta_pos = viewport->Pos - viewport->LastPos;
15908 if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta_pos.x != 0.0f || viewport_delta_pos.y != 0.0f))
15909 TranslateWindowsInViewport(viewport, viewport->LastPos, viewport->Pos, viewport->LastSize, viewport->Size);
15910
15911 // Update DPI scale
15912 float new_dpi_scale;
15913 if (g.PlatformIO.Platform_GetWindowDpiScale && platform_funcs_available)
15914 new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport);
15915 else if (viewport->PlatformMonitor != -1)
15916 new_dpi_scale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale;
15917 else
15918 new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f;
15919 if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale)
15920 {
15921 float scale_factor = new_dpi_scale / viewport->DpiScale;
15922 if (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports)
15923 ScaleWindowsInViewport(viewport, scale_factor);
15924 //if (viewport == GetMainViewport())
15925 // g.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor);
15926
15927 // Scale our window moving pivot so that the window will rescale roughly around the mouse position.
15928 // FIXME-VIEWPORT: This currently creates a resizing feedback loop when a window is straddling a DPI transition border.
15929 // (Minor: since our sizes do not perfectly linearly scale, deferring the click offset scale until we know the actual window scale ratio may get us slightly more precise mouse positioning.)
15930 //if (g.MovingWindow != NULL && g.MovingWindow->Viewport == viewport)
15931 // g.ActiveIdClickOffset = ImTrunc(g.ActiveIdClickOffset * scale_factor);
15932 }
15933 viewport->DpiScale = new_dpi_scale;
15934 }
15935
15936 // Update fallback monitor
15937 g.PlatformMonitorsFullWorkRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
15938 if (g.PlatformIO.Monitors.Size == 0)
15939 {
15940 ImGuiPlatformMonitor* monitor = &g.FallbackMonitor;
15941 monitor->MainPos = main_viewport->Pos;
15942 monitor->MainSize = main_viewport->Size;
15943 monitor->WorkPos = main_viewport->WorkPos;
15944 monitor->WorkSize = main_viewport->WorkSize;
15945 monitor->DpiScale = main_viewport->DpiScale;
15946 g.PlatformMonitorsFullWorkRect.Add(monitor->WorkPos);
15947 g.PlatformMonitorsFullWorkRect.Add(monitor->WorkPos + monitor->WorkSize);
15948 }
15949 else
15950 {
15951 g.FallbackMonitor = g.PlatformIO.Monitors[0];
15952 }
15953 for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors)
15954 {
15955 g.PlatformMonitorsFullWorkRect.Add(monitor.WorkPos);
15956 g.PlatformMonitorsFullWorkRect.Add(monitor.WorkPos + monitor.WorkSize);
15957 }
15958
15959 if (!viewports_enabled)
15960 {
15961 g.MouseViewport = main_viewport;
15962 return;
15963 }
15964
15965 // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport.
15966 // Note that 'viewport_hovered' should skip over any viewport that has the ImGuiViewportFlags_NoInputs flags set.
15967 ImGuiViewportP* viewport_hovered = NULL;
15968 if (g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)
15969 {
15970 viewport_hovered = g.IO.MouseHoveredViewport ? (ImGuiViewportP*)FindViewportByID(g.IO.MouseHoveredViewport) : NULL;
15971 if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs))
15972 viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); // Backend failed to handle _NoInputs viewport: revert to our fallback.
15973 }
15974 else
15975 {
15976 // If the backend doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search:
15977 // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window.
15978 // B) won't take account of how the backend apply parent<>child relationship to secondary viewports, which affects their Z order.
15979 // C) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO)
15980 viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos);
15981 }
15982 if (viewport_hovered != NULL)
15983 g.MouseLastHoveredViewport = viewport_hovered;
15984 else if (g.MouseLastHoveredViewport == NULL)
15985 g.MouseLastHoveredViewport = g.Viewports[0];
15986
15987 // Update mouse reference viewport
15988 // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode)
15989 // (MovingViewport->Viewport will be NULL in the rare situation where the window disappared while moving, set UpdateMouseMovingWindowNewFrame() for details)
15990 if (g.MovingWindow && g.MovingWindow->Viewport)
15991 g.MouseViewport = g.MovingWindow->Viewport;
15992 else
15993 g.MouseViewport = g.MouseLastHoveredViewport;
15994
15995 // When dragging something, always refer to the last hovered viewport.
15996 // - when releasing a moving window we will revert to aiming behind (at viewport_hovered)
15997 // - when we are between viewports, our dragged preview will tend to show in the last viewport _even_ if we don't have tooltips in their viewports (when lacking monitor info)
15998 // - consider the case of holding on a menu item to browse child menus: even thou a mouse button is held, there's no active id because menu items only react on mouse release.
15999 // FIXME-VIEWPORT: This is essentially broken, when ImGuiBackendFlags_HasMouseHoveredViewport is set we want to trust when viewport_hovered==NULL and use that.
16000 const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive;
16001 if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL)
16002 viewport_hovered = g.MouseLastHoveredViewport;
16003 if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !IsAnyMouseDown())
16004 if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs))
16005 g.MouseViewport = viewport_hovered;
16006
16007 IM_ASSERT(g.MouseViewport != NULL);
16008}
16009
16010// Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)
16011static void ImGui::UpdateViewportsEndFrame()
16012{
16013 ImGuiContext& g = *GImGui;
16014 g.PlatformIO.Viewports.resize(0);
16015 for (int i = 0; i < g.Viewports.Size; i++)
16016 {
16017 ImGuiViewportP* viewport = g.Viewports[i];
16018 viewport->LastPos = viewport->Pos;
16019 viewport->LastSize = viewport->Size;
16020 if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f)
16021 if (i > 0) // Always include main viewport in the list
16022 continue;
16023 if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window))
16024 continue;
16025 if (i > 0)
16026 IM_ASSERT(viewport->Window != NULL);
16027 g.PlatformIO.Viewports.push_back(viewport);
16028 }
16029 g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called
16030}
16031
16032// FIXME: We should ideally refactor the system to call this every frame (we currently don't)
16033ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags)
16034{
16035 ImGuiContext& g = *GImGui;
16036 IM_ASSERT(id != 0);
16037
16038 flags |= ImGuiViewportFlags_IsPlatformWindow;
16039 if (window != NULL)
16040 {
16041 if (g.MovingWindow && g.MovingWindow->RootWindowDockTree == window)
16042 flags |= ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_NoFocusOnAppearing;
16043 if ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs))
16044 flags |= ImGuiViewportFlags_NoInputs;
16045 if (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing)
16046 flags |= ImGuiViewportFlags_NoFocusOnAppearing;
16047 }
16048
16049 ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id);
16050 if (viewport)
16051 {
16052 // Always update for main viewport as we are already pulling correct platform pos/size (see #4900)
16053 if (!viewport->PlatformRequestMove || viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID)
16054 viewport->Pos = pos;
16055 if (!viewport->PlatformRequestResize || viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID)
16056 viewport->Size = size;
16057 viewport->Flags = flags | (viewport->Flags & (ImGuiViewportFlags_IsMinimized | ImGuiViewportFlags_IsFocused)); // Preserve existing flags
16058 }
16059 else
16060 {
16061 // New viewport
16062 viewport = IM_NEW(ImGuiViewportP)();
16063 viewport->ID = id;
16064 viewport->Idx = g.Viewports.Size;
16065 viewport->Pos = viewport->LastPos = pos;
16066 viewport->Size = viewport->LastSize = size;
16067 viewport->Flags = flags;
16068 UpdateViewportPlatformMonitor(viewport);
16069 g.Viewports.push_back(viewport);
16070 g.ViewportCreatedCount++;
16071 IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Add Viewport %08X '%s'\n", id, window ? window->Name : "<NULL>");
16072
16073 // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport.
16074 // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame
16075 g.DrawListSharedData.ClipRectFullscreen.x = ImMin(g.DrawListSharedData.ClipRectFullscreen.x, viewport->Pos.x);
16076 g.DrawListSharedData.ClipRectFullscreen.y = ImMin(g.DrawListSharedData.ClipRectFullscreen.y, viewport->Pos.y);
16077 g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x);
16078 g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y);
16079
16080 // Store initial DpiScale before the OS platform window creation, based on expected monitor data.
16081 // This is so we can select an appropriate font size on the first frame of our window lifetime
16082 if (viewport->PlatformMonitor != -1)
16083 viewport->DpiScale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale;
16084 }
16085
16086 viewport->Window = window;
16087 viewport->LastFrameActive = g.FrameCount;
16088 viewport->UpdateWorkRect();
16089 IM_ASSERT(window == NULL || viewport->ID == window->ID);
16090
16091 if (window != NULL)
16092 window->ViewportOwned = true;
16093
16094 return viewport;
16095}
16096
16097static void ImGui::DestroyViewport(ImGuiViewportP* viewport)
16098{
16099 // Clear references to this viewport in windows (window->ViewportId becomes the master data)
16100 ImGuiContext& g = *GImGui;
16101 for (ImGuiWindow* window : g.Windows)
16102 {
16103 if (window->Viewport != viewport)
16104 continue;
16105 window->Viewport = NULL;
16106 window->ViewportOwned = false;
16107 }
16108 if (viewport == g.MouseLastHoveredViewport)
16109 g.MouseLastHoveredViewport = NULL;
16110
16111 // Destroy
16112 IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Delete Viewport %08X '%s'\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a");
16113 DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here.
16114 IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false);
16115 IM_ASSERT(g.Viewports[viewport->Idx] == viewport);
16116 g.Viewports.erase(g.Viewports.Data + viewport->Idx);
16117 IM_DELETE(viewport);
16118}
16119
16120// FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten.
16121static void ImGui::WindowSelectViewport(ImGuiWindow* window)
16122{
16123 ImGuiContext& g = *GImGui;
16124 ImGuiWindowFlags flags = window->Flags;
16125 window->ViewportAllowPlatformMonitorExtend = -1;
16126
16127 // Restore main viewport if multi-viewport is not supported by the backend
16128 ImGuiViewportP* main_viewport = (ImGuiViewportP*)(void*)GetMainViewport();
16129 if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable))
16130 {
16131 SetWindowViewport(window, main_viewport);
16132 return;
16133 }
16134 window->ViewportOwned = false;
16135
16136 // Appearing popups reset their viewport so they can inherit again
16137 if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && window->Appearing)
16138 {
16139 window->Viewport = NULL;
16140 window->ViewportId = 0;
16141 }
16142
16143 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport) == 0)
16144 {
16145 // By default inherit from parent window
16146 if (window->Viewport == NULL && window->ParentWindow && (!window->ParentWindow->IsFallbackWindow || window->ParentWindow->WasActive))
16147 window->Viewport = window->ParentWindow->Viewport;
16148
16149 // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file
16150 if (window->Viewport == NULL && window->ViewportId != 0)
16151 {
16152 window->Viewport = (ImGuiViewportP*)FindViewportByID(window->ViewportId);
16153 if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX)
16154 window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_None);
16155 }
16156 }
16157
16158 bool lock_viewport = false;
16159 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport)
16160 {
16161 // Code explicitly request a viewport
16162 window->Viewport = (ImGuiViewportP*)FindViewportByID(g.NextWindowData.ViewportId);
16163 window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet.
16164 if (window->Viewport && (window->Flags & ImGuiWindowFlags_DockNodeHost) != 0 && window->Viewport->Window != NULL)
16165 {
16166 window->Viewport->Window = window;
16167 window->Viewport->ID = window->ViewportId = window->ID; // Overwrite ID (always owned by node)
16168 }
16169 lock_viewport = true;
16170 }
16171 else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu))
16172 {
16173 // Always inherit viewport from parent window
16174 if (window->DockNode && window->DockNode->HostWindow)
16175 IM_ASSERT(window->DockNode->HostWindow->Viewport == window->ParentWindow->Viewport);
16176 window->Viewport = window->ParentWindow->Viewport;
16177 }
16178 else if (window->DockNode && window->DockNode->HostWindow)
16179 {
16180 // This covers the "always inherit viewport from parent window" case for when a window reattach to a node that was just created mid-frame
16181 window->Viewport = window->DockNode->HostWindow->Viewport;
16182 }
16183 else if (flags & ImGuiWindowFlags_Tooltip)
16184 {
16185 window->Viewport = g.MouseViewport;
16186 }
16187 else if (GetWindowAlwaysWantOwnViewport(window))
16188 {
16189 window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
16190 }
16191 else if (g.MovingWindow && g.MovingWindow->RootWindowDockTree == window && IsMousePosValid())
16192 {
16193 if (window->Viewport != NULL && window->Viewport->Window == window)
16194 window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
16195 }
16196 else
16197 {
16198 // Merge into host viewport?
16199 // We cannot test window->ViewportOwned as it set lower in the function.
16200 // Testing (g.ActiveId == 0 || g.ActiveIdAllowOverlap) to avoid merging during a short-term widget interaction. Main intent was to avoid during resize (see #4212)
16201 bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && (g.ActiveId == 0 || g.ActiveIdAllowOverlap));
16202 if (try_to_merge_into_host_viewport)
16203 UpdateTryMergeWindowIntoHostViewports(window);
16204 }
16205
16206 // Fallback: merge in default viewport if z-order matches, otherwise create a new viewport
16207 if (window->Viewport == NULL)
16208 if (!UpdateTryMergeWindowIntoHostViewport(window, main_viewport))
16209 window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
16210
16211 // Mark window as allowed to protrude outside of its viewport and into the current monitor
16212 if (!lock_viewport)
16213 {
16214 if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
16215 {
16216 // We need to take account of the possibility that mouse may become invalid.
16217 // Popups/Tooltip always set ViewportAllowPlatformMonitorExtend so GetWindowAllowedExtentRect() will return full monitor bounds.
16218 ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.BeginPopupStack.back().OpenMousePos;
16219 bool use_mouse_ref = (!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !g.NavWindow);
16220 bool mouse_valid = IsMousePosValid(&mouse_ref);
16221 if ((window->Appearing || (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_ChildMenu))) && (!use_mouse_ref || mouse_valid))
16222 window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos());
16223 else
16224 window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor;
16225 }
16226 else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow) && window->DockNode == NULL)
16227 {
16228 // When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code.
16229 const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true;
16230 if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible)
16231 {
16232 // Steal/transfer ownership
16233 IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Window '%s' steal Viewport %08X from Window '%s'\n", window->Name, window->Viewport->ID, window->Viewport->Window->Name);
16234 window->Viewport->Window = window;
16235 window->Viewport->ID = window->ID;
16236 window->Viewport->LastNameHash = 0;
16237 }
16238 else if (!UpdateTryMergeWindowIntoHostViewports(window)) // Merge?
16239 {
16240 // New viewport
16241 window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing);
16242 }
16243 }
16244 else if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0)
16245 {
16246 // Regular (non-child, non-popup) windows by default are also allowed to protrude
16247 // Child windows are kept contained within their parent.
16248 window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor;
16249 }
16250 }
16251
16252 // Update flags
16253 window->ViewportOwned = (window == window->Viewport->Window);
16254 window->ViewportId = window->Viewport->ID;
16255
16256 // If the OS window has a title bar, hide our imgui title bar
16257 //if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration))
16258 // window->Flags |= ImGuiWindowFlags_NoTitleBar;
16259}
16260
16261void ImGui::WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_window_in_stack)
16262{
16263 ImGuiContext& g = *GImGui;
16264
16265 bool viewport_rect_changed = false;
16266
16267 // Synchronize window --> viewport in most situations
16268 // Synchronize viewport -> window in case the platform window has been moved or resized from the OS/WM
16269 if (window->Viewport->PlatformRequestMove)
16270 {
16271 window->Pos = window->Viewport->Pos;
16272 MarkIniSettingsDirty(window);
16273 }
16274 else if (memcmp(&window->Viewport->Pos, &window->Pos, sizeof(window->Pos)) != 0)
16275 {
16276 viewport_rect_changed = true;
16277 window->Viewport->Pos = window->Pos;
16278 }
16279
16280 if (window->Viewport->PlatformRequestResize)
16281 {
16282 window->Size = window->SizeFull = window->Viewport->Size;
16283 MarkIniSettingsDirty(window);
16284 }
16285 else if (memcmp(&window->Viewport->Size, &window->Size, sizeof(window->Size)) != 0)
16286 {
16287 viewport_rect_changed = true;
16288 window->Viewport->Size = window->Size;
16289 }
16290 window->Viewport->UpdateWorkRect();
16291
16292 // The viewport may have changed monitor since the global update in UpdateViewportsNewFrame()
16293 // Either a SetNextWindowPos() call in the current frame or a SetWindowPos() call in the previous frame may have this effect.
16294 if (viewport_rect_changed)
16295 UpdateViewportPlatformMonitor(window->Viewport);
16296
16297 // Update common viewport flags
16298 const ImGuiViewportFlags viewport_flags_to_clear = ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoRendererClear;
16299 ImGuiViewportFlags viewport_flags = window->Viewport->Flags & ~viewport_flags_to_clear;
16300 ImGuiWindowFlags window_flags = window->Flags;
16301 const bool is_modal = (window_flags & ImGuiWindowFlags_Modal) != 0;
16302 const bool is_short_lived_floating_window = (window_flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0;
16303 if (window_flags & ImGuiWindowFlags_Tooltip)
16304 viewport_flags |= ImGuiViewportFlags_TopMost;
16305 if ((g.IO.ConfigViewportsNoTaskBarIcon || is_short_lived_floating_window) && !is_modal)
16306 viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon;
16307 if (g.IO.ConfigViewportsNoDecoration || is_short_lived_floating_window)
16308 viewport_flags |= ImGuiViewportFlags_NoDecoration;
16309
16310 // Not correct to set modal as topmost because:
16311 // - Because other popups can be stacked above a modal (e.g. combo box in a modal)
16312 // - ImGuiViewportFlags_TopMost is currently handled different in backends: in Win32 it is "appear top most" whereas in GLFW and SDL it is "stay topmost"
16313 //if (flags & ImGuiWindowFlags_Modal)
16314 // viewport_flags |= ImGuiViewportFlags_TopMost;
16315
16316 // For popups and menus that may be protruding out of their parent viewport, we enable _NoFocusOnClick so that clicking on them
16317 // won't steal the OS focus away from their parent window (which may be reflected in OS the title bar decoration).
16318 // Setting _NoFocusOnClick would technically prevent us from bringing back to front in case they are being covered by an OS window from a different app,
16319 // but it shouldn't be much of a problem considering those are already popups that are closed when clicking elsewhere.
16320 if (is_short_lived_floating_window && !is_modal)
16321 viewport_flags |= ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoFocusOnClick;
16322
16323 // We can overwrite viewport flags using ImGuiWindowClass (advanced users)
16324 if (window->WindowClass.ViewportFlagsOverrideSet)
16325 viewport_flags |= window->WindowClass.ViewportFlagsOverrideSet;
16326 if (window->WindowClass.ViewportFlagsOverrideClear)
16327 viewport_flags &= ~window->WindowClass.ViewportFlagsOverrideClear;
16328
16329 // We can also tell the backend that clearing the platform window won't be necessary,
16330 // as our window background is filling the viewport and we have disabled BgAlpha.
16331 // FIXME: Work on support for per-viewport transparency (#2766)
16332 if (!(window_flags & ImGuiWindowFlags_NoBackground))
16333 viewport_flags |= ImGuiViewportFlags_NoRendererClear;
16334
16335 window->Viewport->Flags = viewport_flags;
16336
16337 // Update parent viewport ID
16338 // (the !IsFallbackWindow test mimic the one done in WindowSelectViewport())
16339 if (window->WindowClass.ParentViewportId != (ImGuiID)-1)
16340 window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId;
16341 else if ((window_flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && parent_window_in_stack && (!parent_window_in_stack->IsFallbackWindow || parent_window_in_stack->WasActive))
16342 window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID;
16343 else
16344 window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID;
16345}
16346
16347// Called by user at the end of the main loop, after EndFrame()
16348// This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO api.
16349void ImGui::UpdatePlatformWindows()
16350{
16351 ImGuiContext& g = *GImGui;
16352 IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?");
16353 IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount);
16354 g.FrameCountPlatformEnded = g.FrameCount;
16355 if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable))
16356 return;
16357
16358 // Create/resize/destroy platform windows to match each active viewport.
16359 // Skip the main viewport (index 0), which is always fully handled by the application!
16360 for (int i = 1; i < g.Viewports.Size; i++)
16361 {
16362 ImGuiViewportP* viewport = g.Viewports[i];
16363
16364 // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window
16365 // (the implicit/fallback Debug##Default window will be registering its viewport then be disabled, causing a dummy DestroyPlatformWindow to be made each frame)
16366 bool destroy_platform_window = false;
16367 destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1);
16368 destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window));
16369 if (destroy_platform_window)
16370 {
16371 DestroyPlatformWindow(viewport);
16372 continue;
16373 }
16374
16375 // New windows that appears directly in a new viewport won't always have a size on their first frame
16376 if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0 || viewport->Size.y <= 0)
16377 continue;
16378
16379 // Create window
16380 const bool is_new_platform_window = (viewport->PlatformWindowCreated == false);
16381 if (is_new_platform_window)
16382 {
16383 IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Create Platform Window %08X '%s'\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a");
16384 g.PlatformIO.Platform_CreateWindow(viewport);
16385 if (g.PlatformIO.Renderer_CreateWindow != NULL)
16386 g.PlatformIO.Renderer_CreateWindow(viewport);
16387 g.PlatformWindowsCreatedCount++;
16388 viewport->LastNameHash = 0;
16389 viewport->LastPlatformPos = viewport->LastPlatformSize = ImVec2(FLT_MAX, FLT_MAX); // By clearing those we'll enforce a call to Platform_SetWindowPos/Size below, before Platform_ShowWindow (FIXME: Is that necessary?)
16390 viewport->LastRendererSize = viewport->Size; // We don't need to call Renderer_SetWindowSize() as it is expected Renderer_CreateWindow() already did it.
16391 viewport->PlatformWindowCreated = true;
16392 }
16393
16394 // Apply Position and Size (from ImGui to Platform/Renderer backends)
16395 if ((viewport->LastPlatformPos.x != viewport->Pos.x || viewport->LastPlatformPos.y != viewport->Pos.y) && !viewport->PlatformRequestMove)
16396 g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos);
16397 if ((viewport->LastPlatformSize.x != viewport->Size.x || viewport->LastPlatformSize.y != viewport->Size.y) && !viewport->PlatformRequestResize)
16398 g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size);
16399 if ((viewport->LastRendererSize.x != viewport->Size.x || viewport->LastRendererSize.y != viewport->Size.y) && g.PlatformIO.Renderer_SetWindowSize)
16400 g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size);
16401 viewport->LastPlatformPos = viewport->Pos;
16402 viewport->LastPlatformSize = viewport->LastRendererSize = viewport->Size;
16403
16404 // Update title bar (if it changed)
16405 if (ImGuiWindow* window_for_title = GetWindowForTitleDisplay(viewport->Window))
16406 {
16407 const char* title_begin = window_for_title->Name;
16408 char* title_end = (char*)(intptr_t)FindRenderedTextEnd(title_begin);
16409 const ImGuiID title_hash = ImHashStr(title_begin, title_end - title_begin);
16410 if (viewport->LastNameHash != title_hash)
16411 {
16412 char title_end_backup_c = *title_end;
16413 *title_end = 0; // Cut existing buffer short instead of doing an alloc/free, no small gain.
16414 g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin);
16415 *title_end = title_end_backup_c;
16416 viewport->LastNameHash = title_hash;
16417 }
16418 }
16419
16420 // Update alpha (if it changed)
16421 if (viewport->LastAlpha != viewport->Alpha && g.PlatformIO.Platform_SetWindowAlpha)
16422 g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha);
16423 viewport->LastAlpha = viewport->Alpha;
16424
16425 // Optional, general purpose call to allow the backend to perform general book-keeping even if things haven't changed.
16426 if (g.PlatformIO.Platform_UpdateWindow)
16427 g.PlatformIO.Platform_UpdateWindow(viewport);
16428
16429 if (is_new_platform_window)
16430 {
16431 // On startup ensure new platform window don't steal focus (give it a few frames, as nested contents may lead to viewport being created a few frames late)
16432 if (g.FrameCount < 3)
16433 viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing;
16434
16435 // Show window
16436 g.PlatformIO.Platform_ShowWindow(viewport);
16437
16438 // Even without focus, we assume the window becomes front-most.
16439 // This is useful for our platform z-order heuristic when io.MouseHoveredViewport is not available.
16440 if (viewport->LastFocusedStampCount != g.ViewportFocusedStampCount)
16441 viewport->LastFocusedStampCount = ++g.ViewportFocusedStampCount;
16442 }
16443
16444 // Clear request flags
16445 viewport->ClearRequestFlags();
16446 }
16447}
16448
16449// This is a default/basic function for performing the rendering/swap of multiple Platform Windows.
16450// Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves.
16451// The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself:
16452//
16453// ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
16454// for (int i = 1; i < platform_io.Viewports.Size; i++)
16455// if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0)
16456// MyRenderFunction(platform_io.Viewports[i], my_args);
16457// for (int i = 1; i < platform_io.Viewports.Size; i++)
16458// if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0)
16459// MySwapBufferFunction(platform_io.Viewports[i], my_args);
16460//
16461void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg)
16462{
16463 // Skip the main viewport (index 0), which is always fully handled by the application!
16464 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
16465 for (int i = 1; i < platform_io.Viewports.Size; i++)
16466 {
16467 ImGuiViewport* viewport = platform_io.Viewports[i];
16468 if (viewport->Flags & ImGuiViewportFlags_IsMinimized)
16469 continue;
16470 if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg);
16471 if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg);
16472 }
16473 for (int i = 1; i < platform_io.Viewports.Size; i++)
16474 {
16475 ImGuiViewport* viewport = platform_io.Viewports[i];
16476 if (viewport->Flags & ImGuiViewportFlags_IsMinimized)
16477 continue;
16478 if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg);
16479 if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg);
16480 }
16481}
16482
16483static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos)
16484{
16485 ImGuiContext& g = *GImGui;
16486 for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++)
16487 {
16488 const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n];
16489 if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(pos))
16490 return monitor_n;
16491 }
16492 return -1;
16493}
16494
16495// Search for the monitor with the largest intersection area with the given rectangle
16496// We generally try to avoid searching loops but the monitor count should be very small here
16497// FIXME-OPT: We could test the last monitor used for that viewport first, and early
16498static int ImGui::FindPlatformMonitorForRect(const ImRect& rect)
16499{
16500 ImGuiContext& g = *GImGui;
16501
16502 const int monitor_count = g.PlatformIO.Monitors.Size;
16503 if (monitor_count <= 1)
16504 return monitor_count - 1;
16505
16506 // Use a minimum threshold of 1.0f so a zero-sized rect won't false positive, and will still find the correct monitor given its position.
16507 // This is necessary for tooltips which always resize down to zero at first.
16508 const float surface_threshold = ImMax(rect.GetWidth() * rect.GetHeight() * 0.5f, 1.0f);
16509 int best_monitor_n = -1;
16510 float best_monitor_surface = 0.001f;
16511
16512 for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++)
16513 {
16514 const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n];
16515 const ImRect monitor_rect = ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize);
16516 if (monitor_rect.Contains(rect))
16517 return monitor_n;
16518 ImRect overlapping_rect = rect;
16519 overlapping_rect.ClipWithFull(monitor_rect);
16520 float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight();
16521 if (overlapping_surface < best_monitor_surface)
16522 continue;
16523 best_monitor_surface = overlapping_surface;
16524 best_monitor_n = monitor_n;
16525 }
16526 return best_monitor_n;
16527}
16528
16529// Update monitor from viewport rectangle (we'll use this info to clamp windows and save windows lost in a removed monitor)
16530static void ImGui::UpdateViewportPlatformMonitor(ImGuiViewportP* viewport)
16531{
16532 viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetMainRect());
16533}
16534
16535// Return value is always != NULL, but don't hold on it across frames.
16536const ImGuiPlatformMonitor* ImGui::GetViewportPlatformMonitor(ImGuiViewport* viewport_p)
16537{
16538 ImGuiContext& g = *GImGui;
16539 ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)viewport_p;
16540 int monitor_idx = viewport->PlatformMonitor;
16541 if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size)
16542 return &g.PlatformIO.Monitors[monitor_idx];
16543 return &g.FallbackMonitor;
16544}
16545
16546void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport)
16547{
16548 ImGuiContext& g = *GImGui;
16549 if (viewport->PlatformWindowCreated)
16550 {
16551 IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Destroy Platform Window %08X '%s'\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a");
16552 if (g.PlatformIO.Renderer_DestroyWindow)
16553 g.PlatformIO.Renderer_DestroyWindow(viewport);
16554 if (g.PlatformIO.Platform_DestroyWindow)
16555 g.PlatformIO.Platform_DestroyWindow(viewport);
16556 IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL);
16557
16558 // Don't clear PlatformWindowCreated for the main viewport, as we initially set that up to true in Initialize()
16559 // The righter way may be to leave it to the backend to set this flag all-together, and made the flag public.
16560 if (viewport->ID != IMGUI_VIEWPORT_DEFAULT_ID)
16561 viewport->PlatformWindowCreated = false;
16562 }
16563 else
16564 {
16565 IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL);
16566 }
16567 viewport->RendererUserData = viewport->PlatformUserData = viewport->PlatformHandle = NULL;
16568 viewport->ClearRequestFlags();
16569}
16570
16571void ImGui::DestroyPlatformWindows()
16572{
16573 // We call the destroy window on every viewport (including the main viewport, index 0) to give a chance to the backend
16574 // to clear any data they may have stored in e.g. PlatformUserData, RendererUserData.
16575 // It is convenient for the platform backend code to store something in the main viewport, in order for e.g. the mouse handling
16576 // code to operator a consistent manner.
16577 // It is expected that the backend can handle calls to Renderer_DestroyWindow/Platform_DestroyWindow without
16578 // crashing if it doesn't have data stored.
16579 ImGuiContext& g = *GImGui;
16580 for (ImGuiViewportP* viewport : g.Viewports)
16581 DestroyPlatformWindow(viewport);
16582}
16583
16584
16585//-----------------------------------------------------------------------------
16586// [SECTION] DOCKING
16587//-----------------------------------------------------------------------------
16588// Docking: Internal Types
16589// Docking: Forward Declarations
16590// Docking: ImGuiDockContext
16591// Docking: ImGuiDockContext Docking/Undocking functions
16592// Docking: ImGuiDockNode
16593// Docking: ImGuiDockNode Tree manipulation functions
16594// Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport)
16595// Docking: Builder Functions
16596// Docking: Begin/End Support Functions (called from Begin/End)
16597// Docking: Settings
16598//-----------------------------------------------------------------------------
16599
16600//-----------------------------------------------------------------------------
16601// Typical Docking call flow: (root level is generally public API):
16602//-----------------------------------------------------------------------------
16603// - NewFrame() new dear imgui frame
16604// | DockContextNewFrameUpdateUndocking() - process queued undocking requests
16605// | - DockContextProcessUndockWindow() - process one window undocking request
16606// | - DockContextProcessUndockNode() - process one whole node undocking request
16607// | DockContextNewFrameUpdateUndocking() - process queue docking requests, create floating dock nodes
16608// | - update g.HoveredDockNode - [debug] update node hovered by mouse
16609// | - DockContextProcessDock() - process one docking request
16610// | - DockNodeUpdate()
16611// | - DockNodeUpdateForRootNode()
16612// | - DockNodeUpdateFlagsAndCollapse()
16613// | - DockNodeFindInfo()
16614// | - destroy unused node or tab bar
16615// | - create dock node host window
16616// | - Begin() etc.
16617// | - DockNodeStartMouseMovingWindow()
16618// | - DockNodeTreeUpdatePosSize()
16619// | - DockNodeTreeUpdateSplitter()
16620// | - draw node background
16621// | - DockNodeUpdateTabBar() - create/update tab bar for a docking node
16622// | - DockNodeAddTabBar()
16623// | - DockNodeWindowMenuUpdate()
16624// | - DockNodeCalcTabBarLayout()
16625// | - BeginTabBarEx()
16626// | - TabItemEx() calls
16627// | - EndTabBar()
16628// | - BeginDockableDragDropTarget()
16629// | - DockNodeUpdate() - recurse into child nodes...
16630//-----------------------------------------------------------------------------
16631// - DockSpace() user submit a dockspace into a window
16632// | Begin(Child) - create a child window
16633// | DockNodeUpdate() - call main dock node update function
16634// | End(Child)
16635// | ItemSize()
16636//-----------------------------------------------------------------------------
16637// - Begin()
16638// | BeginDocked()
16639// | BeginDockableDragDropSource()
16640// | BeginDockableDragDropTarget()
16641// | - DockNodePreviewDockRender()
16642//-----------------------------------------------------------------------------
16643// - EndFrame()
16644// | DockContextEndFrame()
16645//-----------------------------------------------------------------------------
16646
16647//-----------------------------------------------------------------------------
16648// Docking: Internal Types
16649//-----------------------------------------------------------------------------
16650// - ImGuiDockRequestType
16651// - ImGuiDockRequest
16652// - ImGuiDockPreviewData
16653// - ImGuiDockNodeSettings
16654// - ImGuiDockContext
16655//-----------------------------------------------------------------------------
16656
16664
16666{
16668 ImGuiWindow* DockTargetWindow; // Destination/Target Window to dock into (may be a loose window or a DockNode, might be NULL in which case DockTargetNode cannot be NULL)
16669 ImGuiDockNode* DockTargetNode; // Destination/Target Node to dock into
16670 ImGuiWindow* DockPayload; // Source/Payload window to dock (may be a loose window or a DockNode), [Optional]
16675 ImGuiDockNode* UndockTargetNode;
16676
16678 {
16682 DockSplitDir = ImGuiDir_None;
16683 DockSplitRatio = 0.5f;
16684 DockSplitOuter = false;
16685 }
16686};
16687
16689{
16690 ImGuiDockNode FutureNode;
16693 bool IsSidesAvailable; // Hold your breath, grammar freaks..
16694 bool IsSplitDirExplicit; // Set when hovered the drop rect (vs. implicit SplitDir==None when hovered the window)
16695 ImGuiDockNode* SplitNode;
16696 ImGuiDir SplitDir;
16698 ImRect DropRectsDraw[ImGuiDir_COUNT + 1]; // May be slightly different from hit-testing drop rects used in DockNodeCalcDropRects()
16699
16700 ImGuiDockPreviewData() : FutureNode(0) { IsDropAllowed = IsCenterAvailable = IsSidesAvailable = IsSplitDirExplicit = false; SplitNode = NULL; SplitDir = ImGuiDir_None; SplitRatio = 0.f; for (int n = 0; n < IM_ARRAYSIZE(DropRectsDraw); n++) DropRectsDraw[n] = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); }
16701};
16702
16703// Persistent Settings data, stored contiguously in SettingsNodes (sizeof() ~32 bytes)
16705{
16706 ImGuiID ID;
16710 signed char SplitAxis;
16711 char Depth;
16712 ImGuiDockNodeFlags Flags; // NB: We save individual flags one by one in ascii format (ImGuiDockNodeFlags_SavedFlagsMask_)
16713 ImVec2ih Pos;
16714 ImVec2ih Size;
16715 ImVec2ih SizeRef;
16716 ImGuiDockNodeSettings() { memset(this, 0, sizeof(*this)); SplitAxis = ImGuiAxis_None; }
16717};
16718
16719//-----------------------------------------------------------------------------
16720// Docking: Forward Declarations
16721//-----------------------------------------------------------------------------
16722
16723namespace ImGui
16724{
16725 // ImGuiDockContext
16726 static ImGuiDockNode* DockContextAddNode(ImGuiContext* ctx, ImGuiID id);
16727 static void DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node);
16728 static void DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node);
16729 static void DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req);
16730 static void DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx);
16731 static ImGuiDockNode* DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window);
16732 static void DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count);
16733 static void DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id); // Use root_id==0 to add all
16734
16735 // ImGuiDockNode
16736 static void DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar);
16737 static void DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node);
16738 static void DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node);
16739 static ImGuiWindow* DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id);
16740 static void DockNodeApplyPosSizeToWindows(ImGuiDockNode* node);
16741 static void DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id);
16742 static void DockNodeHideHostWindow(ImGuiDockNode* node);
16743 static void DockNodeUpdate(ImGuiDockNode* node);
16744 static void DockNodeUpdateForRootNode(ImGuiDockNode* node);
16745 static void DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node);
16746 static void DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node);
16747 static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window);
16748 static void DockNodeAddTabBar(ImGuiDockNode* node);
16749 static void DockNodeRemoveTabBar(ImGuiDockNode* node);
16750 static void DockNodeWindowMenuUpdate(ImGuiDockNode* node, ImGuiTabBar* tab_bar);
16751 static void DockNodeUpdateVisibleFlag(ImGuiDockNode* node);
16752 static void DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window);
16753 static bool DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window);
16754 static void DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking);
16755 static void DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data);
16756 static void DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos, ImVec2* out_close_button_pos);
16757 static void DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired);
16758 static bool DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking, ImVec2* test_mouse_pos);
16759 static const char* DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, "##DockNode_%02X", node->ID); return buf; }
16760 static int DockNodeGetTabOrder(ImGuiWindow* window);
16761
16762 // ImGuiDockNode tree manipulations
16763 static void DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_first_child, float split_ratio, ImGuiDockNode* new_node);
16764 static void DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child);
16765 static void DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, ImGuiDockNode* only_write_to_single_node = NULL);
16766 static void DockNodeTreeUpdateSplitter(ImGuiDockNode* node);
16767 static ImGuiDockNode* DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos);
16768 static ImGuiDockNode* DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node);
16769
16770 // Settings
16771 static void DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id);
16772 static void DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count);
16773 static ImGuiDockNodeSettings* DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID node_id);
16774 static void DockSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
16775 static void DockSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
16776 static void* DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
16777 static void DockSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
16778 static void DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
16779}
16780
16781//-----------------------------------------------------------------------------
16782// Docking: ImGuiDockContext
16783//-----------------------------------------------------------------------------
16784// The lifetime model is different from the one of regular windows: we always create a ImGuiDockNode for each ImGuiDockNodeSettings,
16785// or we always hold the entire docking node tree. Nodes are frequently hidden, e.g. if the window(s) or child nodes they host are not active.
16786// At boot time only, we run a simple GC to remove nodes that have no references.
16787// Because dock node settings (which are small, contiguous structures) are always mirrored by their corresponding dock nodes (more complete structures),
16788// we can also very easily recreate the nodes from scratch given the settings data (this is what DockContextRebuild() does).
16789// This is convenient as docking reconfiguration can be implemented by mostly poking at the simpler settings data.
16790//-----------------------------------------------------------------------------
16791// - DockContextInitialize()
16792// - DockContextShutdown()
16793// - DockContextClearNodes()
16794// - DockContextRebuildNodes()
16795// - DockContextNewFrameUpdateUndocking()
16796// - DockContextNewFrameUpdateDocking()
16797// - DockContextEndFrame()
16798// - DockContextFindNodeByID()
16799// - DockContextBindNodeToWindow()
16800// - DockContextGenNodeID()
16801// - DockContextAddNode()
16802// - DockContextRemoveNode()
16803// - ImGuiDockContextPruneNodeData
16804// - DockContextPruneUnusedSettingsNodes()
16805// - DockContextBuildNodesFromSettings()
16806// - DockContextBuildAddWindowsToNodes()
16807//-----------------------------------------------------------------------------
16808
16809void ImGui::DockContextInitialize(ImGuiContext* ctx)
16810{
16811 ImGuiContext& g = *ctx;
16812
16813 // Add .ini handle for persistent docking data
16814 ImGuiSettingsHandler ini_handler;
16815 ini_handler.TypeName = "Docking";
16816 ini_handler.TypeHash = ImHashStr("Docking");
16817 ini_handler.ClearAllFn = DockSettingsHandler_ClearAll;
16818 ini_handler.ReadInitFn = DockSettingsHandler_ClearAll; // Also clear on read
16819 ini_handler.ReadOpenFn = DockSettingsHandler_ReadOpen;
16820 ini_handler.ReadLineFn = DockSettingsHandler_ReadLine;
16821 ini_handler.ApplyAllFn = DockSettingsHandler_ApplyAll;
16822 ini_handler.WriteAllFn = DockSettingsHandler_WriteAll;
16823 g.SettingsHandlers.push_back(ini_handler);
16824
16825 g.DockNodeWindowMenuHandler = &DockNodeWindowMenuHandler_Default;
16826}
16827
16828void ImGui::DockContextShutdown(ImGuiContext* ctx)
16829{
16830 ImGuiDockContext* dc = &ctx->DockContext;
16831 for (int n = 0; n < dc->Nodes.Data.Size; n++)
16832 if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
16833 IM_DELETE(node);
16834}
16835
16836void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs)
16837{
16838 IM_UNUSED(ctx);
16839 IM_ASSERT(ctx == GImGui);
16840 DockBuilderRemoveNodeDockedWindows(root_id, clear_settings_refs);
16841 DockBuilderRemoveNodeChildNodes(root_id);
16842}
16843
16844// [DEBUG] This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch
16845// (Different from DockSettingsHandler_ClearAll() + DockSettingsHandler_ApplyAll() because this reuses current settings!)
16846void ImGui::DockContextRebuildNodes(ImGuiContext* ctx)
16847{
16848 ImGuiContext& g = *ctx;
16849 ImGuiDockContext* dc = &ctx->DockContext;
16850 IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextRebuildNodes\n");
16851 SaveIniSettingsToMemory();
16852 ImGuiID root_id = 0; // Rebuild all
16853 DockContextClearNodes(ctx, root_id, false);
16854 DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size);
16855 DockContextBuildAddWindowsToNodes(ctx, root_id);
16856}
16857
16858// Docking context update function, called by NewFrame()
16859void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx)
16860{
16861 ImGuiContext& g = *ctx;
16862 ImGuiDockContext* dc = &ctx->DockContext;
16863 if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
16864 {
16865 if (dc->Nodes.Data.Size > 0 || dc->Requests.Size > 0)
16866 DockContextClearNodes(ctx, 0, true);
16867 return;
16868 }
16869
16870 // Setting NoSplit at runtime merges all nodes
16871 if (g.IO.ConfigDockingNoSplit)
16872 for (int n = 0; n < dc->Nodes.Data.Size; n++)
16873 if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
16874 if (node->IsRootNode() && node->IsSplitNode())
16875 {
16876 DockBuilderRemoveNodeChildNodes(node->ID);
16877 //dc->WantFullRebuild = true;
16878 }
16879
16880 // Process full rebuild
16881#if 0
16882 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C)))
16883 dc->WantFullRebuild = true;
16884#endif
16885 if (dc->WantFullRebuild)
16886 {
16887 DockContextRebuildNodes(ctx);
16888 dc->WantFullRebuild = false;
16889 }
16890
16891 // Process Undocking requests (we need to process them _before_ the UpdateMouseMovingWindowNewFrame call in NewFrame)
16892 for (ImGuiDockRequest& req : dc->Requests)
16893 {
16894 if (req.Type == ImGuiDockRequestType_Undock && req.UndockTargetWindow)
16895 DockContextProcessUndockWindow(ctx, req.UndockTargetWindow);
16896 else if (req.Type == ImGuiDockRequestType_Undock && req.UndockTargetNode)
16897 DockContextProcessUndockNode(ctx, req.UndockTargetNode);
16898 }
16899}
16900
16901// Docking context update function, called by NewFrame()
16902void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx)
16903{
16904 ImGuiContext& g = *ctx;
16905 ImGuiDockContext* dc = &ctx->DockContext;
16906 if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
16907 return;
16908
16909 // [DEBUG] Store hovered dock node.
16910 // We could in theory use DockNodeTreeFindVisibleNodeByPos() on the root host dock node, but using ->DockNode is a good shortcut.
16911 // Note this is mostly a debug thing and isn't actually used for docking target, because docking involve more detailed filtering.
16912 g.DebugHoveredDockNode = NULL;
16913 if (ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow)
16914 {
16915 if (hovered_window->DockNodeAsHost)
16916 g.DebugHoveredDockNode = DockNodeTreeFindVisibleNodeByPos(hovered_window->DockNodeAsHost, g.IO.MousePos);
16917 else if (hovered_window->RootWindow->DockNode)
16918 g.DebugHoveredDockNode = hovered_window->RootWindow->DockNode;
16919 }
16920
16921 // Process Docking requests
16922 for (ImGuiDockRequest& req : dc->Requests)
16923 if (req.Type == ImGuiDockRequestType_Dock)
16924 DockContextProcessDock(ctx, &req);
16925 dc->Requests.resize(0);
16926
16927 // Create windows for each automatic docking nodes
16928 // We can have NULL pointers when we delete nodes, but because ID are recycled this should amortize nicely (and our node count will never be very high)
16929 for (int n = 0; n < dc->Nodes.Data.Size; n++)
16930 if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
16931 if (node->IsFloatingNode())
16932 DockNodeUpdate(node);
16933}
16934
16935void ImGui::DockContextEndFrame(ImGuiContext* ctx)
16936{
16937 // Draw backgrounds of node missing their window
16938 ImGuiContext& g = *ctx;
16939 ImGuiDockContext* dc = &g.DockContext;
16940 for (int n = 0; n < dc->Nodes.Data.Size; n++)
16941 if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
16942 if (node->LastFrameActive == g.FrameCount && node->IsVisible && node->HostWindow && node->IsLeafNode() && !node->IsBgDrawnThisFrame)
16943 {
16944 ImRect bg_rect(node->Pos + ImVec2(0.0f, GetFrameHeight()), node->Pos + node->Size);
16945 ImDrawFlags bg_rounding_flags = CalcRoundingFlagsForRectInRect(bg_rect, node->HostWindow->Rect(), g.Style.DockingSeparatorSize);
16946 node->HostWindow->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG);
16947 node->HostWindow->DrawList->AddRectFilled(bg_rect.Min, bg_rect.Max, node->LastBgColor, node->HostWindow->WindowRounding, bg_rounding_flags);
16948 }
16949}
16950
16951ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id)
16952{
16953 return (ImGuiDockNode*)ctx->DockContext.Nodes.GetVoidPtr(id);
16954}
16955
16956ImGuiID ImGui::DockContextGenNodeID(ImGuiContext* ctx)
16957{
16958 // Generate an ID for new node (the exact ID value doesn't matter as long as it is not already used)
16959 // FIXME-OPT FIXME-DOCK: This is suboptimal, even if the node count is small enough not to be a worry.0
16960 // We should poke in ctx->Nodes to find a suitable ID faster. Even more so trivial that ctx->Nodes lookup is already sorted.
16961 ImGuiID id = 0x0001;
16962 while (DockContextFindNodeByID(ctx, id) != NULL)
16963 id++;
16964 return id;
16965}
16966
16967static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id)
16968{
16969 // Generate an ID for the new node (the exact ID value doesn't matter as long as it is not already used) and add the first window.
16970 ImGuiContext& g = *ctx;
16971 if (id == 0)
16972 id = DockContextGenNodeID(ctx);
16973 else
16974 IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL);
16975
16976 // We don't set node->LastFrameAlive on construction. Nodes are always created at all time to reflect .ini settings!
16977 IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextAddNode 0x%08X\n", id);
16978 ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id);
16979 ctx->DockContext.Nodes.SetVoidPtr(node->ID, node);
16980 return node;
16981}
16982
16983static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node)
16984{
16985 ImGuiContext& g = *ctx;
16986 ImGuiDockContext* dc = &ctx->DockContext;
16987
16988 IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextRemoveNode 0x%08X\n", node->ID);
16989 IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node);
16990 IM_ASSERT(node->ChildNodes[0] == NULL && node->ChildNodes[1] == NULL);
16991 IM_ASSERT(node->Windows.Size == 0);
16992
16993 if (node->HostWindow)
16994 node->HostWindow->DockNodeAsHost = NULL;
16995
16996 ImGuiDockNode* parent_node = node->ParentNode;
16997 const bool merge = (merge_sibling_into_parent_node && parent_node != NULL);
16998 if (merge)
16999 {
17000 IM_ASSERT(parent_node->ChildNodes[0] == node || parent_node->ChildNodes[1] == node);
17001 ImGuiDockNode* sibling_node = (parent_node->ChildNodes[0] == node ? parent_node->ChildNodes[1] : parent_node->ChildNodes[0]);
17002 DockNodeTreeMerge(&g, parent_node, sibling_node);
17003 }
17004 else
17005 {
17006 for (int n = 0; parent_node && n < IM_ARRAYSIZE(parent_node->ChildNodes); n++)
17007 if (parent_node->ChildNodes[n] == node)
17008 node->ParentNode->ChildNodes[n] = NULL;
17009 dc->Nodes.SetVoidPtr(node->ID, NULL);
17010 IM_DELETE(node);
17011 }
17012}
17013
17014static int IMGUI_CDECL DockNodeComparerDepthMostFirst(const void* lhs, const void* rhs)
17015{
17016 const ImGuiDockNode* a = *(const ImGuiDockNode* const*)lhs;
17017 const ImGuiDockNode* b = *(const ImGuiDockNode* const*)rhs;
17018 return ImGui::DockNodeGetDepth(b) - ImGui::DockNodeGetDepth(a);
17019}
17020
17021// Pre C++0x doesn't allow us to use a function-local type (without linkage) as template parameter, so we moved this here.
17028
17029// Garbage collect unused nodes (run once at init time)
17030static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx)
17031{
17032 ImGuiContext& g = *ctx;
17033 ImGuiDockContext* dc = &ctx->DockContext;
17034 IM_ASSERT(g.Windows.Size == 0);
17035
17036 ImPool<ImGuiDockContextPruneNodeData> pool;
17037 pool.Reserve(dc->NodesSettings.Size);
17038
17039 // Count child nodes and compute RootID
17040 for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
17041 {
17042 ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
17043 ImGuiDockContextPruneNodeData* parent_data = settings->ParentNodeId ? pool.GetByKey(settings->ParentNodeId) : 0;
17044 pool.GetOrAddByKey(settings->ID)->RootId = parent_data ? parent_data->RootId : settings->ID;
17045 if (settings->ParentNodeId)
17046 pool.GetOrAddByKey(settings->ParentNodeId)->CountChildNodes++;
17047 }
17048
17049 // Count reference to dock ids from dockspaces
17050 // We track the 'auto-DockNode <- manual-Window <- manual-DockSpace' in order to avoid 'auto-DockNode' being ditched by DockContextPruneUnusedSettingsNodes()
17051 for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
17052 {
17053 ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
17054 if (settings->ParentWindowId != 0)
17055 if (ImGuiWindowSettings* window_settings = FindWindowSettingsByID(settings->ParentWindowId))
17056 if (window_settings->DockId)
17057 if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(window_settings->DockId))
17058 data->CountChildNodes++;
17059 }
17060
17061 // Count reference to dock ids from window settings
17062 // We guard against the possibility of an invalid .ini file (RootID may point to a missing node)
17063 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
17064 if (ImGuiID dock_id = settings->DockId)
17065 if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(dock_id))
17066 {
17067 data->CountWindows++;
17068 if (ImGuiDockContextPruneNodeData* data_root = (data->RootId == dock_id) ? data : pool.GetByKey(data->RootId))
17069 data_root->CountChildWindows++;
17070 }
17071
17072 // Prune
17073 for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
17074 {
17075 ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
17076 ImGuiDockContextPruneNodeData* data = pool.GetByKey(settings->ID);
17077 if (data->CountWindows > 1)
17078 continue;
17079 ImGuiDockContextPruneNodeData* data_root = (data->RootId == settings->ID) ? data : pool.GetByKey(data->RootId);
17080
17081 bool remove = false;
17082 remove |= (data->CountWindows == 1 && settings->ParentNodeId == 0 && data->CountChildNodes == 0 && !(settings->Flags & ImGuiDockNodeFlags_CentralNode)); // Floating root node with only 1 window
17083 remove |= (data->CountWindows == 0 && settings->ParentNodeId == 0 && data->CountChildNodes == 0); // Leaf nodes with 0 window
17084 remove |= (data_root->CountChildWindows == 0);
17085 if (remove)
17086 {
17087 IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextPruneUnusedSettingsNodes: Prune 0x%08X\n", settings->ID);
17088 DockSettingsRemoveNodeReferences(&settings->ID, 1);
17089 settings->ID = 0;
17090 }
17091 }
17092}
17093
17094static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count)
17095{
17096 // Build nodes
17097 for (int node_n = 0; node_n < node_settings_count; node_n++)
17098 {
17099 ImGuiDockNodeSettings* settings = &node_settings_array[node_n];
17100 if (settings->ID == 0)
17101 continue;
17102 ImGuiDockNode* node = DockContextAddNode(ctx, settings->ID);
17103 node->ParentNode = settings->ParentNodeId ? DockContextFindNodeByID(ctx, settings->ParentNodeId) : NULL;
17104 node->Pos = ImVec2(settings->Pos.x, settings->Pos.y);
17105 node->Size = ImVec2(settings->Size.x, settings->Size.y);
17106 node->SizeRef = ImVec2(settings->SizeRef.x, settings->SizeRef.y);
17107 node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_DockNode;
17108 if (node->ParentNode && node->ParentNode->ChildNodes[0] == NULL)
17109 node->ParentNode->ChildNodes[0] = node;
17110 else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL)
17111 node->ParentNode->ChildNodes[1] = node;
17112 node->SelectedTabId = settings->SelectedTabId;
17113 node->SplitAxis = (ImGuiAxis)settings->SplitAxis;
17114 node->SetLocalFlags(settings->Flags & ImGuiDockNodeFlags_SavedFlagsMask_);
17115
17116 // Bind host window immediately if it already exist (in case of a rebuild)
17117 // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set.
17118 char host_window_title[20];
17119 ImGuiDockNode* root_node = DockNodeGetRootNode(node);
17120 node->HostWindow = FindWindowByName(DockNodeGetHostWindowTitle(root_node, host_window_title, IM_ARRAYSIZE(host_window_title)));
17121 }
17122}
17123
17124void ImGui::DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id)
17125{
17126 // Rebind all windows to nodes (they can also lazily rebind but we'll have a visible glitch during the first frame)
17127 ImGuiContext& g = *ctx;
17128 for (ImGuiWindow* window : g.Windows)
17129 {
17130 if (window->DockId == 0 || window->LastFrameActive < g.FrameCount - 1)
17131 continue;
17132 if (window->DockNode != NULL)
17133 continue;
17134
17135 ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId);
17136 IM_ASSERT(node != NULL); // This should have been called after DockContextBuildNodesFromSettings()
17137 if (root_id == 0 || DockNodeGetRootNode(node)->ID == root_id)
17138 DockNodeAddWindow(node, window, true);
17139 }
17140}
17141
17142//-----------------------------------------------------------------------------
17143// Docking: ImGuiDockContext Docking/Undocking functions
17144//-----------------------------------------------------------------------------
17145// - DockContextQueueDock()
17146// - DockContextQueueUndockWindow()
17147// - DockContextQueueUndockNode()
17148// - DockContextQueueNotifyRemovedNode()
17149// - DockContextProcessDock()
17150// - DockContextProcessUndockWindow()
17151// - DockContextProcessUndockNode()
17152// - DockContextCalcDropPosForDocking()
17153//-----------------------------------------------------------------------------
17154
17155void ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer)
17156{
17157 IM_ASSERT(target != payload);
17158 ImGuiDockRequest req;
17160 req.DockTargetWindow = target;
17161 req.DockTargetNode = target_node;
17162 req.DockPayload = payload;
17163 req.DockSplitDir = split_dir;
17164 req.DockSplitRatio = split_ratio;
17165 req.DockSplitOuter = split_outer;
17166 ctx->DockContext.Requests.push_back(req);
17167}
17168
17169void ImGui::DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window)
17170{
17171 ImGuiDockRequest req;
17173 req.UndockTargetWindow = window;
17174 ctx->DockContext.Requests.push_back(req);
17175}
17176
17177void ImGui::DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node)
17178{
17179 ImGuiDockRequest req;
17181 req.UndockTargetNode = node;
17182 ctx->DockContext.Requests.push_back(req);
17183}
17184
17185void ImGui::DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node)
17186{
17187 ImGuiDockContext* dc = &ctx->DockContext;
17188 for (ImGuiDockRequest& req : dc->Requests)
17189 if (req.DockTargetNode == node)
17190 req.Type = ImGuiDockRequestType_None;
17191}
17192
17193void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req)
17194{
17195 IM_ASSERT((req->Type == ImGuiDockRequestType_Dock && req->DockPayload != NULL) || (req->Type == ImGuiDockRequestType_Split && req->DockPayload == NULL));
17196 IM_ASSERT(req->DockTargetWindow != NULL || req->DockTargetNode != NULL);
17197
17198 ImGuiContext& g = *ctx;
17199 IM_UNUSED(g);
17200
17201 ImGuiWindow* payload_window = req->DockPayload; // Optional
17202 ImGuiWindow* target_window = req->DockTargetWindow;
17203 ImGuiDockNode* node = req->DockTargetNode;
17204 if (payload_window)
17205 IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessDock node 0x%08X target '%s' dock window '%s', split_dir %d\n", node ? node->ID : 0, target_window ? target_window->Name : "NULL", payload_window->Name, req->DockSplitDir);
17206 else
17207 IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessDock node 0x%08X, split_dir %d\n", node ? node->ID : 0, req->DockSplitDir);
17208
17209 // Decide which Tab will be selected at the end of the operation
17210 ImGuiID next_selected_id = 0;
17211 ImGuiDockNode* payload_node = NULL;
17212 if (payload_window)
17213 {
17214 payload_node = payload_window->DockNodeAsHost;
17215 payload_window->DockNodeAsHost = NULL; // Important to clear this as the node will have its life as a child which might be merged/deleted later.
17216 if (payload_node && payload_node->IsLeafNode())
17217 next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId;
17218 if (payload_node == NULL)
17219 next_selected_id = payload_window->TabId;
17220 }
17221
17222 // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well
17223 // When processing an interactive split, usually LastFrameAlive will be < g.FrameCount. But DockBuilder operations can make it ==.
17224 if (node)
17225 IM_ASSERT(node->LastFrameAlive <= g.FrameCount);
17226 if (node && target_window && node == target_window->DockNodeAsHost)
17227 IM_ASSERT(node->Windows.Size > 0 || node->IsSplitNode() || node->IsCentralNode());
17228
17229 // Create new node and add existing window to it
17230 if (node == NULL)
17231 {
17232 node = DockContextAddNode(ctx, 0);
17233 node->Pos = target_window->Pos;
17234 node->Size = target_window->Size;
17235 if (target_window->DockNodeAsHost == NULL)
17236 {
17237 DockNodeAddWindow(node, target_window, true);
17238 node->TabBar->Tabs[0].Flags &= ~ImGuiTabItemFlags_Unsorted;
17239 target_window->DockIsActive = true;
17240 }
17241 }
17242
17243 ImGuiDir split_dir = req->DockSplitDir;
17244 if (split_dir != ImGuiDir_None)
17245 {
17246 // Split into two, one side will be our payload node unless we are dropping a loose window
17247 const ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
17248 const int split_inheritor_child_idx = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0; // Current contents will be moved to the opposite side
17249 const float split_ratio = req->DockSplitRatio;
17250 DockNodeTreeSplit(ctx, node, split_axis, split_inheritor_child_idx, split_ratio, payload_node); // payload_node may be NULL here!
17251 ImGuiDockNode* new_node = node->ChildNodes[split_inheritor_child_idx ^ 1];
17252 new_node->HostWindow = node->HostWindow;
17253 node = new_node;
17254 }
17255 node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar);
17256
17257 if (node != payload_node)
17258 {
17259 // Create tab bar before we call DockNodeMoveWindows (which would attempt to move the old tab-bar, which would lead us to payload tabs wrongly appearing before target tabs!)
17260 if (node->Windows.Size > 0 && node->TabBar == NULL)
17261 {
17262 DockNodeAddTabBar(node);
17263 for (int n = 0; n < node->Windows.Size; n++)
17264 TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]);
17265 }
17266
17267 if (payload_node != NULL)
17268 {
17269 // Transfer full payload node (with 1+ child windows or child nodes)
17270 if (payload_node->IsSplitNode())
17271 {
17272 if (node->Windows.Size > 0)
17273 {
17274 // We can dock a split payload into a node that already has windows _only_ if our payload is a node tree with a single visible node.
17275 // In this situation, we move the windows of the target node into the currently visible node of the payload.
17276 // This allows us to preserve some of the underlying dock tree settings nicely.
17277 IM_ASSERT(payload_node->OnlyNodeWithWindows != NULL); // The docking should have been blocked by DockNodePreviewDockSetup() early on and never submitted.
17278 ImGuiDockNode* visible_node = payload_node->OnlyNodeWithWindows;
17279 if (visible_node->TabBar)
17280 IM_ASSERT(visible_node->TabBar->Tabs.Size > 0);
17281 DockNodeMoveWindows(node, visible_node);
17282 DockNodeMoveWindows(visible_node, node);
17283 DockSettingsRenameNodeReferences(node->ID, visible_node->ID);
17284 }
17285 if (node->IsCentralNode())
17286 {
17287 // Central node property needs to be moved to a leaf node, pick the last focused one.
17288 // FIXME-DOCK: If we had to transfer other flags here, what would the policy be?
17289 ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeId);
17290 IM_ASSERT(last_focused_node != NULL);
17291 ImGuiDockNode* last_focused_root_node = DockNodeGetRootNode(last_focused_node);
17292 IM_ASSERT(last_focused_root_node == DockNodeGetRootNode(payload_node));
17293 last_focused_node->SetLocalFlags(last_focused_node->LocalFlags | ImGuiDockNodeFlags_CentralNode);
17294 node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_CentralNode);
17295 last_focused_root_node->CentralNode = last_focused_node;
17296 }
17297
17298 IM_ASSERT(node->Windows.Size == 0);
17299 DockNodeMoveChildNodes(node, payload_node);
17300 }
17301 else
17302 {
17303 const ImGuiID payload_dock_id = payload_node->ID;
17304 DockNodeMoveWindows(node, payload_node);
17305 DockSettingsRenameNodeReferences(payload_dock_id, node->ID);
17306 }
17307 DockContextRemoveNode(ctx, payload_node, true);
17308 }
17309 else if (payload_window)
17310 {
17311 // Transfer single window
17312 const ImGuiID payload_dock_id = payload_window->DockId;
17313 node->VisibleWindow = payload_window;
17314 DockNodeAddWindow(node, payload_window, true);
17315 if (payload_dock_id != 0)
17316 DockSettingsRenameNodeReferences(payload_dock_id, node->ID);
17317 }
17318 }
17319 else
17320 {
17321 // When docking a floating single window node we want to reevaluate auto-hiding of the tab bar
17322 node->WantHiddenTabBarUpdate = true;
17323 }
17324
17325 // Update selection immediately
17326 if (ImGuiTabBar* tab_bar = node->TabBar)
17327 tab_bar->NextSelectedTabId = next_selected_id;
17328 MarkIniSettingsDirty();
17329}
17330
17331// Problem:
17332// Undocking a large (~full screen) window would leave it so large that the bottom right sizing corner would more
17333// than likely be off the screen and the window would be hard to resize to fit on screen. This can be particularly problematic
17334// with 'ConfigWindowsMoveFromTitleBarOnly=true' and/or with 'ConfigWindowsResizeFromEdges=false' as well (the later can be
17335// due to missing ImGuiBackendFlags_HasMouseCursors backend flag).
17336// Solution:
17337// When undocking a window we currently force its maximum size to 90% of the host viewport or monitor.
17338// Reevaluate this when we implement preserving docked/undocked size ("docking_wip/undocked_size" branch).
17339static ImVec2 FixLargeWindowsWhenUndocking(const ImVec2& size, ImGuiViewport* ref_viewport)
17340{
17341 if (ref_viewport == NULL)
17342 return size;
17343
17344 ImGuiContext& g = *GImGui;
17345 ImVec2 max_size = ImTrunc(ref_viewport->WorkSize * 0.90f);
17346 if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
17347 {
17348 const ImGuiPlatformMonitor* monitor = ImGui::GetViewportPlatformMonitor(ref_viewport);
17349 max_size = ImTrunc(monitor->WorkSize * 0.90f);
17350 }
17351 return ImMin(size, max_size);
17352}
17353
17354void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref)
17355{
17356 ImGuiContext& g = *ctx;
17357 IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessUndockWindow window '%s', clear_persistent_docking_ref = %d\n", window->Name, clear_persistent_docking_ref);
17358 if (window->DockNode)
17359 DockNodeRemoveWindow(window->DockNode, window, clear_persistent_docking_ref ? 0 : window->DockId);
17360 else
17361 window->DockId = 0;
17362 window->Collapsed = false;
17363 window->DockIsActive = false;
17364 window->DockNodeIsVisible = window->DockTabIsVisible = false;
17365 window->Size = window->SizeFull = FixLargeWindowsWhenUndocking(window->SizeFull, window->Viewport);
17366
17367 MarkIniSettingsDirty();
17368}
17369
17370void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node)
17371{
17372 ImGuiContext& g = *ctx;
17373 IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessUndockNode node %08X\n", node->ID);
17374 IM_ASSERT(node->IsLeafNode());
17375 IM_ASSERT(node->Windows.Size >= 1);
17376
17377 if (node->IsRootNode() || node->IsCentralNode())
17378 {
17379 // In the case of a root node or central node, the node will have to stay in place. Create a new node to receive the payload.
17380 ImGuiDockNode* new_node = DockContextAddNode(ctx, 0);
17381 new_node->Pos = node->Pos;
17382 new_node->Size = node->Size;
17383 new_node->SizeRef = node->SizeRef;
17384 DockNodeMoveWindows(new_node, node);
17385 DockSettingsRenameNodeReferences(node->ID, new_node->ID);
17386 node = new_node;
17387 }
17388 else
17389 {
17390 // Otherwise extract our node and merge our sibling back into the parent node.
17391 IM_ASSERT(node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node);
17392 int index_in_parent = (node->ParentNode->ChildNodes[0] == node) ? 0 : 1;
17393 node->ParentNode->ChildNodes[index_in_parent] = NULL;
17394 DockNodeTreeMerge(ctx, node->ParentNode, node->ParentNode->ChildNodes[index_in_parent ^ 1]);
17395 node->ParentNode->AuthorityForViewport = ImGuiDataAuthority_Window; // The node that stays in place keeps the viewport, so our newly dragged out node will create a new viewport
17396 node->ParentNode = NULL;
17397 }
17398 for (ImGuiWindow* window : node->Windows)
17399 {
17400 window->Flags &= ~ImGuiWindowFlags_ChildWindow;
17401 if (window->ParentWindow)
17402 window->ParentWindow->DC.ChildWindows.find_erase(window);
17403 UpdateWindowParentAndRootLinks(window, window->Flags, NULL);
17404 }
17405 node->AuthorityForPos = node->AuthorityForSize = ImGuiDataAuthority_DockNode;
17406 node->Size = FixLargeWindowsWhenUndocking(node->Size, node->Windows[0]->Viewport);
17407 node->WantMouseMove = true;
17408 MarkIniSettingsDirty();
17409}
17410
17411// This is mostly used for automation.
17412bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos)
17413{
17414 if (target != NULL && target_node == NULL)
17415 target_node = target->DockNode;
17416
17417 // In DockNodePreviewDockSetup() for a root central node instead of showing both "inner" and "outer" drop rects
17418 // (which would be functionally identical) we only show the outer one. Reflect this here.
17419 if (target_node && target_node->ParentNode == NULL && target_node->IsCentralNode() && split_dir != ImGuiDir_None)
17420 split_outer = true;
17421 ImGuiDockPreviewData split_data;
17422 DockNodePreviewDockSetup(target, target_node, payload_window, payload_node, &split_data, false, split_outer);
17423 if (split_data.DropRectsDraw[split_dir+1].IsInverted())
17424 return false;
17425 *out_pos = split_data.DropRectsDraw[split_dir+1].GetCenter();
17426 return true;
17427}
17428
17429//-----------------------------------------------------------------------------
17430// Docking: ImGuiDockNode
17431//-----------------------------------------------------------------------------
17432// - DockNodeGetTabOrder()
17433// - DockNodeAddWindow()
17434// - DockNodeRemoveWindow()
17435// - DockNodeMoveChildNodes()
17436// - DockNodeMoveWindows()
17437// - DockNodeApplyPosSizeToWindows()
17438// - DockNodeHideHostWindow()
17439// - ImGuiDockNodeFindInfoResults
17440// - DockNodeFindInfo()
17441// - DockNodeFindWindowByID()
17442// - DockNodeUpdateFlagsAndCollapse()
17443// - DockNodeUpdateHasCentralNodeFlag()
17444// - DockNodeUpdateVisibleFlag()
17445// - DockNodeStartMouseMovingWindow()
17446// - DockNodeUpdate()
17447// - DockNodeUpdateWindowMenu()
17448// - DockNodeBeginAmendTabBar()
17449// - DockNodeEndAmendTabBar()
17450// - DockNodeUpdateTabBar()
17451// - DockNodeAddTabBar()
17452// - DockNodeRemoveTabBar()
17453// - DockNodeIsDropAllowedOne()
17454// - DockNodeIsDropAllowed()
17455// - DockNodeCalcTabBarLayout()
17456// - DockNodeCalcSplitRects()
17457// - DockNodeCalcDropRectsAndTestMousePos()
17458// - DockNodePreviewDockSetup()
17459// - DockNodePreviewDockRender()
17460//-----------------------------------------------------------------------------
17461
17462ImGuiDockNode::ImGuiDockNode(ImGuiID id)
17463{
17464 ID = id;
17465 SharedFlags = LocalFlags = LocalFlagsInWindows = MergedFlags = ImGuiDockNodeFlags_None;
17466 ParentNode = ChildNodes[0] = ChildNodes[1] = NULL;
17467 TabBar = NULL;
17468 SplitAxis = ImGuiAxis_None;
17469
17470 State = ImGuiDockNodeState_Unknown;
17471 LastBgColor = IM_COL32_WHITE;
17472 HostWindow = VisibleWindow = NULL;
17473 CentralNode = OnlyNodeWithWindows = NULL;
17474 CountNodeWithWindows = 0;
17475 LastFrameAlive = LastFrameActive = LastFrameFocused = -1;
17476 LastFocusedNodeId = 0;
17477 SelectedTabId = 0;
17478 WantCloseTabId = 0;
17479 RefViewportId = 0;
17480 AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode;
17481 AuthorityForViewport = ImGuiDataAuthority_Auto;
17482 IsVisible = true;
17483 IsFocused = HasCloseButton = HasWindowMenuButton = HasCentralNodeChild = false;
17484 IsBgDrawnThisFrame = false;
17485 WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false;
17486}
17487
17488ImGuiDockNode::~ImGuiDockNode()
17489{
17490 IM_DELETE(TabBar);
17491 TabBar = NULL;
17492 ChildNodes[0] = ChildNodes[1] = NULL;
17493}
17494
17495int ImGui::DockNodeGetTabOrder(ImGuiWindow* window)
17496{
17497 ImGuiTabBar* tab_bar = window->DockNode->TabBar;
17498 if (tab_bar == NULL)
17499 return -1;
17500 ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->TabId);
17501 return tab ? TabBarGetTabOrder(tab_bar, tab) : -1;
17502}
17503
17504static void DockNodeHideWindowDuringHostWindowCreation(ImGuiWindow* window)
17505{
17506 window->Hidden = true;
17507 window->HiddenFramesCanSkipItems = window->Active ? 1 : 2;
17508}
17509
17510static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar)
17511{
17512 ImGuiContext& g = *GImGui; (void)g;
17513 if (window->DockNode)
17514 {
17515 // Can overwrite an existing window->DockNode (e.g. pointing to a disabled DockSpace node)
17516 IM_ASSERT(window->DockNode->ID != node->ID);
17517 DockNodeRemoveWindow(window->DockNode, window, 0);
17518 }
17519 IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL);
17520 IMGUI_DEBUG_LOG_DOCKING("[docking] DockNodeAddWindow node 0x%08X window '%s'\n", node->ID, window->Name);
17521
17522 // If more than 2 windows appeared on the same frame leading to the creation of a new hosting window,
17523 // we'll hide windows until the host window is ready. Hide the 1st window after its been output (so it is not visible for one frame).
17524 // We will call DockNodeHideWindowDuringHostWindowCreation() on ourselves in Begin()
17525 if (node->HostWindow == NULL && node->Windows.Size == 1 && node->Windows[0]->WasActive == false)
17526 DockNodeHideWindowDuringHostWindowCreation(node->Windows[0]);
17527
17528 node->Windows.push_back(window);
17529 node->WantHiddenTabBarUpdate = true;
17530 window->DockNode = node;
17531 window->DockId = node->ID;
17532 window->DockIsActive = (node->Windows.Size > 1);
17533 window->DockTabWantClose = false;
17534
17535 // When reactivating a node with one or two loose window, the window pos/size/viewport are authoritative over the node storage.
17536 // In particular it is important we init the viewport from the first window so we don't create two viewports and drop one.
17537 if (node->HostWindow == NULL && node->IsFloatingNode())
17538 {
17539 if (node->AuthorityForPos == ImGuiDataAuthority_Auto)
17540 node->AuthorityForPos = ImGuiDataAuthority_Window;
17541 if (node->AuthorityForSize == ImGuiDataAuthority_Auto)
17542 node->AuthorityForSize = ImGuiDataAuthority_Window;
17543 if (node->AuthorityForViewport == ImGuiDataAuthority_Auto)
17544 node->AuthorityForViewport = ImGuiDataAuthority_Window;
17545 }
17546
17547 // Add to tab bar if requested
17548 if (add_to_tab_bar)
17549 {
17550 if (node->TabBar == NULL)
17551 {
17552 DockNodeAddTabBar(node);
17553 node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabId;
17554
17555 // Add existing windows
17556 for (int n = 0; n < node->Windows.Size - 1; n++)
17557 TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]);
17558 }
17559 TabBarAddTab(node->TabBar, ImGuiTabItemFlags_Unsorted, window);
17560 }
17561
17562 DockNodeUpdateVisibleFlag(node);
17563
17564 // Update this without waiting for the next time we Begin() in the window, so our host window will have the proper title bar color on its first frame.
17565 if (node->HostWindow)
17566 UpdateWindowParentAndRootLinks(window, window->Flags | ImGuiWindowFlags_ChildWindow, node->HostWindow);
17567}
17568
17569static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id)
17570{
17571 ImGuiContext& g = *GImGui;
17572 IM_ASSERT(window->DockNode == node);
17573 //IM_ASSERT(window->RootWindowDockTree == node->HostWindow);
17574 //IM_ASSERT(window->LastFrameActive < g.FrameCount); // We may call this from Begin()
17575 IM_ASSERT(save_dock_id == 0 || save_dock_id == node->ID);
17576 IMGUI_DEBUG_LOG_DOCKING("[docking] DockNodeRemoveWindow node 0x%08X window '%s'\n", node->ID, window->Name);
17577
17578 window->DockNode = NULL;
17579 window->DockIsActive = window->DockTabWantClose = false;
17580 window->DockId = save_dock_id;
17581 window->Flags &= ~ImGuiWindowFlags_ChildWindow;
17582 if (window->ParentWindow)
17583 window->ParentWindow->DC.ChildWindows.find_erase(window);
17584 UpdateWindowParentAndRootLinks(window, window->Flags, NULL); // Update immediately
17585
17586 if (node->HostWindow && node->HostWindow->ViewportOwned)
17587 {
17588 // When undocking from a user interaction this will always run in NewFrame() and have not much effect.
17589 // But mid-frame, if we clear viewport we need to mark window as hidden as well.
17590 window->Viewport = NULL;
17591 window->ViewportId = 0;
17592 window->ViewportOwned = false;
17593 window->Hidden = true;
17594 }
17595
17596 // Remove window
17597 bool erased = false;
17598 for (int n = 0; n < node->Windows.Size; n++)
17599 if (node->Windows[n] == window)
17600 {
17601 node->Windows.erase(node->Windows.Data + n);
17602 erased = true;
17603 break;
17604 }
17605 if (!erased)
17606 IM_ASSERT(erased);
17607 if (node->VisibleWindow == window)
17608 node->VisibleWindow = NULL;
17609
17610 // Remove tab and possibly tab bar
17611 node->WantHiddenTabBarUpdate = true;
17612 if (node->TabBar)
17613 {
17614 TabBarRemoveTab(node->TabBar, window->TabId);
17615 const int tab_count_threshold_for_tab_bar = node->IsCentralNode() ? 1 : 2;
17616 if (node->Windows.Size < tab_count_threshold_for_tab_bar)
17617 DockNodeRemoveTabBar(node);
17618 }
17619
17620 if (node->Windows.Size == 0 && !node->IsCentralNode() && !node->IsDockSpace() && window->DockId != node->ID)
17621 {
17622 // Automatic dock node delete themselves if they are not holding at least one tab
17623 DockContextRemoveNode(&g, node, true);
17624 return;
17625 }
17626
17627 if (node->Windows.Size == 1 && !node->IsCentralNode() && node->HostWindow)
17628 {
17629 ImGuiWindow* remaining_window = node->Windows[0];
17630 // Note: we used to transport viewport ownership here.
17631 remaining_window->Collapsed = node->HostWindow->Collapsed;
17632 }
17633
17634 // Update visibility immediately is required so the DockNodeUpdateRemoveInactiveChilds() processing can reflect changes up the tree
17635 DockNodeUpdateVisibleFlag(node);
17636}
17637
17638static void ImGui::DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node)
17639{
17640 IM_ASSERT(dst_node->Windows.Size == 0);
17641 dst_node->ChildNodes[0] = src_node->ChildNodes[0];
17642 dst_node->ChildNodes[1] = src_node->ChildNodes[1];
17643 if (dst_node->ChildNodes[0])
17644 dst_node->ChildNodes[0]->ParentNode = dst_node;
17645 if (dst_node->ChildNodes[1])
17646 dst_node->ChildNodes[1]->ParentNode = dst_node;
17647 dst_node->SplitAxis = src_node->SplitAxis;
17648 dst_node->SizeRef = src_node->SizeRef;
17649 src_node->ChildNodes[0] = src_node->ChildNodes[1] = NULL;
17650}
17651
17652static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node)
17653{
17654 // Insert tabs in the same orders as currently ordered (node->Windows isn't ordered)
17655 IM_ASSERT(src_node && dst_node && dst_node != src_node);
17656 ImGuiTabBar* src_tab_bar = src_node->TabBar;
17657 if (src_tab_bar != NULL)
17658 IM_ASSERT(src_node->Windows.Size <= src_node->TabBar->Tabs.Size);
17659
17660 // If the dst_node is empty we can just move the entire tab bar (to preserve selection, scrolling, etc.)
17661 bool move_tab_bar = (src_tab_bar != NULL) && (dst_node->TabBar == NULL);
17662 if (move_tab_bar)
17663 {
17664 dst_node->TabBar = src_node->TabBar;
17665 src_node->TabBar = NULL;
17666 }
17667
17668 // Tab order is not important here, it is preserved by sorting in DockNodeUpdateTabBar().
17669 for (ImGuiWindow* window : src_node->Windows)
17670 {
17671 window->DockNode = NULL;
17672 window->DockIsActive = false;
17673 DockNodeAddWindow(dst_node, window, !move_tab_bar);
17674 }
17675 src_node->Windows.clear();
17676
17677 if (!move_tab_bar && src_node->TabBar)
17678 {
17679 if (dst_node->TabBar)
17680 dst_node->TabBar->SelectedTabId = src_node->TabBar->SelectedTabId;
17681 DockNodeRemoveTabBar(src_node);
17682 }
17683}
17684
17685static void ImGui::DockNodeApplyPosSizeToWindows(ImGuiDockNode* node)
17686{
17687 for (ImGuiWindow* window : node->Windows)
17688 {
17689 SetWindowPos(window, node->Pos, ImGuiCond_Always); // We don't assign directly to Pos because it can break the calculation of SizeContents on next frame
17690 SetWindowSize(window, node->Size, ImGuiCond_Always);
17691 }
17692}
17693
17694static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node)
17695{
17696 if (node->HostWindow)
17697 {
17698 if (node->HostWindow->DockNodeAsHost == node)
17699 node->HostWindow->DockNodeAsHost = NULL;
17700 node->HostWindow = NULL;
17701 }
17702
17703 if (node->Windows.Size == 1)
17704 {
17705 node->VisibleWindow = node->Windows[0];
17706 node->Windows[0]->DockIsActive = false;
17707 }
17708
17709 if (node->TabBar)
17710 DockNodeRemoveTabBar(node);
17711}
17712
17713// Search function called once by root node in DockNodeUpdate()
17715{
17716 ImGuiDockNode* CentralNode;
17717 ImGuiDockNode* FirstNodeWithWindows;
17719 //ImGuiWindowClass WindowClassForMerges;
17720
17721 ImGuiDockNodeTreeInfo() { memset(this, 0, sizeof(*this)); }
17722};
17723
17724static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeTreeInfo* info)
17725{
17726 if (node->Windows.Size > 0)
17727 {
17728 if (info->FirstNodeWithWindows == NULL)
17729 info->FirstNodeWithWindows = node;
17730 info->CountNodesWithWindows++;
17731 }
17732 if (node->IsCentralNode())
17733 {
17734 IM_ASSERT(info->CentralNode == NULL); // Should be only one
17735 IM_ASSERT(node->IsLeafNode() && "If you get this assert: please submit .ini file + repro of actions leading to this.");
17736 info->CentralNode = node;
17737 }
17738 if (info->CountNodesWithWindows > 1 && info->CentralNode != NULL)
17739 return;
17740 if (node->ChildNodes[0])
17741 DockNodeFindInfo(node->ChildNodes[0], info);
17742 if (node->ChildNodes[1])
17743 DockNodeFindInfo(node->ChildNodes[1], info);
17744}
17745
17746static ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id)
17747{
17748 IM_ASSERT(id != 0);
17749 for (ImGuiWindow* window : node->Windows)
17750 if (window->ID == id)
17751 return window;
17752 return NULL;
17753}
17754
17755// - Remove inactive windows/nodes.
17756// - Update visibility flag.
17757static void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node)
17758{
17759 ImGuiContext& g = *GImGui;
17760 IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node);
17761
17762 // Inherit most flags
17763 if (node->ParentNode)
17764 node->SharedFlags = node->ParentNode->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
17765
17766 // Recurse into children
17767 // There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'.
17768 // If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node'
17769 // If 'node->ChildNode[1]' delete itself, then 'node->ChildNode[0]->Windows' will be moved into 'node' and the "remove inactive windows" loop will have run twice on those windows (harmless)
17770 node->HasCentralNodeChild = false;
17771 if (node->ChildNodes[0])
17772 DockNodeUpdateFlagsAndCollapse(node->ChildNodes[0]);
17773 if (node->ChildNodes[1])
17774 DockNodeUpdateFlagsAndCollapse(node->ChildNodes[1]);
17775
17776 // Remove inactive windows, collapse nodes
17777 // Merge node flags overrides stored in windows
17778 node->LocalFlagsInWindows = ImGuiDockNodeFlags_None;
17779 for (int window_n = 0; window_n < node->Windows.Size; window_n++)
17780 {
17781 ImGuiWindow* window = node->Windows[window_n];
17782 IM_ASSERT(window->DockNode == node);
17783
17784 bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount);
17785 bool remove = false;
17786 remove |= node_was_active && (window->LastFrameActive + 1 < g.FrameCount);
17787 remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabId == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame
17788 remove |= (window->DockTabWantClose);
17789 if (remove)
17790 {
17791 window->DockTabWantClose = false;
17792 if (node->Windows.Size == 1 && !node->IsCentralNode())
17793 {
17794 DockNodeHideHostWindow(node);
17795 node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow;
17796 DockNodeRemoveWindow(node, window, node->ID); // Will delete the node so it'll be invalid on return
17797 return;
17798 }
17799 DockNodeRemoveWindow(node, window, node->ID);
17800 window_n--;
17801 continue;
17802 }
17803
17804 // FIXME-DOCKING: Missing policies for conflict resolution, hence the "Experimental" tag on this.
17805 //node->LocalFlagsInWindow &= ~window->WindowClass.DockNodeFlagsOverrideClear;
17806 node->LocalFlagsInWindows |= window->WindowClass.DockNodeFlagsOverrideSet;
17807 }
17808 node->UpdateMergedFlags();
17809
17810 // Auto-hide tab bar option
17811 ImGuiDockNodeFlags node_flags = node->MergedFlags;
17812 if (node->WantHiddenTabBarUpdate && node->Windows.Size == 1 && (node_flags & ImGuiDockNodeFlags_AutoHideTabBar) && !node->IsHiddenTabBar())
17813 node->WantHiddenTabBarToggle = true;
17814 node->WantHiddenTabBarUpdate = false;
17815
17816 // Cancel toggling if we know our tab bar is enforced to be hidden at all times
17817 if (node->WantHiddenTabBarToggle && node->VisibleWindow && (node->VisibleWindow->WindowClass.DockNodeFlagsOverrideSet & ImGuiDockNodeFlags_HiddenTabBar))
17818 node->WantHiddenTabBarToggle = false;
17819
17820 // Apply toggles at a single point of the frame (here!)
17821 if (node->Windows.Size > 1)
17822 node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar);
17823 else if (node->WantHiddenTabBarToggle)
17824 node->SetLocalFlags(node->LocalFlags ^ ImGuiDockNodeFlags_HiddenTabBar);
17825 node->WantHiddenTabBarToggle = false;
17826
17827 DockNodeUpdateVisibleFlag(node);
17828}
17829
17830// This is rarely called as DockNodeUpdateForRootNode() generally does it most frames.
17831static void ImGui::DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node)
17832{
17833 node->HasCentralNodeChild = false;
17834 if (node->ChildNodes[0])
17835 DockNodeUpdateHasCentralNodeChild(node->ChildNodes[0]);
17836 if (node->ChildNodes[1])
17837 DockNodeUpdateHasCentralNodeChild(node->ChildNodes[1]);
17838 if (node->IsRootNode())
17839 {
17840 ImGuiDockNode* mark_node = node->CentralNode;
17841 while (mark_node)
17842 {
17843 mark_node->HasCentralNodeChild = true;
17844 mark_node = mark_node->ParentNode;
17845 }
17846 }
17847}
17848
17849static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node)
17850{
17851 // Update visibility flag
17852 bool is_visible = (node->ParentNode == NULL) ? node->IsDockSpace() : node->IsCentralNode();
17853 is_visible |= (node->Windows.Size > 0);
17854 is_visible |= (node->ChildNodes[0] && node->ChildNodes[0]->IsVisible);
17855 is_visible |= (node->ChildNodes[1] && node->ChildNodes[1]->IsVisible);
17856 node->IsVisible = is_visible;
17857}
17858
17859static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window)
17860{
17861 ImGuiContext& g = *GImGui;
17862 IM_ASSERT(node->WantMouseMove == true);
17863 StartMouseMovingWindow(window);
17864 g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - node->Pos;
17865 g.MovingWindow = window; // If we are docked into a non moveable root window, StartMouseMovingWindow() won't set g.MovingWindow. Override that decision.
17866 node->WantMouseMove = false;
17867}
17868
17869// Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class.
17870static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node)
17871{
17872 DockNodeUpdateFlagsAndCollapse(node);
17873
17874 // - Setup central node pointers
17875 // - Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!)
17876 // Cannot merge this with DockNodeUpdateFlagsAndCollapse() because FirstNodeWithWindows is found after window removal and child collapsing
17878 DockNodeFindInfo(node, &info);
17879 node->CentralNode = info.CentralNode;
17880 node->OnlyNodeWithWindows = (info.CountNodesWithWindows == 1) ? info.FirstNodeWithWindows : NULL;
17881 node->CountNodeWithWindows = info.CountNodesWithWindows;
17882 if (node->LastFocusedNodeId == 0 && info.FirstNodeWithWindows != NULL)
17883 node->LastFocusedNodeId = info.FirstNodeWithWindows->ID;
17884
17885 // Copy the window class from of our first window so it can be used for proper dock filtering.
17886 // When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy.
17887 // FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec.
17888 if (ImGuiDockNode* first_node_with_windows = info.FirstNodeWithWindows)
17889 {
17890 node->WindowClass = first_node_with_windows->Windows[0]->WindowClass;
17891 for (int n = 1; n < first_node_with_windows->Windows.Size; n++)
17892 if (first_node_with_windows->Windows[n]->WindowClass.DockingAllowUnclassed == false)
17893 {
17894 node->WindowClass = first_node_with_windows->Windows[n]->WindowClass;
17895 break;
17896 }
17897 }
17898
17899 ImGuiDockNode* mark_node = node->CentralNode;
17900 while (mark_node)
17901 {
17902 mark_node->HasCentralNodeChild = true;
17903 mark_node = mark_node->ParentNode;
17904 }
17905}
17906
17907static void DockNodeSetupHostWindow(ImGuiDockNode* node, ImGuiWindow* host_window)
17908{
17909 // Remove ourselves from any previous different host window
17910 // This can happen if a user mistakenly does (see #4295 for details):
17911 // - N+0: DockBuilderAddNode(id, 0) // missing ImGuiDockNodeFlags_DockSpace
17912 // - N+1: NewFrame() // will create floating host window for that node
17913 // - N+1: DockSpace(id) // requalify node as dockspace, moving host window
17914 if (node->HostWindow && node->HostWindow != host_window && node->HostWindow->DockNodeAsHost == node)
17915 node->HostWindow->DockNodeAsHost = NULL;
17916
17917 host_window->DockNodeAsHost = node;
17918 node->HostWindow = host_window;
17919}
17920
17921static void ImGui::DockNodeUpdate(ImGuiDockNode* node)
17922{
17923 ImGuiContext& g = *GImGui;
17924 IM_ASSERT(node->LastFrameActive != g.FrameCount);
17925 node->LastFrameAlive = g.FrameCount;
17926 node->IsBgDrawnThisFrame = false;
17927
17928 node->CentralNode = node->OnlyNodeWithWindows = NULL;
17929 if (node->IsRootNode())
17930 DockNodeUpdateForRootNode(node);
17931
17932 // Remove tab bar if not needed
17933 if (node->TabBar && node->IsNoTabBar())
17934 DockNodeRemoveTabBar(node);
17935
17936 // Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId)
17937 bool want_to_hide_host_window = false;
17938 if (node->IsFloatingNode())
17939 {
17940 if (node->Windows.Size <= 1 && node->IsLeafNode())
17941 if (!g.IO.ConfigDockingAlwaysTabBar && (node->Windows.Size == 0 || !node->Windows[0]->WindowClass.DockingAlwaysTabBar))
17942 want_to_hide_host_window = true;
17943 if (node->CountNodeWithWindows == 0)
17944 want_to_hide_host_window = true;
17945 }
17946 if (want_to_hide_host_window)
17947 {
17948 if (node->Windows.Size == 1)
17949 {
17950 // Floating window pos/size is authoritative
17951 ImGuiWindow* single_window = node->Windows[0];
17952 node->Pos = single_window->Pos;
17953 node->Size = single_window->SizeFull;
17954 node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window;
17955
17956 // Transfer focus immediately so when we revert to a regular window it is immediately selected
17957 if (node->HostWindow && g.NavWindow == node->HostWindow)
17958 FocusWindow(single_window);
17959 if (node->HostWindow)
17960 {
17961 IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Node %08X transfer Viewport %08X->%08X to Window '%s'\n", node->ID, node->HostWindow->Viewport->ID, single_window->ID, single_window->Name);
17962 single_window->Viewport = node->HostWindow->Viewport;
17963 single_window->ViewportId = node->HostWindow->ViewportId;
17964 if (node->HostWindow->ViewportOwned)
17965 {
17966 single_window->Viewport->ID = single_window->ID;
17967 single_window->Viewport->Window = single_window;
17968 single_window->ViewportOwned = true;
17969 }
17970 }
17971 node->RefViewportId = single_window->ViewportId;
17972 }
17973
17974 DockNodeHideHostWindow(node);
17975 node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow;
17976 node->WantCloseAll = false;
17977 node->WantCloseTabId = 0;
17978 node->HasCloseButton = node->HasWindowMenuButton = false;
17979 node->LastFrameActive = g.FrameCount;
17980
17981 if (node->WantMouseMove && node->Windows.Size == 1)
17982 DockNodeStartMouseMovingWindow(node, node->Windows[0]);
17983 return;
17984 }
17985
17986 // In some circumstance we will defer creating the host window (so everything will be kept hidden),
17987 // while the expected visible window is resizing itself.
17988 // This is important for first-time (no ini settings restored) single window when io.ConfigDockingAlwaysTabBar is enabled,
17989 // otherwise the node ends up using the minimum window size. Effectively those windows will take an extra frame to show up:
17990 // N+0: Begin(): window created (with no known size), node is created
17991 // N+1: DockNodeUpdate(): node skip creating host window / Begin(): window size applied, not visible
17992 // N+2: DockNodeUpdate(): node can create host window / Begin(): window becomes visible
17993 // We could remove this frame if we could reliably calculate the expected window size during node update, before the Begin() code.
17994 // It would require a generalization of CalcWindowExpectedSize(), probably extracting code away from Begin().
17995 // In reality it isn't very important as user quickly ends up with size data in .ini file.
17996 if (node->IsVisible && node->HostWindow == NULL && node->IsFloatingNode() && node->IsLeafNode())
17997 {
17998 IM_ASSERT(node->Windows.Size > 0);
17999 ImGuiWindow* ref_window = NULL;
18000 if (node->SelectedTabId != 0) // Note that we prune single-window-node settings on .ini loading, so this is generally 0 for them!
18001 ref_window = DockNodeFindWindowByID(node, node->SelectedTabId);
18002 if (ref_window == NULL)
18003 ref_window = node->Windows[0];
18004 if (ref_window->AutoFitFramesX > 0 || ref_window->AutoFitFramesY > 0)
18005 {
18006 node->State = ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing;
18007 return;
18008 }
18009 }
18010
18011 const ImGuiDockNodeFlags node_flags = node->MergedFlags;
18012
18013 // Decide if the node will have a close button and a window menu button
18014 node->HasWindowMenuButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0;
18015 node->HasCloseButton = false;
18016 for (ImGuiWindow* window : node->Windows)
18017 {
18018 // FIXME-DOCK: Setting DockIsActive here means that for single active window in a leaf node, DockIsActive will be cleared until the next Begin() call.
18019 node->HasCloseButton |= window->HasCloseButton;
18020 window->DockIsActive = (node->Windows.Size > 1);
18021 }
18022 if (node_flags & ImGuiDockNodeFlags_NoCloseButton)
18023 node->HasCloseButton = false;
18024
18025 // Bind or create host window
18026 ImGuiWindow* host_window = NULL;
18027 bool beginned_into_host_window = false;
18028 if (node->IsDockSpace())
18029 {
18030 // [Explicit root dockspace node]
18031 IM_ASSERT(node->HostWindow);
18032 host_window = node->HostWindow;
18033 }
18034 else
18035 {
18036 // [Automatic root or child nodes]
18037 if (node->IsRootNode() && node->IsVisible)
18038 {
18039 ImGuiWindow* ref_window = (node->Windows.Size > 0) ? node->Windows[0] : NULL;
18040
18041 // Sync Pos
18042 if (node->AuthorityForPos == ImGuiDataAuthority_Window && ref_window)
18043 SetNextWindowPos(ref_window->Pos);
18044 else if (node->AuthorityForPos == ImGuiDataAuthority_DockNode)
18045 SetNextWindowPos(node->Pos);
18046
18047 // Sync Size
18048 if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window)
18049 SetNextWindowSize(ref_window->SizeFull);
18050 else if (node->AuthorityForSize == ImGuiDataAuthority_DockNode)
18051 SetNextWindowSize(node->Size);
18052
18053 // Sync Collapsed
18054 if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window)
18055 SetNextWindowCollapsed(ref_window->Collapsed);
18056
18057 // Sync Viewport
18058 if (node->AuthorityForViewport == ImGuiDataAuthority_Window && ref_window)
18059 SetNextWindowViewport(ref_window->ViewportId);
18060 else if (node->AuthorityForViewport == ImGuiDataAuthority_Window && node->RefViewportId != 0)
18061 SetNextWindowViewport(node->RefViewportId);
18062
18063 SetNextWindowClass(&node->WindowClass);
18064
18065 // Begin into the host window
18066 char window_label[20];
18067 DockNodeGetHostWindowTitle(node, window_label, IM_ARRAYSIZE(window_label));
18068 ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_DockNodeHost;
18069 window_flags |= ImGuiWindowFlags_NoFocusOnAppearing;
18070 window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoCollapse;
18071 window_flags |= ImGuiWindowFlags_NoTitleBar;
18072
18073 SetNextWindowBgAlpha(0.0f); // Don't set ImGuiWindowFlags_NoBackground because it disables borders
18074 PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
18075 Begin(window_label, NULL, window_flags);
18076 PopStyleVar();
18077 beginned_into_host_window = true;
18078
18079 host_window = g.CurrentWindow;
18080 DockNodeSetupHostWindow(node, host_window);
18081 host_window->DC.CursorPos = host_window->Pos;
18082 node->Pos = host_window->Pos;
18083 node->Size = host_window->Size;
18084
18085 // We set ImGuiWindowFlags_NoFocusOnAppearing because we don't want the host window to take full focus (e.g. steal NavWindow)
18086 // But we still it bring it to the front of display. There's no way to choose this precise behavior via window flags.
18087 // One simple case to ponder if: window A has a toggle to create windows B/C/D. Dock B/C/D together, clear the toggle and enable it again.
18088 // When reappearing B/C/D will request focus and be moved to the top of the display pile, but they are not linked to the dock host window
18089 // during the frame they appear. The dock host window would keep its old display order, and the sorting in EndFrame would move B/C/D back
18090 // after the dock host window, losing their top-most status.
18091 if (node->HostWindow->Appearing)
18092 BringWindowToDisplayFront(node->HostWindow);
18093
18094 node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto;
18095 }
18096 else if (node->ParentNode)
18097 {
18098 node->HostWindow = host_window = node->ParentNode->HostWindow;
18099 node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto;
18100 }
18101 if (node->WantMouseMove && node->HostWindow)
18102 DockNodeStartMouseMovingWindow(node, node->HostWindow);
18103 }
18104 node->RefViewportId = 0; // Clear when we have a host window
18105
18106 // Update focused node (the one whose title bar is highlight) within a node tree
18107 if (node->IsSplitNode())
18108 IM_ASSERT(node->TabBar == NULL);
18109 if (node->IsRootNode())
18110 if (ImGuiWindow* p_window = g.NavWindow ? g.NavWindow->RootWindow : NULL)
18111 while (p_window != NULL && p_window->DockNode != NULL)
18112 {
18113 ImGuiDockNode* p_node = DockNodeGetRootNode(p_window->DockNode);
18114 if (p_node == node)
18115 {
18116 node->LastFocusedNodeId = p_window->DockNode->ID; // Note: not using root node ID!
18117 break;
18118 }
18119 p_window = p_node->HostWindow ? p_node->HostWindow->RootWindow : NULL;
18120 }
18121
18122 // Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace
18123 ImGuiDockNode* central_node = node->CentralNode;
18124 const bool central_node_hole = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0 && central_node != NULL && central_node->IsEmpty();
18125 bool central_node_hole_register_hit_test_hole = central_node_hole;
18126 if (central_node_hole)
18127 if (const ImGuiPayload* payload = ImGui::GetDragDropPayload())
18128 if (payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && DockNodeIsDropAllowed(host_window, *(ImGuiWindow**)payload->Data))
18129 central_node_hole_register_hit_test_hole = false;
18130 if (central_node_hole_register_hit_test_hole)
18131 {
18132 // We add a little padding to match the "resize from edges" behavior and allow grabbing the splitter easily.
18133 // (But we only add it if there's something else on the other side of the hole, otherwise for e.g. fullscreen
18134 // covering passthru node we'd have a gap on the edge not covered by the hole)
18135 IM_ASSERT(node->IsDockSpace()); // We cannot pass this flag without the DockSpace() api. Testing this because we also setup the hole in host_window->ParentNode
18136 ImGuiDockNode* root_node = DockNodeGetRootNode(central_node);
18137 ImRect root_rect(root_node->Pos, root_node->Pos + root_node->Size);
18138 ImRect hole_rect(central_node->Pos, central_node->Pos + central_node->Size);
18139 if (hole_rect.Min.x > root_rect.Min.x) { hole_rect.Min.x += WINDOWS_HOVER_PADDING; }
18140 if (hole_rect.Max.x < root_rect.Max.x) { hole_rect.Max.x -= WINDOWS_HOVER_PADDING; }
18141 if (hole_rect.Min.y > root_rect.Min.y) { hole_rect.Min.y += WINDOWS_HOVER_PADDING; }
18142 if (hole_rect.Max.y < root_rect.Max.y) { hole_rect.Max.y -= WINDOWS_HOVER_PADDING; }
18143 //GetForegroundDrawList()->AddRect(hole_rect.Min, hole_rect.Max, IM_COL32(255, 0, 0, 255));
18144 if (central_node_hole && !hole_rect.IsInverted())
18145 {
18146 SetWindowHitTestHole(host_window, hole_rect.Min, hole_rect.Max - hole_rect.Min);
18147 if (host_window->ParentWindow)
18148 SetWindowHitTestHole(host_window->ParentWindow, hole_rect.Min, hole_rect.Max - hole_rect.Min);
18149 }
18150 }
18151
18152 // Update position/size, process and draw resizing splitters
18153 if (node->IsRootNode() && host_window)
18154 {
18155 DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size);
18156 PushStyleColor(ImGuiCol_Separator, g.Style.Colors[ImGuiCol_Border]);
18157 PushStyleColor(ImGuiCol_SeparatorActive, g.Style.Colors[ImGuiCol_ResizeGripActive]);
18158 PushStyleColor(ImGuiCol_SeparatorHovered, g.Style.Colors[ImGuiCol_ResizeGripHovered]);
18159 DockNodeTreeUpdateSplitter(node);
18160 PopStyleColor(3);
18161 }
18162
18163 // Draw empty node background (currently can only be the Central Node)
18164 if (host_window && node->IsEmpty() && node->IsVisible)
18165 {
18166 host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG);
18167 node->LastBgColor = (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) ? 0 : GetColorU32(ImGuiCol_DockingEmptyBg);
18168 if (node->LastBgColor != 0)
18169 host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, node->LastBgColor);
18170 node->IsBgDrawnThisFrame = true;
18171 }
18172
18173 // Draw whole dockspace background if ImGuiDockNodeFlags_PassthruCentralNode if set.
18174 // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size
18175 // _after_ processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order!
18176 const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0;
18177 if (render_dockspace_bg && node->IsVisible)
18178 {
18179 host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG);
18180 if (central_node_hole)
18181 RenderRectFilledWithHole(host_window->DrawList, node->Rect(), central_node->Rect(), GetColorU32(ImGuiCol_WindowBg), 0.0f);
18182 else
18183 host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_WindowBg), 0.0f);
18184 }
18185
18186 // Draw and populate Tab Bar
18187 if (host_window)
18188 host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG);
18189 if (host_window && node->Windows.Size > 0)
18190 {
18191 DockNodeUpdateTabBar(node, host_window);
18192 }
18193 else
18194 {
18195 node->WantCloseAll = false;
18196 node->WantCloseTabId = 0;
18197 node->IsFocused = false;
18198 }
18199 if (node->TabBar && node->TabBar->SelectedTabId)
18200 node->SelectedTabId = node->TabBar->SelectedTabId;
18201 else if (node->Windows.Size > 0)
18202 node->SelectedTabId = node->Windows[0]->TabId;
18203
18204 // Draw payload drop target
18205 if (host_window && node->IsVisible)
18206 if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != host_window))
18207 BeginDockableDragDropTarget(host_window);
18208
18209 // We update this after DockNodeUpdateTabBar()
18210 node->LastFrameActive = g.FrameCount;
18211
18212 // Recurse into children
18213 // FIXME-DOCK FIXME-OPT: Should not need to recurse into children
18214 if (host_window)
18215 {
18216 if (node->ChildNodes[0])
18217 DockNodeUpdate(node->ChildNodes[0]);
18218 if (node->ChildNodes[1])
18219 DockNodeUpdate(node->ChildNodes[1]);
18220
18221 // Render outer borders last (after the tab bar)
18222 if (node->IsRootNode())
18223 RenderWindowOuterBorders(host_window);
18224 }
18225
18226 // End host window
18227 if (beginned_into_host_window) //-V1020
18228 End();
18229}
18230
18231// Compare TabItem nodes given the last known DockOrder (will persist in .ini file as hint), used to sort tabs when multiple tabs are added on the same frame.
18232static int IMGUI_CDECL TabItemComparerByDockOrder(const void* lhs, const void* rhs)
18233{
18234 ImGuiWindow* a = ((const ImGuiTabItem*)lhs)->Window;
18235 ImGuiWindow* b = ((const ImGuiTabItem*)rhs)->Window;
18236 if (int d = ((a->DockOrder == -1) ? INT_MAX : a->DockOrder) - ((b->DockOrder == -1) ? INT_MAX : b->DockOrder))
18237 return d;
18238 return (a->BeginOrderWithinContext - b->BeginOrderWithinContext);
18239}
18240
18241// Default handler for g.DockNodeWindowMenuHandler(): display the list of windows for a given dock-node.
18242// This is exceptionally stored in a function pointer to also user applications to tweak this menu (undocumented)
18243// Custom overrides may want to decorate, group, sort entries.
18244// Please note those are internal structures: if you copy this expect occasional breakage.
18245// (if you don't need to modify the "Tabs.Size == 1" behavior/path it is recommend you call this function in your handler)
18246void ImGui::DockNodeWindowMenuHandler_Default(ImGuiContext* ctx, ImGuiDockNode* node, ImGuiTabBar* tab_bar)
18247{
18248 IM_UNUSED(ctx);
18249 if (tab_bar->Tabs.Size == 1)
18250 {
18251 // "Hide tab bar" option. Being one of our rare user-facing string we pull it from a table.
18252 if (MenuItem(LocalizeGetMsg(ImGuiLocKey_DockingHideTabBar), NULL, node->IsHiddenTabBar()))
18253 node->WantHiddenTabBarToggle = true;
18254 }
18255 else
18256 {
18257 // Display a selectable list of windows in this docking node
18258 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
18259 {
18260 ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
18261 if (tab->Flags & ImGuiTabItemFlags_Button)
18262 continue;
18263 if (Selectable(TabBarGetTabName(tab_bar, tab), tab->ID == tab_bar->SelectedTabId))
18264 TabBarQueueFocus(tab_bar, tab);
18265 SameLine();
18266 Text(" ");
18267 }
18268 }
18269}
18270
18271static void ImGui::DockNodeWindowMenuUpdate(ImGuiDockNode* node, ImGuiTabBar* tab_bar)
18272{
18273 // Try to position the menu so it is more likely to stays within the same viewport
18274 ImGuiContext& g = *GImGui;
18275 if (g.Style.WindowMenuButtonPosition == ImGuiDir_Left)
18276 SetNextWindowPos(ImVec2(node->Pos.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(0.0f, 0.0f));
18277 else
18278 SetNextWindowPos(ImVec2(node->Pos.x + node->Size.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
18279 if (BeginPopup("#WindowMenu"))
18280 {
18281 node->IsFocused = true;
18282 g.DockNodeWindowMenuHandler(&g, node, tab_bar);
18283 EndPopup();
18284 }
18285}
18286
18287// User helper to append/amend into a dock node tab bar. Most commonly used to add e.g. a "+" button.
18288bool ImGui::DockNodeBeginAmendTabBar(ImGuiDockNode* node)
18289{
18290 if (node->TabBar == NULL || node->HostWindow == NULL)
18291 return false;
18292 if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly)
18293 return false;
18294 if (node->TabBar->ID == 0)
18295 return false;
18296 Begin(node->HostWindow->Name);
18297 PushOverrideID(node->ID);
18298 bool ret = BeginTabBarEx(node->TabBar, node->TabBar->BarRect, node->TabBar->Flags);
18299 IM_UNUSED(ret);
18300 IM_ASSERT(ret);
18301 return true;
18302}
18303
18304void ImGui::DockNodeEndAmendTabBar()
18305{
18306 EndTabBar();
18307 PopID();
18308 End();
18309}
18310
18311static bool IsDockNodeTitleBarHighlighted(ImGuiDockNode* node, ImGuiDockNode* root_node)
18312{
18313 // CTRL+Tab highlight (only highlighting leaf node, not whole hierarchy)
18314 ImGuiContext& g = *GImGui;
18315 if (g.NavWindowingTarget)
18316 return (g.NavWindowingTarget->DockNode == node);
18317
18318 // FIXME-DOCKING: May want alternative to treat central node void differently? e.g. if (g.NavWindow == host_window)
18319 if (g.NavWindow && root_node->LastFocusedNodeId == node->ID)
18320 {
18321 // FIXME: This could all be backed in RootWindowForTitleBarHighlight? Probably need to reorganize for both dock nodes + other RootWindowForTitleBarHighlight users (not-node)
18322 ImGuiWindow* parent_window = g.NavWindow->RootWindow;
18323 while (parent_window->Flags & ImGuiWindowFlags_ChildMenu)
18324 parent_window = parent_window->ParentWindow->RootWindow;
18325 ImGuiDockNode* start_parent_node = parent_window->DockNodeAsHost ? parent_window->DockNodeAsHost : parent_window->DockNode;
18326 for (ImGuiDockNode* parent_node = start_parent_node; parent_node != NULL; parent_node = parent_node->HostWindow ? parent_node->HostWindow->RootWindow->DockNode : NULL)
18327 if ((parent_node = ImGui::DockNodeGetRootNode(parent_node)) == root_node)
18328 return true;
18329 }
18330 return false;
18331}
18332
18333// Submit the tab bar corresponding to a dock node and various housekeeping details.
18334static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window)
18335{
18336 ImGuiContext& g = *GImGui;
18337 ImGuiStyle& style = g.Style;
18338
18339 const bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount);
18340 const bool closed_all = node->WantCloseAll && node_was_active;
18341 const ImGuiID closed_one = node->WantCloseTabId && node_was_active;
18342 node->WantCloseAll = false;
18343 node->WantCloseTabId = 0;
18344
18345 // Decide if we should use a focused title bar color
18346 bool is_focused = false;
18347 ImGuiDockNode* root_node = DockNodeGetRootNode(node);
18348 if (IsDockNodeTitleBarHighlighted(node, root_node))
18349 is_focused = true;
18350
18351 // Hidden tab bar will show a triangle on the upper-left (in Begin)
18352 if (node->IsHiddenTabBar() || node->IsNoTabBar())
18353 {
18354 node->VisibleWindow = (node->Windows.Size > 0) ? node->Windows[0] : NULL;
18355 node->IsFocused = is_focused;
18356 if (is_focused)
18357 node->LastFrameFocused = g.FrameCount;
18358 if (node->VisibleWindow)
18359 {
18360 // Notify root of visible window (used to display title in OS task bar)
18361 if (is_focused || root_node->VisibleWindow == NULL)
18362 root_node->VisibleWindow = node->VisibleWindow;
18363 if (node->TabBar)
18364 node->TabBar->VisibleTabId = node->VisibleWindow->TabId;
18365 }
18366 return;
18367 }
18368
18369 // Move ourselves to the Menu layer (so we can be accessed by tapping Alt) + undo SkipItems flag in order to draw over the title bar even if the window is collapsed
18370 bool backup_skip_item = host_window->SkipItems;
18371 if (!node->IsDockSpace())
18372 {
18373 host_window->SkipItems = false;
18374 host_window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
18375 }
18376
18377 // Use PushOverrideID() instead of PushID() to use the node id _without_ the host window ID.
18378 // This is to facilitate computing those ID from the outside, and will affect more or less only the ID of the collapse button, popup and tabs,
18379 // as docked windows themselves will override the stack with their own root ID.
18380 PushOverrideID(node->ID);
18381 ImGuiTabBar* tab_bar = node->TabBar;
18382 bool tab_bar_is_recreated = (tab_bar == NULL); // Tab bar are automatically destroyed when a node gets hidden
18383 if (tab_bar == NULL)
18384 {
18385 DockNodeAddTabBar(node);
18386 tab_bar = node->TabBar;
18387 }
18388
18389 ImGuiID focus_tab_id = 0;
18390 node->IsFocused = is_focused;
18391
18392 const ImGuiDockNodeFlags node_flags = node->MergedFlags;
18393 const bool has_window_menu_button = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0 && (style.WindowMenuButtonPosition != ImGuiDir_None);
18394
18395 // In a dock node, the Collapse Button turns into the Window Menu button.
18396 // FIXME-DOCK FIXME-OPT: Could we recycle popups id across multiple dock nodes?
18397 if (has_window_menu_button && IsPopupOpen("#WindowMenu"))
18398 {
18399 ImGuiID next_selected_tab_id = tab_bar->NextSelectedTabId;
18400 DockNodeWindowMenuUpdate(node, tab_bar);
18401 if (tab_bar->NextSelectedTabId != 0 && tab_bar->NextSelectedTabId != next_selected_tab_id)
18402 focus_tab_id = tab_bar->NextSelectedTabId;
18403 is_focused |= node->IsFocused;
18404 }
18405
18406 // Layout
18407 ImRect title_bar_rect, tab_bar_rect;
18408 ImVec2 window_menu_button_pos;
18409 ImVec2 close_button_pos;
18410 DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &window_menu_button_pos, &close_button_pos);
18411
18412 // Submit new tabs, they will be added as Unsorted and sorted below based on relative DockOrder value.
18413 const int tabs_count_old = tab_bar->Tabs.Size;
18414 for (int window_n = 0; window_n < node->Windows.Size; window_n++)
18415 {
18416 ImGuiWindow* window = node->Windows[window_n];
18417 if (TabBarFindTabByID(tab_bar, window->TabId) == NULL)
18418 TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window);
18419 }
18420
18421 // Title bar
18422 if (is_focused)
18423 node->LastFrameFocused = g.FrameCount;
18424 ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
18425 ImDrawFlags rounding_flags = CalcRoundingFlagsForRectInRect(title_bar_rect, host_window->Rect(), g.Style.DockingSeparatorSize);
18426 host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, rounding_flags);
18427
18428 // Docking/Collapse button
18429 if (has_window_menu_button)
18430 {
18431 if (CollapseButton(host_window->GetID("#COLLAPSE"), window_menu_button_pos, node)) // == DockNodeGetWindowMenuButtonId(node)
18432 OpenPopup("#WindowMenu");
18433 if (IsItemActive())
18434 focus_tab_id = tab_bar->SelectedTabId;
18435 if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal) && g.HoveredIdTimer > 0.5f)
18436 SetTooltip("%s", LocalizeGetMsg(ImGuiLocKey_DockingDragToUndockOrMoveNode));
18437 }
18438
18439 // If multiple tabs are appearing on the same frame, sort them based on their persistent DockOrder value
18440 int tabs_unsorted_start = tab_bar->Tabs.Size;
18441 for (int tab_n = tab_bar->Tabs.Size - 1; tab_n >= 0 && (tab_bar->Tabs[tab_n].Flags & ImGuiTabItemFlags_Unsorted); tab_n--)
18442 {
18443 // FIXME-DOCK: Consider only clearing the flag after the tab has been alive for a few consecutive frames, allowing late comers to not break sorting?
18444 tab_bar->Tabs[tab_n].Flags &= ~ImGuiTabItemFlags_Unsorted;
18445 tabs_unsorted_start = tab_n;
18446 }
18447 if (tab_bar->Tabs.Size > tabs_unsorted_start)
18448 {
18449 IMGUI_DEBUG_LOG_DOCKING("[docking] In node 0x%08X: %d new appearing tabs:%s\n", node->ID, tab_bar->Tabs.Size - tabs_unsorted_start, (tab_bar->Tabs.Size > tabs_unsorted_start + 1) ? " (will sort)" : "");
18450 for (int tab_n = tabs_unsorted_start; tab_n < tab_bar->Tabs.Size; tab_n++)
18451 {
18452 ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
18453 IMGUI_DEBUG_LOG_DOCKING("[docking] - Tab 0x%08X '%s' Order %d\n", tab->ID, TabBarGetTabName(tab_bar, tab), tab->Window ? tab->Window->DockOrder : -1);
18454 }
18455 IMGUI_DEBUG_LOG_DOCKING("[docking] SelectedTabId = 0x%08X, NavWindow->TabId = 0x%08X\n", node->SelectedTabId, g.NavWindow ? g.NavWindow->TabId : -1);
18456 if (tab_bar->Tabs.Size > tabs_unsorted_start + 1)
18457 ImQsort(tab_bar->Tabs.Data + tabs_unsorted_start, tab_bar->Tabs.Size - tabs_unsorted_start, sizeof(ImGuiTabItem), TabItemComparerByDockOrder);
18458 }
18459
18460 // Apply NavWindow focus back to the tab bar
18461 if (g.NavWindow && g.NavWindow->RootWindow->DockNode == node)
18462 tab_bar->SelectedTabId = g.NavWindow->RootWindow->TabId;
18463
18464 // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated
18465 if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabId) != NULL)
18466 tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabId;
18467 else if (tab_bar->Tabs.Size > tabs_count_old)
18468 tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->TabId;
18469
18470 // Begin tab bar
18471 ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs; // | ImGuiTabBarFlags_NoTabListScrollingButtons);
18472 tab_bar_flags |= ImGuiTabBarFlags_SaveSettings | ImGuiTabBarFlags_DockNode;// | ImGuiTabBarFlags_FittingPolicyScroll;
18473 tab_bar_flags |= ImGuiTabBarFlags_DrawSelectedOverline;
18474 if (!host_window->Collapsed && is_focused)
18475 tab_bar_flags |= ImGuiTabBarFlags_IsFocused;
18476 tab_bar->ID = GetID("#TabBar");
18477 tab_bar->SeparatorMinX = node->Pos.x + host_window->WindowBorderSize; // Separator cover the whole node width
18478 tab_bar->SeparatorMaxX = node->Pos.x + node->Size.x - host_window->WindowBorderSize;
18479 BeginTabBarEx(tab_bar, tab_bar_rect, tab_bar_flags);
18480 //host_window->DrawList->AddRect(tab_bar_rect.Min, tab_bar_rect.Max, IM_COL32(255,0,255,255));
18481
18482 // Backup style colors
18483 ImVec4 backup_style_cols[ImGuiWindowDockStyleCol_COUNT];
18484 for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
18485 backup_style_cols[color_n] = g.Style.Colors[GWindowDockStyleColors[color_n]];
18486
18487 // Submit actual tabs
18488 node->VisibleWindow = NULL;
18489 for (int window_n = 0; window_n < node->Windows.Size; window_n++)
18490 {
18491 ImGuiWindow* window = node->Windows[window_n];
18492 if ((closed_all || closed_one == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument))
18493 continue;
18494 if (window->LastFrameActive + 1 >= g.FrameCount || !node_was_active)
18495 {
18496 ImGuiTabItemFlags tab_item_flags = 0;
18497 tab_item_flags |= window->WindowClass.TabItemFlagsOverrideSet;
18498 if (window->Flags & ImGuiWindowFlags_UnsavedDocument)
18499 tab_item_flags |= ImGuiTabItemFlags_UnsavedDocument;
18500 if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)
18501 tab_item_flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;
18502
18503 // Apply stored style overrides for the window
18504 for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
18505 g.Style.Colors[GWindowDockStyleColors[color_n]] = ColorConvertU32ToFloat4(window->DockStyle.Colors[color_n]);
18506
18507 // Note that TabItemEx() calls TabBarCalcTabID() so our tab item ID will ignore the current ID stack (rightly so)
18508 bool tab_open = true;
18509 TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window);
18510 if (!tab_open)
18511 node->WantCloseTabId = window->TabId;
18512 if (tab_bar->VisibleTabId == window->TabId)
18513 node->VisibleWindow = window;
18514
18515 // Store last item data so it can be queried with IsItemXXX functions after the user Begin() call
18516 window->DockTabItemStatusFlags = g.LastItemData.StatusFlags;
18517 window->DockTabItemRect = g.LastItemData.Rect;
18518
18519 // Update navigation ID on menu layer
18520 if (g.NavWindow && g.NavWindow->RootWindow == window && (window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0)
18521 host_window->NavLastIds[1] = window->TabId;
18522 }
18523 }
18524
18525 // Restore style colors
18526 for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
18527 g.Style.Colors[GWindowDockStyleColors[color_n]] = backup_style_cols[color_n];
18528
18529 // Notify root of visible window (used to display title in OS task bar)
18530 if (node->VisibleWindow)
18531 if (is_focused || root_node->VisibleWindow == NULL)
18532 root_node->VisibleWindow = node->VisibleWindow;
18533
18534 // Close button (after VisibleWindow was updated)
18535 // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->TabId may be != from tab_bar->SelectedTabId
18536 const bool close_button_is_enabled = node->HasCloseButton && node->VisibleWindow && node->VisibleWindow->HasCloseButton;
18537 const bool close_button_is_visible = node->HasCloseButton;
18538 //const bool close_button_is_visible = close_button_is_enabled; // Most people would expect this behavior of not even showing the button (leaving a hole since we can't claim that space as other windows in the tba bar have one)
18539 if (close_button_is_visible)
18540 {
18541 if (!close_button_is_enabled)
18542 {
18543 PushItemFlag(ImGuiItemFlags_Disabled, true);
18544 PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_Text] * ImVec4(1.0f,1.0f,1.0f,0.4f));
18545 }
18546 if (CloseButton(host_window->GetID("#CLOSE"), close_button_pos))
18547 {
18548 node->WantCloseAll = true;
18549 for (int n = 0; n < tab_bar->Tabs.Size; n++)
18550 TabBarCloseTab(tab_bar, &tab_bar->Tabs[n]);
18551 }
18552 //if (IsItemActive())
18553 // focus_tab_id = tab_bar->SelectedTabId;
18554 if (!close_button_is_enabled)
18555 {
18556 PopStyleColor();
18557 PopItemFlag();
18558 }
18559 }
18560
18561 // When clicking on the title bar outside of tabs, we still focus the selected tab for that node
18562 // FIXME: TabItems submitted earlier use AllowItemOverlap so we manually perform a more specific test for now (hovered || held) in order to not cover them.
18563 ImGuiID title_bar_id = host_window->GetID("#TITLEBAR");
18564 if (g.HoveredId == 0 || g.HoveredId == title_bar_id || g.ActiveId == title_bar_id)
18565 {
18566 // AllowOverlap mode required for appending into dock node tab bar,
18567 // otherwise dragging window will steal HoveredId and amended tabs cannot get them.
18568 bool held;
18569 KeepAliveID(title_bar_id);
18570 ButtonBehavior(title_bar_rect, title_bar_id, NULL, &held, ImGuiButtonFlags_AllowOverlap);
18571 if (g.HoveredId == title_bar_id)
18572 {
18573 g.LastItemData.ID = title_bar_id;
18574 }
18575 if (held)
18576 {
18577 if (IsMouseClicked(0))
18578 focus_tab_id = tab_bar->SelectedTabId;
18579
18580 // Forward moving request to selected window
18581 if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId))
18582 StartMouseMovingWindowOrNode(tab->Window ? tab->Window : node->HostWindow, node, false); // Undock from tab bar empty space
18583 }
18584 }
18585
18586 // Forward focus from host node to selected window
18587 //if (is_focused && g.NavWindow == host_window && !g.NavWindowingTarget)
18588 // focus_tab_id = tab_bar->SelectedTabId;
18589
18590 // When clicked on a tab we requested focus to the docked child
18591 // This overrides the value set by "forward focus from host node to selected window".
18592 if (tab_bar->NextSelectedTabId)
18593 focus_tab_id = tab_bar->NextSelectedTabId;
18594
18595 // Apply navigation focus
18596 if (focus_tab_id != 0)
18597 if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id))
18598 if (tab->Window)
18599 {
18600 FocusWindow(tab->Window);
18601 NavInitWindow(tab->Window, false);
18602 }
18603
18604 EndTabBar();
18605 PopID();
18606
18607 // Restore SkipItems flag
18608 if (!node->IsDockSpace())
18609 {
18610 host_window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
18611 host_window->SkipItems = backup_skip_item;
18612 }
18613}
18614
18615static void ImGui::DockNodeAddTabBar(ImGuiDockNode* node)
18616{
18617 IM_ASSERT(node->TabBar == NULL);
18618 node->TabBar = IM_NEW(ImGuiTabBar);
18619}
18620
18621static void ImGui::DockNodeRemoveTabBar(ImGuiDockNode* node)
18622{
18623 if (node->TabBar == NULL)
18624 return;
18625 IM_DELETE(node->TabBar);
18626 node->TabBar = NULL;
18627}
18628
18629static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_window)
18630{
18631 if (host_window->DockNodeAsHost && host_window->DockNodeAsHost->IsDockSpace() && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext)
18632 return false;
18633
18634 ImGuiWindowClass* host_class = host_window->DockNodeAsHost ? &host_window->DockNodeAsHost->WindowClass : &host_window->WindowClass;
18635 ImGuiWindowClass* payload_class = &payload->WindowClass;
18636 if (host_class->ClassId != payload_class->ClassId)
18637 {
18638 bool pass = false;
18639 if (host_class->ClassId != 0 && host_class->DockingAllowUnclassed && payload_class->ClassId == 0)
18640 pass = true;
18641 if (payload_class->ClassId != 0 && payload_class->DockingAllowUnclassed && host_class->ClassId == 0)
18642 pass = true;
18643 if (!pass)
18644 return false;
18645 }
18646
18647 // Prevent docking any window created above a popup
18648 // Technically we should support it (e.g. in the case of a long-lived modal window that had fancy docking features),
18649 // by e.g. adding a 'if (!ImGui::IsWindowWithinBeginStackOf(host_window, popup_window))' test.
18650 // But it would requires more work on our end because the dock host windows is technically created in NewFrame()
18651 // and our ->ParentXXX and ->RootXXX pointers inside windows are currently mislading or lacking.
18652 ImGuiContext& g = *GImGui;
18653 for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--)
18654 if (ImGuiWindow* popup_window = g.OpenPopupStack[i].Window)
18655 if (ImGui::IsWindowWithinBeginStackOf(payload, popup_window)) // Payload is created from within a popup begin stack.
18656 return false;
18657
18658 return true;
18659}
18660
18661static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* root_payload)
18662{
18663 if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode()) // FIXME-DOCK: Missing filtering
18664 return true;
18665
18666 const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows.Size : 1;
18667 for (int payload_n = 0; payload_n < payload_count; payload_n++)
18668 {
18669 ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows[payload_n] : root_payload;
18670 if (DockNodeIsDropAllowedOne(payload, host_window))
18671 return true;
18672 }
18673 return false;
18674}
18675
18676// window menu button == collapse button when not in a dock node.
18677// FIXME: This is similar to RenderWindowTitleBarContents(), may want to share code.
18678static void ImGui::DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos, ImVec2* out_close_button_pos)
18679{
18680 ImGuiContext& g = *GImGui;
18681 ImGuiStyle& style = g.Style;
18682
18683 ImRect r = ImRect(node->Pos.x, node->Pos.y, node->Pos.x + node->Size.x, node->Pos.y + g.FontSize + g.Style.FramePadding.y * 2.0f);
18684 if (out_title_rect) { *out_title_rect = r; }
18685
18686 r.Min.x += style.WindowBorderSize;
18687 r.Max.x -= style.WindowBorderSize;
18688
18689 float button_sz = g.FontSize;
18690 r.Min.x += style.FramePadding.x;
18691 r.Max.x -= style.FramePadding.x;
18692 ImVec2 window_menu_button_pos = ImVec2(r.Min.x, r.Min.y + style.FramePadding.y);
18693 if (node->HasCloseButton)
18694 {
18695 if (out_close_button_pos) *out_close_button_pos = ImVec2(r.Max.x - button_sz, r.Min.y + style.FramePadding.y);
18696 r.Max.x -= button_sz + style.ItemInnerSpacing.x;
18697 }
18698 if (node->HasWindowMenuButton && style.WindowMenuButtonPosition == ImGuiDir_Left)
18699 {
18700 r.Min.x += button_sz + style.ItemInnerSpacing.x;
18701 }
18702 else if (node->HasWindowMenuButton && style.WindowMenuButtonPosition == ImGuiDir_Right)
18703 {
18704 window_menu_button_pos = ImVec2(r.Max.x - button_sz, r.Min.y + style.FramePadding.y);
18705 r.Max.x -= button_sz + style.ItemInnerSpacing.x;
18706 }
18707 if (out_tab_bar_rect) { *out_tab_bar_rect = r; }
18708 if (out_window_menu_button_pos) { *out_window_menu_button_pos = window_menu_button_pos; }
18709}
18710
18711void ImGui::DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired)
18712{
18713 ImGuiContext& g = *GImGui;
18714 const float dock_spacing = g.Style.ItemInnerSpacing.x;
18715 const ImGuiAxis axis = (dir == ImGuiDir_Left || dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
18716 pos_new[axis ^ 1] = pos_old[axis ^ 1];
18717 size_new[axis ^ 1] = size_old[axis ^ 1];
18718
18719 // Distribute size on given axis (with a desired size or equally)
18720 const float w_avail = size_old[axis] - dock_spacing;
18721 if (size_new_desired[axis] > 0.0f && size_new_desired[axis] <= w_avail * 0.5f)
18722 {
18723 size_new[axis] = size_new_desired[axis];
18724 size_old[axis] = IM_TRUNC(w_avail - size_new[axis]);
18725 }
18726 else
18727 {
18728 size_new[axis] = IM_TRUNC(w_avail * 0.5f);
18729 size_old[axis] = IM_TRUNC(w_avail - size_new[axis]);
18730 }
18731
18732 // Position each node
18733 if (dir == ImGuiDir_Right || dir == ImGuiDir_Down)
18734 {
18735 pos_new[axis] = pos_old[axis] + size_old[axis] + dock_spacing;
18736 }
18737 else if (dir == ImGuiDir_Left || dir == ImGuiDir_Up)
18738 {
18739 pos_new[axis] = pos_old[axis];
18740 pos_old[axis] = pos_new[axis] + size_new[axis] + dock_spacing;
18741 }
18742}
18743
18744// Retrieve the drop rectangles for a given direction or for the center + perform hit testing.
18745bool ImGui::DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_r, bool outer_docking, ImVec2* test_mouse_pos)
18746{
18747 ImGuiContext& g = *GImGui;
18748
18749 const float parent_smaller_axis = ImMin(parent.GetWidth(), parent.GetHeight());
18750 const float hs_for_central_nodes = ImMin(g.FontSize * 1.5f, ImMax(g.FontSize * 0.5f, parent_smaller_axis / 8.0f));
18751 float hs_w; // Half-size, longer axis
18752 float hs_h; // Half-size, smaller axis
18753 ImVec2 off; // Distance from edge or center
18754 if (outer_docking)
18755 {
18756 //hs_w = ImTrunc(ImClamp(parent_smaller_axis - hs_for_central_nodes * 4.0f, g.FontSize * 0.5f, g.FontSize * 8.0f));
18757 //hs_h = ImTrunc(hs_w * 0.15f);
18758 //off = ImVec2(ImTrunc(parent.GetWidth() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h), ImTrunc(parent.GetHeight() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h));
18759 hs_w = ImTrunc(hs_for_central_nodes * 1.50f);
18760 hs_h = ImTrunc(hs_for_central_nodes * 0.80f);
18761 off = ImTrunc(ImVec2(parent.GetWidth() * 0.5f - hs_h, parent.GetHeight() * 0.5f - hs_h));
18762 }
18763 else
18764 {
18765 hs_w = ImTrunc(hs_for_central_nodes);
18766 hs_h = ImTrunc(hs_for_central_nodes * 0.90f);
18767 off = ImTrunc(ImVec2(hs_w * 2.40f, hs_w * 2.40f));
18768 }
18769
18770 ImVec2 c = ImTrunc(parent.GetCenter());
18771 if (dir == ImGuiDir_None) { out_r = ImRect(c.x - hs_w, c.y - hs_w, c.x + hs_w, c.y + hs_w); }
18772 else if (dir == ImGuiDir_Up) { out_r = ImRect(c.x - hs_w, c.y - off.y - hs_h, c.x + hs_w, c.y - off.y + hs_h); }
18773 else if (dir == ImGuiDir_Down) { out_r = ImRect(c.x - hs_w, c.y + off.y - hs_h, c.x + hs_w, c.y + off.y + hs_h); }
18774 else if (dir == ImGuiDir_Left) { out_r = ImRect(c.x - off.x - hs_h, c.y - hs_w, c.x - off.x + hs_h, c.y + hs_w); }
18775 else if (dir == ImGuiDir_Right) { out_r = ImRect(c.x + off.x - hs_h, c.y - hs_w, c.x + off.x + hs_h, c.y + hs_w); }
18776
18777 if (test_mouse_pos == NULL)
18778 return false;
18779
18780 ImRect hit_r = out_r;
18781 if (!outer_docking)
18782 {
18783 // Custom hit testing for the 5-way selection, designed to reduce flickering when moving diagonally between sides
18784 hit_r.Expand(ImTrunc(hs_w * 0.30f));
18785 ImVec2 mouse_delta = (*test_mouse_pos - c);
18786 float mouse_delta_len2 = ImLengthSqr(mouse_delta);
18787 float r_threshold_center = hs_w * 1.4f;
18788 float r_threshold_sides = hs_w * (1.4f + 1.2f);
18789 if (mouse_delta_len2 < r_threshold_center * r_threshold_center)
18790 return (dir == ImGuiDir_None);
18791 if (mouse_delta_len2 < r_threshold_sides * r_threshold_sides)
18792 return (dir == ImGetDirQuadrantFromDelta(mouse_delta.x, mouse_delta.y));
18793 }
18794 return hit_r.Contains(*test_mouse_pos);
18795}
18796
18797// host_node may be NULL if the window doesn't have a DockNode already.
18798// FIXME-DOCK: This is misnamed since it's also doing the filtering.
18799static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking)
18800{
18801 ImGuiContext& g = *GImGui;
18802
18803 // There is an edge case when docking into a dockspace which only has inactive nodes.
18804 // In this case DockNodeTreeFindNodeByPos() will have selected a leaf node which is inactive.
18805 // Because the inactive leaf node doesn't have proper pos/size yet, we'll use the root node as reference.
18806 if (payload_node == NULL)
18807 payload_node = payload_window->DockNodeAsHost;
18808 ImGuiDockNode* ref_node_for_rect = (host_node && !host_node->IsVisible) ? DockNodeGetRootNode(host_node) : host_node;
18809 if (ref_node_for_rect)
18810 IM_ASSERT(ref_node_for_rect->IsVisible == true);
18811
18812 // Filter, figure out where we are allowed to dock
18813 ImGuiDockNodeFlags src_node_flags = payload_node ? payload_node->MergedFlags : payload_window->WindowClass.DockNodeFlagsOverrideSet;
18814 ImGuiDockNodeFlags dst_node_flags = host_node ? host_node->MergedFlags : host_window->WindowClass.DockNodeFlagsOverrideSet;
18815 data->IsCenterAvailable = true;
18816 if (is_outer_docking)
18817 data->IsCenterAvailable = false;
18818 else if (dst_node_flags & ImGuiDockNodeFlags_NoDockingOverMe)
18819 data->IsCenterAvailable = false;
18820 else if (host_node && (dst_node_flags & ImGuiDockNodeFlags_NoDockingOverCentralNode) && host_node->IsCentralNode())
18821 data->IsCenterAvailable = false;
18822 else if ((!host_node || !host_node->IsEmpty()) && payload_node && payload_node->IsSplitNode() && (payload_node->OnlyNodeWithWindows == NULL)) // Is _visibly_ split?
18823 data->IsCenterAvailable = false;
18824 else if ((src_node_flags & ImGuiDockNodeFlags_NoDockingOverOther) && (!host_node || !host_node->IsEmpty()))
18825 data->IsCenterAvailable = false;
18826 else if ((src_node_flags & ImGuiDockNodeFlags_NoDockingOverEmpty) && host_node && host_node->IsEmpty())
18827 data->IsCenterAvailable = false;
18828
18829 data->IsSidesAvailable = true;
18830 if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingSplit) || g.IO.ConfigDockingNoSplit)
18831 data->IsSidesAvailable = false;
18832 else if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode())
18833 data->IsSidesAvailable = false;
18834 else if (src_node_flags & ImGuiDockNodeFlags_NoDockingSplitOther)
18835 data->IsSidesAvailable = false;
18836
18837 // Build a tentative future node (reuse same structure because it is practical. Shape will be readjusted when previewing a split)
18838 data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (payload_window->HasCloseButton);
18839 data->FutureNode.HasWindowMenuButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0);
18840 data->FutureNode.Pos = ref_node_for_rect ? ref_node_for_rect->Pos : host_window->Pos;
18841 data->FutureNode.Size = ref_node_for_rect ? ref_node_for_rect->Size : host_window->Size;
18842
18843 // Calculate drop shapes geometry for allowed splitting directions
18844 IM_ASSERT(ImGuiDir_None == -1);
18845 data->SplitNode = host_node;
18846 data->SplitDir = ImGuiDir_None;
18847 data->IsSplitDirExplicit = false;
18848 if (!host_window->Collapsed)
18849 for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++)
18850 {
18851 if (dir == ImGuiDir_None && !data->IsCenterAvailable)
18852 continue;
18853 if (dir != ImGuiDir_None && !data->IsSidesAvailable)
18854 continue;
18855 if (DockNodeCalcDropRectsAndTestMousePos(data->FutureNode.Rect(), (ImGuiDir)dir, data->DropRectsDraw[dir+1], is_outer_docking, &g.IO.MousePos))
18856 {
18857 data->SplitDir = (ImGuiDir)dir;
18858 data->IsSplitDirExplicit = true;
18859 }
18860 }
18861
18862 // When docking without holding Shift, we only allow and preview docking when hovering over a drop rect or over the title bar
18863 data->IsDropAllowed = (data->SplitDir != ImGuiDir_None) || (data->IsCenterAvailable);
18864 if (!is_explicit_target && !data->IsSplitDirExplicit && !g.IO.ConfigDockingWithShift)
18865 data->IsDropAllowed = false;
18866
18867 // Calculate split area
18868 data->SplitRatio = 0.0f;
18869 if (data->SplitDir != ImGuiDir_None)
18870 {
18871 ImGuiDir split_dir = data->SplitDir;
18872 ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
18873 ImVec2 pos_new, pos_old = data->FutureNode.Pos;
18874 ImVec2 size_new, size_old = data->FutureNode.Size;
18875 DockNodeCalcSplitRects(pos_old, size_old, pos_new, size_new, split_dir, payload_window->Size);
18876
18877 // Calculate split ratio so we can pass it down the docking request
18878 float split_ratio = ImSaturate(size_new[split_axis] / data->FutureNode.Size[split_axis]);
18879 data->FutureNode.Pos = pos_new;
18880 data->FutureNode.Size = size_new;
18881 data->SplitRatio = (split_dir == ImGuiDir_Right || split_dir == ImGuiDir_Down) ? (1.0f - split_ratio) : (split_ratio);
18882 }
18883}
18884
18885static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, const ImGuiDockPreviewData* data)
18886{
18887 ImGuiContext& g = *GImGui;
18888 IM_ASSERT(g.CurrentWindow == host_window); // Because we rely on font size to calculate tab sizes
18889
18890 // With this option, we only display the preview on the target viewport, and the payload viewport is made transparent.
18891 // To compensate for the single layer obstructed by the payload, we'll increase the alpha of the preview nodes.
18892 const bool is_transparent_payload = g.IO.ConfigDockingTransparentPayload;
18893
18894 // In case the two windows involved are on different viewports, we will draw the overlay on each of them.
18895 int overlay_draw_lists_count = 0;
18896 ImDrawList* overlay_draw_lists[2];
18897 overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(host_window->Viewport);
18898 if (host_window->Viewport != root_payload->Viewport && !is_transparent_payload)
18899 overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(root_payload->Viewport);
18900
18901 // Draw main preview rectangle
18902 const ImU32 overlay_col_main = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.60f : 0.40f);
18903 const ImU32 overlay_col_drop = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.90f : 0.70f);
18904 const ImU32 overlay_col_drop_hovered = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 1.20f : 1.00f);
18905 const ImU32 overlay_col_lines = GetColorU32(ImGuiCol_NavWindowingHighlight, is_transparent_payload ? 0.80f : 0.60f);
18906
18907 // Display area preview
18908 const bool can_preview_tabs = (root_payload->DockNodeAsHost == NULL || root_payload->DockNodeAsHost->Windows.Size > 0);
18909 if (data->IsDropAllowed)
18910 {
18911 ImRect overlay_rect = data->FutureNode.Rect();
18912 if (data->SplitDir == ImGuiDir_None && can_preview_tabs)
18913 overlay_rect.Min.y += GetFrameHeight();
18914 if (data->SplitDir != ImGuiDir_None || data->IsCenterAvailable)
18915 for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
18916 overlay_draw_lists[overlay_n]->AddRectFilled(overlay_rect.Min, overlay_rect.Max, overlay_col_main, host_window->WindowRounding, CalcRoundingFlagsForRectInRect(overlay_rect, host_window->Rect(), g.Style.DockingSeparatorSize));
18917 }
18918
18919 // Display tab shape/label preview unless we are splitting node (it generally makes the situation harder to read)
18920 if (data->IsDropAllowed && can_preview_tabs && data->SplitDir == ImGuiDir_None && data->IsCenterAvailable)
18921 {
18922 // Compute target tab bar geometry so we can locate our preview tabs
18923 ImRect tab_bar_rect;
18924 DockNodeCalcTabBarLayout(&data->FutureNode, NULL, &tab_bar_rect, NULL, NULL);
18925 ImVec2 tab_pos = tab_bar_rect.Min;
18926 if (host_node && host_node->TabBar)
18927 {
18928 if (!host_node->IsHiddenTabBar() && !host_node->IsNoTabBar())
18929 tab_pos.x += host_node->TabBar->WidthAllTabs + g.Style.ItemInnerSpacing.x; // We don't use OffsetNewTab because when using non-persistent-order tab bar it is incremented with each Tab submission.
18930 else
18931 tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_node->Windows[0]).x;
18932 }
18933 else if (!(host_window->Flags & ImGuiWindowFlags_DockNodeHost))
18934 {
18935 tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_window).x; // Account for slight offset which will be added when changing from title bar to tab bar
18936 }
18937
18938 // Draw tab shape/label preview (payload may be a loose window or a host window carrying multiple tabbed windows)
18939 if (root_payload->DockNodeAsHost)
18940 IM_ASSERT(root_payload->DockNodeAsHost->Windows.Size <= root_payload->DockNodeAsHost->TabBar->Tabs.Size);
18941 ImGuiTabBar* tab_bar_with_payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar : NULL;
18942 const int payload_count = tab_bar_with_payload ? tab_bar_with_payload->Tabs.Size : 1;
18943 for (int payload_n = 0; payload_n < payload_count; payload_n++)
18944 {
18945 // DockNode's TabBar may have non-window Tabs manually appended by user
18946 ImGuiWindow* payload_window = tab_bar_with_payload ? tab_bar_with_payload->Tabs[payload_n].Window : root_payload;
18947 if (tab_bar_with_payload && payload_window == NULL)
18948 continue;
18949 if (!DockNodeIsDropAllowedOne(payload_window, host_window))
18950 continue;
18951
18952 // Calculate the tab bounding box for each payload window
18953 ImVec2 tab_size = TabItemCalcSize(payload_window);
18954 ImRect tab_bb(tab_pos.x, tab_pos.y, tab_pos.x + tab_size.x, tab_pos.y + tab_size.y);
18955 tab_pos.x += tab_size.x + g.Style.ItemInnerSpacing.x;
18956 const ImU32 overlay_col_text = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_Text]);
18957 const ImU32 overlay_col_tabs = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_TabSelected]);
18958 PushStyleColor(ImGuiCol_Text, overlay_col_text);
18959 for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
18960 {
18961 ImGuiTabItemFlags tab_flags = (payload_window->Flags & ImGuiWindowFlags_UnsavedDocument) ? ImGuiTabItemFlags_UnsavedDocument : 0;
18962 if (!tab_bar_rect.Contains(tab_bb))
18963 overlay_draw_lists[overlay_n]->PushClipRect(tab_bar_rect.Min, tab_bar_rect.Max);
18964 TabItemBackground(overlay_draw_lists[overlay_n], tab_bb, tab_flags, overlay_col_tabs);
18965 TabItemLabelAndCloseButton(overlay_draw_lists[overlay_n], tab_bb, tab_flags, g.Style.FramePadding, payload_window->Name, 0, 0, false, NULL, NULL);
18966 if (!tab_bar_rect.Contains(tab_bb))
18967 overlay_draw_lists[overlay_n]->PopClipRect();
18968 }
18969 PopStyleColor();
18970 }
18971 }
18972
18973 // Display drop boxes
18974 const float overlay_rounding = ImMax(3.0f, g.Style.FrameRounding);
18975 for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++)
18976 {
18977 if (!data->DropRectsDraw[dir + 1].IsInverted())
18978 {
18979 ImRect draw_r = data->DropRectsDraw[dir + 1];
18980 ImRect draw_r_in = draw_r;
18981 draw_r_in.Expand(-2.0f);
18982 ImU32 overlay_col = (data->SplitDir == (ImGuiDir)dir && data->IsSplitDirExplicit) ? overlay_col_drop_hovered : overlay_col_drop;
18983 for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
18984 {
18985 ImVec2 center = ImFloor(draw_r_in.GetCenter());
18986 overlay_draw_lists[overlay_n]->AddRectFilled(draw_r.Min, draw_r.Max, overlay_col, overlay_rounding);
18987 overlay_draw_lists[overlay_n]->AddRect(draw_r_in.Min, draw_r_in.Max, overlay_col_lines, overlay_rounding);
18988 if (dir == ImGuiDir_Left || dir == ImGuiDir_Right)
18989 overlay_draw_lists[overlay_n]->AddLine(ImVec2(center.x, draw_r_in.Min.y), ImVec2(center.x, draw_r_in.Max.y), overlay_col_lines);
18990 if (dir == ImGuiDir_Up || dir == ImGuiDir_Down)
18991 overlay_draw_lists[overlay_n]->AddLine(ImVec2(draw_r_in.Min.x, center.y), ImVec2(draw_r_in.Max.x, center.y), overlay_col_lines);
18992 }
18993 }
18994
18995 // Stop after ImGuiDir_None
18996 if ((host_node && (host_node->MergedFlags & ImGuiDockNodeFlags_NoDockingSplit)) || g.IO.ConfigDockingNoSplit)
18997 return;
18998 }
18999}
19000
19001//-----------------------------------------------------------------------------
19002// Docking: ImGuiDockNode Tree manipulation functions
19003//-----------------------------------------------------------------------------
19004// - DockNodeTreeSplit()
19005// - DockNodeTreeMerge()
19006// - DockNodeTreeUpdatePosSize()
19007// - DockNodeTreeUpdateSplitterFindTouchingNode()
19008// - DockNodeTreeUpdateSplitter()
19009// - DockNodeTreeFindFallbackLeafNode()
19010// - DockNodeTreeFindNodeByPos()
19011//-----------------------------------------------------------------------------
19012
19013void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_inheritor_child_idx, float split_ratio, ImGuiDockNode* new_node)
19014{
19015 ImGuiContext& g = *GImGui;
19016 IM_ASSERT(split_axis != ImGuiAxis_None);
19017
19018 ImGuiDockNode* child_0 = (new_node && split_inheritor_child_idx != 0) ? new_node : DockContextAddNode(ctx, 0);
19019 child_0->ParentNode = parent_node;
19020
19021 ImGuiDockNode* child_1 = (new_node && split_inheritor_child_idx != 1) ? new_node : DockContextAddNode(ctx, 0);
19022 child_1->ParentNode = parent_node;
19023
19024 ImGuiDockNode* child_inheritor = (split_inheritor_child_idx == 0) ? child_0 : child_1;
19025 DockNodeMoveChildNodes(child_inheritor, parent_node);
19026 parent_node->ChildNodes[0] = child_0;
19027 parent_node->ChildNodes[1] = child_1;
19028 parent_node->ChildNodes[split_inheritor_child_idx]->VisibleWindow = parent_node->VisibleWindow;
19029 parent_node->SplitAxis = split_axis;
19030 parent_node->VisibleWindow = NULL;
19031 parent_node->AuthorityForPos = parent_node->AuthorityForSize = ImGuiDataAuthority_DockNode;
19032
19033 float size_avail = (parent_node->Size[split_axis] - g.Style.DockingSeparatorSize);
19034 size_avail = ImMax(size_avail, g.Style.WindowMinSize[split_axis] * 2.0f);
19035 IM_ASSERT(size_avail > 0.0f); // If you created a node manually with DockBuilderAddNode(), you need to also call DockBuilderSetNodeSize() before splitting.
19036 child_0->SizeRef = child_1->SizeRef = parent_node->Size;
19037 child_0->SizeRef[split_axis] = ImTrunc(size_avail * split_ratio);
19038 child_1->SizeRef[split_axis] = ImTrunc(size_avail - child_0->SizeRef[split_axis]);
19039
19040 DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node);
19041 DockSettingsRenameNodeReferences(parent_node->ID, parent_node->ChildNodes[split_inheritor_child_idx]->ID);
19042 DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(parent_node));
19043 DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size);
19044
19045 // Flags transfer (e.g. this is where we transfer the ImGuiDockNodeFlags_CentralNode property)
19046 child_0->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
19047 child_1->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
19048 child_inheritor->LocalFlags = parent_node->LocalFlags & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
19049 parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_;
19050 child_0->UpdateMergedFlags();
19051 child_1->UpdateMergedFlags();
19052 parent_node->UpdateMergedFlags();
19053 if (child_inheritor->IsCentralNode())
19054 DockNodeGetRootNode(parent_node)->CentralNode = child_inheritor;
19055}
19056
19057void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child)
19058{
19059 // When called from DockContextProcessUndockNode() it is possible that one of the child is NULL.
19060 ImGuiContext& g = *GImGui;
19061 ImGuiDockNode* child_0 = parent_node->ChildNodes[0];
19062 ImGuiDockNode* child_1 = parent_node->ChildNodes[1];
19063 IM_ASSERT(child_0 || child_1);
19064 IM_ASSERT(merge_lead_child == child_0 || merge_lead_child == child_1);
19065 if ((child_0 && child_0->Windows.Size > 0) || (child_1 && child_1->Windows.Size > 0))
19066 {
19067 IM_ASSERT(parent_node->TabBar == NULL);
19068 IM_ASSERT(parent_node->Windows.Size == 0);
19069 }
19070 IMGUI_DEBUG_LOG_DOCKING("[docking] DockNodeTreeMerge: 0x%08X + 0x%08X back into parent 0x%08X\n", child_0 ? child_0->ID : 0, child_1 ? child_1->ID : 0, parent_node->ID);
19071
19072 ImVec2 backup_last_explicit_size = parent_node->SizeRef;
19073 DockNodeMoveChildNodes(parent_node, merge_lead_child);
19074 if (child_0)
19075 {
19076 DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows
19077 DockSettingsRenameNodeReferences(child_0->ID, parent_node->ID);
19078 }
19079 if (child_1)
19080 {
19081 DockNodeMoveWindows(parent_node, child_1);
19082 DockSettingsRenameNodeReferences(child_1->ID, parent_node->ID);
19083 }
19084 DockNodeApplyPosSizeToWindows(parent_node);
19085 parent_node->AuthorityForPos = parent_node->AuthorityForSize = parent_node->AuthorityForViewport = ImGuiDataAuthority_Auto;
19086 parent_node->VisibleWindow = merge_lead_child->VisibleWindow;
19087 parent_node->SizeRef = backup_last_explicit_size;
19088
19089 // Flags transfer
19090 parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; // Preserve Dockspace flag
19091 parent_node->LocalFlags |= (child_0 ? child_0->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
19092 parent_node->LocalFlags |= (child_1 ? child_1->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
19093 parent_node->LocalFlagsInWindows = (child_0 ? child_0->LocalFlagsInWindows : 0) | (child_1 ? child_1->LocalFlagsInWindows : 0); // FIXME: Would be more consistent to update from actual windows
19094 parent_node->UpdateMergedFlags();
19095
19096 if (child_0)
19097 {
19098 ctx->DockContext.Nodes.SetVoidPtr(child_0->ID, NULL);
19099 IM_DELETE(child_0);
19100 }
19101 if (child_1)
19102 {
19103 ctx->DockContext.Nodes.SetVoidPtr(child_1->ID, NULL);
19104 IM_DELETE(child_1);
19105 }
19106}
19107
19108// Update Pos/Size for a node hierarchy (don't affect child Windows yet)
19109// (Depth-first, Pre-Order)
19110void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, ImGuiDockNode* only_write_to_single_node)
19111{
19112 // During the regular dock node update we write to all nodes.
19113 // 'only_write_to_single_node' is only set when turning a node visible mid-frame and we need its size right-away.
19114 ImGuiContext& g = *GImGui;
19115 const bool write_to_node = only_write_to_single_node == NULL || only_write_to_single_node == node;
19116 if (write_to_node)
19117 {
19118 node->Pos = pos;
19119 node->Size = size;
19120 }
19121
19122 if (node->IsLeafNode())
19123 return;
19124
19125 ImGuiDockNode* child_0 = node->ChildNodes[0];
19126 ImGuiDockNode* child_1 = node->ChildNodes[1];
19127 ImVec2 child_0_pos = pos, child_1_pos = pos;
19128 ImVec2 child_0_size = size, child_1_size = size;
19129
19130 const bool child_0_is_toward_single_node = (only_write_to_single_node != NULL && DockNodeIsInHierarchyOf(only_write_to_single_node, child_0));
19131 const bool child_1_is_toward_single_node = (only_write_to_single_node != NULL && DockNodeIsInHierarchyOf(only_write_to_single_node, child_1));
19132 const bool child_0_is_or_will_be_visible = child_0->IsVisible || child_0_is_toward_single_node;
19133 const bool child_1_is_or_will_be_visible = child_1->IsVisible || child_1_is_toward_single_node;
19134
19135 if (child_0_is_or_will_be_visible && child_1_is_or_will_be_visible)
19136 {
19137 const float spacing = g.Style.DockingSeparatorSize;
19138 const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis;
19139 const float size_avail = ImMax(size[axis] - spacing, 0.0f);
19140
19141 // Size allocation policy
19142 // 1) The first 0..WindowMinSize[axis]*2 are allocated evenly to both windows.
19143 const float size_min_each = ImTrunc(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f);
19144
19145 // FIXME: Blocks 2) and 3) are essentially doing nearly the same thing.
19146 // Difference are: write-back to SizeRef; application of a minimum size; rounding before ImTrunc()
19147 // Clarify and rework differences between Size & SizeRef and purpose of WantLockSizeOnce
19148
19149 // 2) Process locked absolute size (during a splitter resize we preserve the child of nodes not touching the splitter edge)
19150 if (child_0->WantLockSizeOnce && !child_1->WantLockSizeOnce)
19151 {
19152 child_0_size[axis] = child_0->SizeRef[axis] = ImMin(size_avail - 1.0f, child_0->Size[axis]);
19153 child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]);
19154 IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
19155 }
19156 else if (child_1->WantLockSizeOnce && !child_0->WantLockSizeOnce)
19157 {
19158 child_1_size[axis] = child_1->SizeRef[axis] = ImMin(size_avail - 1.0f, child_1->Size[axis]);
19159 child_0_size[axis] = child_0->SizeRef[axis] = (size_avail - child_1_size[axis]);
19160 IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
19161 }
19162 else if (child_0->WantLockSizeOnce && child_1->WantLockSizeOnce)
19163 {
19164 // FIXME-DOCK: We cannot honor the requested size, so apply ratio.
19165 // Currently this path will only be taken if code programmatically sets WantLockSizeOnce
19166 float split_ratio = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]);
19167 child_0_size[axis] = child_0->SizeRef[axis] = ImTrunc(size_avail * split_ratio);
19168 child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]);
19169 IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
19170 }
19171
19172 // 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node
19173 else if (child_0->SizeRef[axis] != 0.0f && child_1->HasCentralNodeChild)
19174 {
19175 child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]);
19176 child_1_size[axis] = (size_avail - child_0_size[axis]);
19177 }
19178 else if (child_1->SizeRef[axis] != 0.0f && child_0->HasCentralNodeChild)
19179 {
19180 child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]);
19181 child_0_size[axis] = (size_avail - child_1_size[axis]);
19182 }
19183 else
19184 {
19185 // 4) Otherwise distribute according to the relative ratio of each SizeRef value
19186 float split_ratio = child_0->SizeRef[axis] / (child_0->SizeRef[axis] + child_1->SizeRef[axis]);
19187 child_0_size[axis] = ImMax(size_min_each, ImTrunc(size_avail * split_ratio + 0.5f));
19188 child_1_size[axis] = (size_avail - child_0_size[axis]);
19189 }
19190
19191 child_1_pos[axis] += spacing + child_0_size[axis];
19192 }
19193
19194 if (only_write_to_single_node == NULL)
19195 child_0->WantLockSizeOnce = child_1->WantLockSizeOnce = false;
19196
19197 const bool child_0_recurse = only_write_to_single_node ? child_0_is_toward_single_node : child_0->IsVisible;
19198 const bool child_1_recurse = only_write_to_single_node ? child_1_is_toward_single_node : child_1->IsVisible;
19199 if (child_0_recurse)
19200 DockNodeTreeUpdatePosSize(child_0, child_0_pos, child_0_size);
19201 if (child_1_recurse)
19202 DockNodeTreeUpdatePosSize(child_1, child_1_pos, child_1_size);
19203}
19204
19205static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGuiAxis axis, int side, ImVector<ImGuiDockNode*>* touching_nodes)
19206{
19207 if (node->IsLeafNode())
19208 {
19209 touching_nodes->push_back(node);
19210 return;
19211 }
19212 if (node->ChildNodes[0]->IsVisible)
19213 if (node->SplitAxis != axis || side == 0 || !node->ChildNodes[1]->IsVisible)
19214 DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[0], axis, side, touching_nodes);
19215 if (node->ChildNodes[1]->IsVisible)
19216 if (node->SplitAxis != axis || side == 1 || !node->ChildNodes[0]->IsVisible)
19217 DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[1], axis, side, touching_nodes);
19218}
19219
19220// (Depth-First, Pre-Order)
19221void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node)
19222{
19223 if (node->IsLeafNode())
19224 return;
19225
19226 ImGuiContext& g = *GImGui;
19227
19228 ImGuiDockNode* child_0 = node->ChildNodes[0];
19229 ImGuiDockNode* child_1 = node->ChildNodes[1];
19230 if (child_0->IsVisible && child_1->IsVisible)
19231 {
19232 // Bounding box of the splitter cover the space between both nodes (w = Spacing, h = Size[xy^1] for when splitting horizontally)
19233 const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis;
19234 IM_ASSERT(axis != ImGuiAxis_None);
19235 ImRect bb;
19236 bb.Min = child_0->Pos;
19237 bb.Max = child_1->Pos;
19238 bb.Min[axis] += child_0->Size[axis];
19239 bb.Max[axis ^ 1] += child_1->Size[axis ^ 1];
19240 //if (g.IO.KeyCtrl) GetForegroundDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255));
19241
19242 const ImGuiDockNodeFlags merged_flags = child_0->MergedFlags | child_1->MergedFlags; // Merged flags for BOTH childs
19243 const ImGuiDockNodeFlags no_resize_axis_flag = (axis == ImGuiAxis_X) ? ImGuiDockNodeFlags_NoResizeX : ImGuiDockNodeFlags_NoResizeY;
19244 if ((merged_flags & ImGuiDockNodeFlags_NoResize) || (merged_flags & no_resize_axis_flag))
19245 {
19246 ImGuiWindow* window = g.CurrentWindow;
19247 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator), g.Style.FrameRounding);
19248 }
19249 else
19250 {
19251 //bb.Min[axis] += 1; // Display a little inward so highlight doesn't connect with nearby tabs on the neighbor node.
19252 //bb.Max[axis] -= 1;
19253 PushID(node->ID);
19254
19255 // Find resizing limits by gathering list of nodes that are touching the splitter line.
19256 ImVector<ImGuiDockNode*> touching_nodes[2];
19257 float min_size = g.Style.WindowMinSize[axis];
19258 float resize_limits[2];
19259 resize_limits[0] = node->ChildNodes[0]->Pos[axis] + min_size;
19260 resize_limits[1] = node->ChildNodes[1]->Pos[axis] + node->ChildNodes[1]->Size[axis] - min_size;
19261
19262 ImGuiID splitter_id = GetID("##Splitter");
19263 if (g.ActiveId == splitter_id) // Only process when splitter is active
19264 {
19265 DockNodeTreeUpdateSplitterFindTouchingNode(child_0, axis, 1, &touching_nodes[0]);
19266 DockNodeTreeUpdateSplitterFindTouchingNode(child_1, axis, 0, &touching_nodes[1]);
19267 for (int touching_node_n = 0; touching_node_n < touching_nodes[0].Size; touching_node_n++)
19268 resize_limits[0] = ImMax(resize_limits[0], touching_nodes[0][touching_node_n]->Rect().Min[axis] + min_size);
19269 for (int touching_node_n = 0; touching_node_n < touching_nodes[1].Size; touching_node_n++)
19270 resize_limits[1] = ImMin(resize_limits[1], touching_nodes[1][touching_node_n]->Rect().Max[axis] - min_size);
19271
19272 // [DEBUG] Render touching nodes & limits
19273 /*
19274 ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport());
19275 for (int n = 0; n < 2; n++)
19276 {
19277 for (int touching_node_n = 0; touching_node_n < touching_nodes[n].Size; touching_node_n++)
19278 draw_list->AddRect(touching_nodes[n][touching_node_n]->Pos, touching_nodes[n][touching_node_n]->Pos + touching_nodes[n][touching_node_n]->Size, IM_COL32(0, 255, 0, 255));
19279 if (axis == ImGuiAxis_X)
19280 draw_list->AddLine(ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y), ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y + node->ChildNodes[n]->Size.y), IM_COL32(255, 0, 255, 255), 3.0f);
19281 else
19282 draw_list->AddLine(ImVec2(node->ChildNodes[n]->Pos.x, resize_limits[n]), ImVec2(node->ChildNodes[n]->Pos.x + node->ChildNodes[n]->Size.x, resize_limits[n]), IM_COL32(255, 0, 255, 255), 3.0f);
19283 }
19284 */
19285 }
19286
19287 // Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters
19288 float cur_size_0 = child_0->Size[axis];
19289 float cur_size_1 = child_1->Size[axis];
19290 float min_size_0 = resize_limits[0] - child_0->Pos[axis];
19291 float min_size_1 = child_1->Pos[axis] + child_1->Size[axis] - resize_limits[1];
19292 ImU32 bg_col = GetColorU32(ImGuiCol_WindowBg);
19293 if (SplitterBehavior(bb, GetID("##Splitter"), axis, &cur_size_0, &cur_size_1, min_size_0, min_size_1, WINDOWS_HOVER_PADDING, WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER, bg_col))
19294 {
19295 if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0)
19296 {
19297 child_0->Size[axis] = child_0->SizeRef[axis] = cur_size_0;
19298 child_1->Pos[axis] -= cur_size_1 - child_1->Size[axis];
19299 child_1->Size[axis] = child_1->SizeRef[axis] = cur_size_1;
19300
19301 // Lock the size of every node that is a sibling of the node we are touching
19302 // This might be less desirable if we can merge sibling of a same axis into the same parental level.
19303 for (int side_n = 0; side_n < 2; side_n++)
19304 for (int touching_node_n = 0; touching_node_n < touching_nodes[side_n].Size; touching_node_n++)
19305 {
19306 ImGuiDockNode* touching_node = touching_nodes[side_n][touching_node_n];
19307 //ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport());
19308 //draw_list->AddRect(touching_node->Pos, touching_node->Pos + touching_node->Size, IM_COL32(255, 128, 0, 255));
19309 while (touching_node->ParentNode != node)
19310 {
19311 if (touching_node->ParentNode->SplitAxis == axis)
19312 {
19313 // Mark other node so its size will be preserved during the upcoming call to DockNodeTreeUpdatePosSize().
19314 ImGuiDockNode* node_to_preserve = touching_node->ParentNode->ChildNodes[side_n];
19315 node_to_preserve->WantLockSizeOnce = true;
19316 //draw_list->AddRect(touching_node->Pos, touching_node->Rect().Max, IM_COL32(255, 0, 0, 255));
19317 //draw_list->AddRectFilled(node_to_preserve->Pos, node_to_preserve->Rect().Max, IM_COL32(0, 255, 0, 100));
19318 }
19319 touching_node = touching_node->ParentNode;
19320 }
19321 }
19322
19323 DockNodeTreeUpdatePosSize(child_0, child_0->Pos, child_0->Size);
19324 DockNodeTreeUpdatePosSize(child_1, child_1->Pos, child_1->Size);
19325 MarkIniSettingsDirty();
19326 }
19327 }
19328 PopID();
19329 }
19330 }
19331
19332 if (child_0->IsVisible)
19333 DockNodeTreeUpdateSplitter(child_0);
19334 if (child_1->IsVisible)
19335 DockNodeTreeUpdateSplitter(child_1);
19336}
19337
19338ImGuiDockNode* ImGui::DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node)
19339{
19340 if (node->IsLeafNode())
19341 return node;
19342 if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[0]))
19343 return leaf_node;
19344 if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[1]))
19345 return leaf_node;
19346 return NULL;
19347}
19348
19349ImGuiDockNode* ImGui::DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos)
19350{
19351 if (!node->IsVisible)
19352 return NULL;
19353
19354 const float dock_spacing = 0.0f;// g.Style.ItemInnerSpacing.x; // FIXME: Relation to DOCKING_SPLITTER_SIZE?
19355 ImRect r(node->Pos, node->Pos + node->Size);
19356 r.Expand(dock_spacing * 0.5f);
19357 bool inside = r.Contains(pos);
19358 if (!inside)
19359 return NULL;
19360
19361 if (node->IsLeafNode())
19362 return node;
19363 if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[0], pos))
19364 return hovered_node;
19365 if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[1], pos))
19366 return hovered_node;
19367
19368 // This means we are hovering over the splitter/spacing of a parent node
19369 return node;
19370}
19371
19372//-----------------------------------------------------------------------------
19373// Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport)
19374//-----------------------------------------------------------------------------
19375// - SetWindowDock() [Internal]
19376// - DockSpace()
19377// - DockSpaceOverViewport()
19378//-----------------------------------------------------------------------------
19379
19380// [Internal] Called via SetNextWindowDockID()
19381void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond)
19382{
19383 // Test condition (NB: bit 0 is always true) and clear flags for next time
19384 if (cond && (window->SetWindowDockAllowFlags & cond) == 0)
19385 return;
19386 window->SetWindowDockAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
19387
19388 if (window->DockId == dock_id)
19389 return;
19390
19391 // If the user attempt to set a dock id that is a split node, we'll dig within to find a suitable docking spot
19392 ImGuiContext& g = *GImGui;
19393 if (ImGuiDockNode* new_node = DockContextFindNodeByID(&g, dock_id))
19394 if (new_node->IsSplitNode())
19395 {
19396 // Policy: Find central node or latest focused node. We first move back to our root node.
19397 new_node = DockNodeGetRootNode(new_node);
19398 if (new_node->CentralNode)
19399 {
19400 IM_ASSERT(new_node->CentralNode->IsCentralNode());
19401 dock_id = new_node->CentralNode->ID;
19402 }
19403 else
19404 {
19405 dock_id = new_node->LastFocusedNodeId;
19406 }
19407 }
19408
19409 if (window->DockId == dock_id)
19410 return;
19411
19412 if (window->DockNode)
19413 DockNodeRemoveWindow(window->DockNode, window, 0);
19414 window->DockId = dock_id;
19415}
19416
19417// Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a CentralNode by default.
19418// The Central Node is always displayed even when empty and shrink/extend according to the requested size of its neighbors.
19419// DockSpace() needs to be submitted _before_ any window they can host. If you use a dockspace, submit it early in your app.
19420// When ImGuiDockNodeFlags_KeepAliveOnly is set, nothing is submitted in the current window (function may be called from any location).
19421ImGuiID ImGui::DockSpace(ImGuiID dockspace_id, const ImVec2& size_arg, ImGuiDockNodeFlags flags, const ImGuiWindowClass* window_class)
19422{
19423 ImGuiContext& g = *GImGui;
19424 ImGuiWindow* window = GetCurrentWindowRead();
19425 if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
19426 return 0;
19427
19428 // Early out if parent window is hidden/collapsed
19429 // This is faster but also DockNodeUpdateTabBar() relies on TabBarLayout() running (which won't if SkipItems=true) to set NextSelectedTabId = 0). See #2960.
19430 // If for whichever reason this is causing problem we would need to ensure that DockNodeUpdateTabBar() ends up clearing NextSelectedTabId even if SkipItems=true.
19431 if (window->SkipItems)
19432 flags |= ImGuiDockNodeFlags_KeepAliveOnly;
19433 if ((flags & ImGuiDockNodeFlags_KeepAliveOnly) == 0)
19434 window = GetCurrentWindow(); // call to set window->WriteAccessed = true;
19435
19436 IM_ASSERT((flags & ImGuiDockNodeFlags_DockSpace) == 0);
19437 IM_ASSERT(dockspace_id != 0);
19438 ImGuiDockNode* node = DockContextFindNodeByID(&g, dockspace_id);
19439 if (node == NULL)
19440 {
19441 IMGUI_DEBUG_LOG_DOCKING("[docking] DockSpace: dockspace node 0x%08X created\n", dockspace_id);
19442 node = DockContextAddNode(&g, dockspace_id);
19443 node->SetLocalFlags(ImGuiDockNodeFlags_CentralNode);
19444 }
19445 if (window_class && window_class->ClassId != node->WindowClass.ClassId)
19446 IMGUI_DEBUG_LOG_DOCKING("[docking] DockSpace: dockspace node 0x%08X: setup WindowClass 0x%08X -> 0x%08X\n", dockspace_id, node->WindowClass.ClassId, window_class->ClassId);
19447 node->SharedFlags = flags;
19448 node->WindowClass = window_class ? *window_class : ImGuiWindowClass();
19449
19450 // When a DockSpace transitioned form implicit to explicit this may be called a second time
19451 // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace() node, so we overwrite IsDockSpace again.
19452 if (node->LastFrameActive == g.FrameCount && !(flags & ImGuiDockNodeFlags_KeepAliveOnly))
19453 {
19454 IM_ASSERT(node->IsDockSpace() == false && "Cannot call DockSpace() twice a frame with the same ID");
19455 node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_DockSpace);
19456 return dockspace_id;
19457 }
19458 node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_DockSpace);
19459
19460 // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible
19461 if (flags & ImGuiDockNodeFlags_KeepAliveOnly)
19462 {
19463 node->LastFrameAlive = g.FrameCount;
19464 return dockspace_id;
19465 }
19466
19467 const ImVec2 content_avail = GetContentRegionAvail();
19468 ImVec2 size = ImTrunc(size_arg);
19469 if (size.x <= 0.0f)
19470 size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
19471 if (size.y <= 0.0f)
19472 size.y = ImMax(content_avail.y + size.y, 4.0f);
19473 IM_ASSERT(size.x > 0.0f && size.y > 0.0f);
19474
19475 node->Pos = window->DC.CursorPos;
19476 node->Size = node->SizeRef = size;
19477 SetNextWindowPos(node->Pos);
19478 SetNextWindowSize(node->Size);
19479 g.NextWindowData.PosUndock = false;
19480
19481 // FIXME-DOCK: Why do we need a child window to host a dockspace, could we host it in the existing window?
19482 // FIXME-DOCK: What is the reason for not simply calling BeginChild()? (OK to have a reason but should be commented)
19483 ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_DockNodeHost;
19484 window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar;
19485 window_flags |= ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
19486 window_flags |= ImGuiWindowFlags_NoBackground;
19487
19488 char title[256];
19489 ImFormatString(title, IM_ARRAYSIZE(title), "%s/DockSpace_%08X", window->Name, dockspace_id);
19490
19491 PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f);
19492 Begin(title, NULL, window_flags);
19493 PopStyleVar();
19494
19495 ImGuiWindow* host_window = g.CurrentWindow;
19496 DockNodeSetupHostWindow(node, host_window);
19497 host_window->ChildId = window->GetID(title);
19498 node->OnlyNodeWithWindows = NULL;
19499
19500 IM_ASSERT(node->IsRootNode());
19501
19502 // We need to handle the rare case were a central node is missing.
19503 // This can happen if the node was first created manually with DockBuilderAddNode() but _without_ the ImGuiDockNodeFlags_Dockspace.
19504 // Doing it correctly would set the _CentralNode flags, which would then propagate according to subsequent split.
19505 // It would also be ambiguous to attempt to assign a central node while there are split nodes, so we wait until there's a single node remaining.
19506 // The specific sub-property of _CentralNode we are interested in recovering here is the "Don't delete when empty" property,
19507 // as it doesn't make sense for an empty dockspace to not have this property.
19508 if (node->IsLeafNode() && !node->IsCentralNode())
19509 node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_CentralNode);
19510
19511 // Update the node
19512 DockNodeUpdate(node);
19513
19514 End();
19515
19516 ImRect bb(node->Pos, node->Pos + size);
19517 ItemSize(size);
19518 ItemAdd(bb, dockspace_id, NULL, ImGuiItemFlags_NoNav); // Not a nav point (could be, would need to draw the nav rect and replicate/refactor activation from BeginChild(), but seems like CTRL+Tab works better here?)
19519 if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && IsWindowChildOf(g.HoveredWindow, host_window, false, true)) // To fullfill IsItemHovered(), similar to EndChild()
19520 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
19521
19522 return dockspace_id;
19523}
19524
19525// Tips: Use with ImGuiDockNodeFlags_PassthruCentralNode!
19526// The limitation with this call is that your window won't have a local menu bar, but you can also use BeginMainMenuBar().
19527// Even though we could pass window flags, it would also require the user to be able to call BeginMenuBar() somehow meaning we can't Begin/End in a single function.
19528// If you really want a menu bar inside the same window as the one hosting the dockspace, you will need to copy this code somewhere and tweak it.
19529ImGuiID ImGui::DockSpaceOverViewport(ImGuiID dockspace_id, const ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class)
19530{
19531 if (viewport == NULL)
19532 viewport = GetMainViewport();
19533
19534 // Submit a window filling the entire viewport
19535 SetNextWindowPos(viewport->WorkPos);
19536 SetNextWindowSize(viewport->WorkSize);
19537 SetNextWindowViewport(viewport->ID);
19538
19539 ImGuiWindowFlags host_window_flags = 0;
19540 host_window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking;
19541 host_window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
19542 if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
19543 host_window_flags |= ImGuiWindowFlags_NoBackground;
19544
19545 char label[32];
19546 ImFormatString(label, IM_ARRAYSIZE(label), "WindowOverViewport_%08X", viewport->ID);
19547
19548 PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
19549 PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
19550 PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
19551 Begin(label, NULL, host_window_flags);
19552 PopStyleVar(3);
19553
19554 // Submit the dockspace
19555 if (dockspace_id == 0)
19556 dockspace_id = GetID("DockSpace");
19557 DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, window_class);
19558
19559 End();
19560
19561 return dockspace_id;
19562}
19563
19564//-----------------------------------------------------------------------------
19565// Docking: Builder Functions
19566//-----------------------------------------------------------------------------
19567// Very early end-user API to manipulate dock nodes.
19568// Only available in imgui_internal.h. Expect this API to change/break!
19569// It is expected that those functions are all called _before_ the dockspace node submission.
19570//-----------------------------------------------------------------------------
19571// - DockBuilderDockWindow()
19572// - DockBuilderGetNode()
19573// - DockBuilderSetNodePos()
19574// - DockBuilderSetNodeSize()
19575// - DockBuilderAddNode()
19576// - DockBuilderRemoveNode()
19577// - DockBuilderRemoveNodeChildNodes()
19578// - DockBuilderRemoveNodeDockedWindows()
19579// - DockBuilderSplitNode()
19580// - DockBuilderCopyNodeRec()
19581// - DockBuilderCopyNode()
19582// - DockBuilderCopyWindowSettings()
19583// - DockBuilderCopyDockSpace()
19584// - DockBuilderFinish()
19585//-----------------------------------------------------------------------------
19586
19587void ImGui::DockBuilderDockWindow(const char* window_name, ImGuiID node_id)
19588{
19589 // We don't preserve relative order of multiple docked windows (by clearing DockOrder back to -1)
19590 ImGuiContext& g = *GImGui; IM_UNUSED(g);
19591 IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderDockWindow '%s' to node 0x%08X\n", window_name, node_id);
19592 ImGuiID window_id = ImHashStr(window_name);
19593 if (ImGuiWindow* window = FindWindowByID(window_id))
19594 {
19595 // Apply to created window
19596 ImGuiID prev_node_id = window->DockId;
19597 SetWindowDock(window, node_id, ImGuiCond_Always);
19598 if (window->DockId != prev_node_id)
19599 window->DockOrder = -1;
19600 }
19601 else
19602 {
19603 // Apply to settings
19604 ImGuiWindowSettings* settings = FindWindowSettingsByID(window_id);
19605 if (settings == NULL)
19606 settings = CreateNewWindowSettings(window_name);
19607 if (settings->DockId != node_id)
19608 settings->DockOrder = -1;
19609 settings->DockId = node_id;
19610 }
19611}
19612
19613ImGuiDockNode* ImGui::DockBuilderGetNode(ImGuiID node_id)
19614{
19615 ImGuiContext& g = *GImGui;
19616 return DockContextFindNodeByID(&g, node_id);
19617}
19618
19619void ImGui::DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos)
19620{
19621 ImGuiContext& g = *GImGui;
19622 ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id);
19623 if (node == NULL)
19624 return;
19625 node->Pos = pos;
19626 node->AuthorityForPos = ImGuiDataAuthority_DockNode;
19627}
19628
19629void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size)
19630{
19631 ImGuiContext& g = *GImGui;
19632 ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id);
19633 if (node == NULL)
19634 return;
19635 IM_ASSERT(size.x > 0.0f && size.y > 0.0f);
19636 node->Size = node->SizeRef = size;
19637 node->AuthorityForSize = ImGuiDataAuthority_DockNode;
19638}
19639
19640// Make sure to use the ImGuiDockNodeFlags_DockSpace flag to create a dockspace node! Otherwise this will create a floating node!
19641// - Floating node: you can then call DockBuilderSetNodePos()/DockBuilderSetNodeSize() to position and size the floating node.
19642// - Dockspace node: calling DockBuilderSetNodePos() is unnecessary.
19643// - If you intend to split a node immediately after creation using DockBuilderSplitNode(), make sure to call DockBuilderSetNodeSize() beforehand!
19644// For various reason, the splitting code currently needs a base size otherwise space may not be allocated as precisely as you would expect.
19645// - Use (id == 0) to let the system allocate a node identifier.
19646// - Existing node with a same id will be removed.
19647ImGuiID ImGui::DockBuilderAddNode(ImGuiID node_id, ImGuiDockNodeFlags flags)
19648{
19649 ImGuiContext& g = *GImGui; IM_UNUSED(g);
19650 IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderAddNode 0x%08X flags=%08X\n", node_id, flags);
19651
19652 if (node_id != 0)
19653 DockBuilderRemoveNode(node_id);
19654
19655 ImGuiDockNode* node = NULL;
19656 if (flags & ImGuiDockNodeFlags_DockSpace)
19657 {
19658 DockSpace(node_id, ImVec2(0, 0), (flags & ~ImGuiDockNodeFlags_DockSpace) | ImGuiDockNodeFlags_KeepAliveOnly);
19659 node = DockContextFindNodeByID(&g, node_id);
19660 }
19661 else
19662 {
19663 node = DockContextAddNode(&g, node_id);
19664 node->SetLocalFlags(flags);
19665 }
19666 node->LastFrameAlive = g.FrameCount; // Set this otherwise BeginDocked will undock during the same frame.
19667 return node->ID;
19668}
19669
19670void ImGui::DockBuilderRemoveNode(ImGuiID node_id)
19671{
19672 ImGuiContext& g = *GImGui; IM_UNUSED(g);
19673 IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderRemoveNode 0x%08X\n", node_id);
19674
19675 ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id);
19676 if (node == NULL)
19677 return;
19678 DockBuilderRemoveNodeDockedWindows(node_id, true);
19679 DockBuilderRemoveNodeChildNodes(node_id);
19680 // Node may have moved or deleted if e.g. any merge happened
19681 node = DockContextFindNodeByID(&g, node_id);
19682 if (node == NULL)
19683 return;
19684 if (node->IsCentralNode() && node->ParentNode)
19685 node->ParentNode->SetLocalFlags(node->ParentNode->LocalFlags | ImGuiDockNodeFlags_CentralNode);
19686 DockContextRemoveNode(&g, node, true);
19687}
19688
19689// root_id = 0 to remove all, root_id != 0 to remove child of given node.
19690void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id)
19691{
19692 ImGuiContext& g = *GImGui;
19693 ImGuiDockContext* dc = &g.DockContext;
19694
19695 ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(&g, root_id) : NULL;
19696 if (root_id && root_node == NULL)
19697 return;
19698 bool has_central_node = false;
19699
19700 ImGuiDataAuthority backup_root_node_authority_for_pos = root_node ? root_node->AuthorityForPos : ImGuiDataAuthority_Auto;
19701 ImGuiDataAuthority backup_root_node_authority_for_size = root_node ? root_node->AuthorityForSize : ImGuiDataAuthority_Auto;
19702
19703 // Process active windows
19704 ImVector<ImGuiDockNode*> nodes_to_remove;
19705 for (int n = 0; n < dc->Nodes.Data.Size; n++)
19706 if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
19707 {
19708 bool want_removal = (root_id == 0) || (node->ID != root_id && DockNodeGetRootNode(node)->ID == root_id);
19709 if (want_removal)
19710 {
19711 if (node->IsCentralNode())
19712 has_central_node = true;
19713 if (root_id != 0)
19714 DockContextQueueNotifyRemovedNode(&g, node);
19715 if (root_node)
19716 {
19717 DockNodeMoveWindows(root_node, node);
19718 DockSettingsRenameNodeReferences(node->ID, root_node->ID);
19719 }
19720 nodes_to_remove.push_back(node);
19721 }
19722 }
19723
19724 // DockNodeMoveWindows->DockNodeAddWindow will normally set those when reaching two windows (which is only adequate during interactive merge)
19725 // Make sure we don't lose our current pos/size. (FIXME-DOCK: Consider tidying up that code in DockNodeAddWindow instead)
19726 if (root_node)
19727 {
19728 root_node->AuthorityForPos = backup_root_node_authority_for_pos;
19729 root_node->AuthorityForSize = backup_root_node_authority_for_size;
19730 }
19731
19732 // Apply to settings
19733 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
19734 if (ImGuiID window_settings_dock_id = settings->DockId)
19735 for (int n = 0; n < nodes_to_remove.Size; n++)
19736 if (nodes_to_remove[n]->ID == window_settings_dock_id)
19737 {
19738 settings->DockId = root_id;
19739 break;
19740 }
19741
19742 // Not really efficient, but easier to destroy a whole hierarchy considering DockContextRemoveNode is attempting to merge nodes
19743 if (nodes_to_remove.Size > 1)
19744 ImQsort(nodes_to_remove.Data, nodes_to_remove.Size, sizeof(ImGuiDockNode*), DockNodeComparerDepthMostFirst);
19745 for (int n = 0; n < nodes_to_remove.Size; n++)
19746 DockContextRemoveNode(&g, nodes_to_remove[n], false);
19747
19748 if (root_id == 0)
19749 {
19750 dc->Nodes.Clear();
19751 dc->Requests.clear();
19752 }
19753 else if (has_central_node)
19754 {
19755 root_node->CentralNode = root_node;
19756 root_node->SetLocalFlags(root_node->LocalFlags | ImGuiDockNodeFlags_CentralNode);
19757 }
19758}
19759
19760void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_settings_refs)
19761{
19762 // Clear references in settings
19763 ImGuiContext& g = *GImGui;
19764 if (clear_settings_refs)
19765 {
19766 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
19767 {
19768 bool want_removal = (root_id == 0) || (settings->DockId == root_id);
19769 if (!want_removal && settings->DockId != 0)
19770 if (ImGuiDockNode* node = DockContextFindNodeByID(&g, settings->DockId))
19771 if (DockNodeGetRootNode(node)->ID == root_id)
19772 want_removal = true;
19773 if (want_removal)
19774 settings->DockId = 0;
19775 }
19776 }
19777
19778 // Clear references in windows
19779 for (int n = 0; n < g.Windows.Size; n++)
19780 {
19781 ImGuiWindow* window = g.Windows[n];
19782 bool want_removal = (root_id == 0) || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id);
19783 if (want_removal)
19784 {
19785 const ImGuiID backup_dock_id = window->DockId;
19786 IM_UNUSED(backup_dock_id);
19787 DockContextProcessUndockWindow(&g, window, clear_settings_refs);
19788 if (!clear_settings_refs)
19789 IM_ASSERT(window->DockId == backup_dock_id);
19790 }
19791 }
19792}
19793
19794// If 'out_id_at_dir' or 'out_id_at_opposite_dir' are non NULL, the function will write out the ID of the two new nodes created.
19795// Return value is ID of the node at the specified direction, so same as (*out_id_at_dir) if that pointer is set.
19796// FIXME-DOCK: We are not exposing nor using split_outer.
19797ImGuiID ImGui::DockBuilderSplitNode(ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir)
19798{
19799 ImGuiContext& g = *GImGui;
19800 IM_ASSERT(split_dir != ImGuiDir_None);
19801 IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderSplitNode: node 0x%08X, split_dir %d\n", id, split_dir);
19802
19803 ImGuiDockNode* node = DockContextFindNodeByID(&g, id);
19804 if (node == NULL)
19805 {
19806 IM_ASSERT(node != NULL);
19807 return 0;
19808 }
19809
19810 IM_ASSERT(!node->IsSplitNode()); // Assert if already Split
19811
19812 ImGuiDockRequest req;
19814 req.DockTargetWindow = NULL;
19815 req.DockTargetNode = node;
19816 req.DockPayload = NULL;
19817 req.DockSplitDir = split_dir;
19818 req.DockSplitRatio = ImSaturate((split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? size_ratio_for_node_at_dir : 1.0f - size_ratio_for_node_at_dir);
19819 req.DockSplitOuter = false;
19820 DockContextProcessDock(&g, &req);
19821
19822 ImGuiID id_at_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 0 : 1]->ID;
19823 ImGuiID id_at_opposite_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0]->ID;
19824 if (out_id_at_dir)
19825 *out_id_at_dir = id_at_dir;
19826 if (out_id_at_opposite_dir)
19827 *out_id_at_opposite_dir = id_at_opposite_dir;
19828 return id_at_dir;
19829}
19830
19831static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiDockNode* src_node, ImGuiID dst_node_id_if_known, ImVector<ImGuiID>* out_node_remap_pairs)
19832{
19833 ImGuiContext& g = *GImGui;
19834 ImGuiDockNode* dst_node = ImGui::DockContextAddNode(&g, dst_node_id_if_known);
19835 dst_node->SharedFlags = src_node->SharedFlags;
19836 dst_node->LocalFlags = src_node->LocalFlags;
19837 dst_node->LocalFlagsInWindows = ImGuiDockNodeFlags_None;
19838 dst_node->Pos = src_node->Pos;
19839 dst_node->Size = src_node->Size;
19840 dst_node->SizeRef = src_node->SizeRef;
19841 dst_node->SplitAxis = src_node->SplitAxis;
19842 dst_node->UpdateMergedFlags();
19843
19844 out_node_remap_pairs->push_back(src_node->ID);
19845 out_node_remap_pairs->push_back(dst_node->ID);
19846
19847 for (int child_n = 0; child_n < IM_ARRAYSIZE(src_node->ChildNodes); child_n++)
19848 if (src_node->ChildNodes[child_n])
19849 {
19850 dst_node->ChildNodes[child_n] = DockBuilderCopyNodeRec(src_node->ChildNodes[child_n], 0, out_node_remap_pairs);
19851 dst_node->ChildNodes[child_n]->ParentNode = dst_node;
19852 }
19853
19854 IMGUI_DEBUG_LOG_DOCKING("[docking] Fork node %08X -> %08X (%d childs)\n", src_node->ID, dst_node->ID, dst_node->IsSplitNode() ? 2 : 0);
19855 return dst_node;
19856}
19857
19858void ImGui::DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector<ImGuiID>* out_node_remap_pairs)
19859{
19860 ImGuiContext& g = *GImGui;
19861 IM_ASSERT(src_node_id != 0);
19862 IM_ASSERT(dst_node_id != 0);
19863 IM_ASSERT(out_node_remap_pairs != NULL);
19864
19865 DockBuilderRemoveNode(dst_node_id);
19866
19867 ImGuiDockNode* src_node = DockContextFindNodeByID(&g, src_node_id);
19868 IM_ASSERT(src_node != NULL);
19869
19870 out_node_remap_pairs->clear();
19871 DockBuilderCopyNodeRec(src_node, dst_node_id, out_node_remap_pairs);
19872
19873 IM_ASSERT((out_node_remap_pairs->Size % 2) == 0);
19874}
19875
19876void ImGui::DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name)
19877{
19878 ImGuiWindow* src_window = FindWindowByName(src_name);
19879 if (src_window == NULL)
19880 return;
19881 if (ImGuiWindow* dst_window = FindWindowByName(dst_name))
19882 {
19883 dst_window->Pos = src_window->Pos;
19884 dst_window->Size = src_window->Size;
19885 dst_window->SizeFull = src_window->SizeFull;
19886 dst_window->Collapsed = src_window->Collapsed;
19887 }
19888 else
19889 {
19890 ImGuiWindowSettings* dst_settings = FindWindowSettingsByID(ImHashStr(dst_name));
19891 if (!dst_settings)
19892 dst_settings = CreateNewWindowSettings(dst_name);
19893 ImVec2ih window_pos_2ih = ImVec2ih(src_window->Pos);
19894 if (src_window->ViewportId != 0 && src_window->ViewportId != IMGUI_VIEWPORT_DEFAULT_ID)
19895 {
19896 dst_settings->ViewportPos = window_pos_2ih;
19897 dst_settings->ViewportId = src_window->ViewportId;
19898 dst_settings->Pos = ImVec2ih(0, 0);
19899 }
19900 else
19901 {
19902 dst_settings->Pos = window_pos_2ih;
19903 }
19904 dst_settings->Size = ImVec2ih(src_window->SizeFull);
19905 dst_settings->Collapsed = src_window->Collapsed;
19906 }
19907}
19908
19909// FIXME: Will probably want to change this signature, in particular how the window remapping pairs are passed.
19910void ImGui::DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector<const char*>* in_window_remap_pairs)
19911{
19912 ImGuiContext& g = *GImGui;
19913 IM_ASSERT(src_dockspace_id != 0);
19914 IM_ASSERT(dst_dockspace_id != 0);
19915 IM_ASSERT(in_window_remap_pairs != NULL);
19916 IM_ASSERT((in_window_remap_pairs->Size % 2) == 0);
19917
19918 // Duplicate entire dock
19919 // FIXME: When overwriting dst_dockspace_id, windows that aren't part of our dockspace window class but that are docked in a same node will be split apart,
19920 // whereas we could attempt to at least keep them together in a new, same floating node.
19921 ImVector<ImGuiID> node_remap_pairs;
19922 DockBuilderCopyNode(src_dockspace_id, dst_dockspace_id, &node_remap_pairs);
19923
19924 // Attempt to transition all the upcoming windows associated to dst_dockspace_id into the newly created hierarchy of dock nodes
19925 // (The windows associated to src_dockspace_id are staying in place)
19926 ImVector<ImGuiID> src_windows;
19927 for (int remap_window_n = 0; remap_window_n < in_window_remap_pairs->Size; remap_window_n += 2)
19928 {
19929 const char* src_window_name = (*in_window_remap_pairs)[remap_window_n];
19930 const char* dst_window_name = (*in_window_remap_pairs)[remap_window_n + 1];
19931 ImGuiID src_window_id = ImHashStr(src_window_name);
19932 src_windows.push_back(src_window_id);
19933
19934 // Search in the remapping tables
19935 ImGuiID src_dock_id = 0;
19936 if (ImGuiWindow* src_window = FindWindowByID(src_window_id))
19937 src_dock_id = src_window->DockId;
19938 else if (ImGuiWindowSettings* src_window_settings = FindWindowSettingsByID(src_window_id))
19939 src_dock_id = src_window_settings->DockId;
19940 ImGuiID dst_dock_id = 0;
19941 for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2)
19942 if (node_remap_pairs[dock_remap_n] == src_dock_id)
19943 {
19944 dst_dock_id = node_remap_pairs[dock_remap_n + 1];
19945 //node_remap_pairs[dock_remap_n] = node_remap_pairs[dock_remap_n + 1] = 0; // Clear
19946 break;
19947 }
19948
19949 if (dst_dock_id != 0)
19950 {
19951 // Docked windows gets redocked into the new node hierarchy.
19952 IMGUI_DEBUG_LOG_DOCKING("[docking] Remap live window '%s' 0x%08X -> '%s' 0x%08X\n", src_window_name, src_dock_id, dst_window_name, dst_dock_id);
19953 DockBuilderDockWindow(dst_window_name, dst_dock_id);
19954 }
19955 else
19956 {
19957 // Floating windows gets their settings transferred (regardless of whether the new window already exist or not)
19958 // When this is leading to a Copy and not a Move, we would get two overlapping floating windows. Could we possibly dock them together?
19959 IMGUI_DEBUG_LOG_DOCKING("[docking] Remap window settings '%s' -> '%s'\n", src_window_name, dst_window_name);
19960 DockBuilderCopyWindowSettings(src_window_name, dst_window_name);
19961 }
19962 }
19963
19964 // Anything else in the source nodes of 'node_remap_pairs' are windows that are not included in the remapping list.
19965 // Find those windows and move to them to the cloned dock node. This may be optional?
19966 // Dock those are a second step as undocking would invalidate source dock nodes.
19967 struct DockRemainingWindowTask { ImGuiWindow* Window; ImGuiID DockId; DockRemainingWindowTask(ImGuiWindow* window, ImGuiID dock_id) { Window = window; DockId = dock_id; } };
19968 ImVector<DockRemainingWindowTask> dock_remaining_windows;
19969 for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2)
19970 if (ImGuiID src_dock_id = node_remap_pairs[dock_remap_n])
19971 {
19972 ImGuiID dst_dock_id = node_remap_pairs[dock_remap_n + 1];
19973 ImGuiDockNode* node = DockBuilderGetNode(src_dock_id);
19974 for (int window_n = 0; window_n < node->Windows.Size; window_n++)
19975 {
19976 ImGuiWindow* window = node->Windows[window_n];
19977 if (src_windows.contains(window->ID))
19978 continue;
19979
19980 // Docked windows gets redocked into the new node hierarchy.
19981 IMGUI_DEBUG_LOG_DOCKING("[docking] Remap window '%s' %08X -> %08X\n", window->Name, src_dock_id, dst_dock_id);
19982 dock_remaining_windows.push_back(DockRemainingWindowTask(window, dst_dock_id));
19983 }
19984 }
19985 for (const DockRemainingWindowTask& task : dock_remaining_windows)
19986 DockBuilderDockWindow(task.Window->Name, task.DockId);
19987}
19988
19989// FIXME-DOCK: This is awkward because in series of split user is likely to loose access to its root node.
19990void ImGui::DockBuilderFinish(ImGuiID root_id)
19991{
19992 ImGuiContext& g = *GImGui;
19993 //DockContextRebuild(&g);
19994 DockContextBuildAddWindowsToNodes(&g, root_id);
19995}
19996
19997//-----------------------------------------------------------------------------
19998// Docking: Begin/End Support Functions (called from Begin/End)
19999//-----------------------------------------------------------------------------
20000// - GetWindowAlwaysWantOwnTabBar()
20001// - DockContextBindNodeToWindow()
20002// - BeginDocked()
20003// - BeginDockableDragDropSource()
20004// - BeginDockableDragDropTarget()
20005//-----------------------------------------------------------------------------
20006
20007bool ImGui::GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window)
20008{
20009 ImGuiContext& g = *GImGui;
20010 if (g.IO.ConfigDockingAlwaysTabBar || window->WindowClass.DockingAlwaysTabBar)
20011 if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)) == 0)
20012 if (!window->IsFallbackWindow) // We don't support AlwaysTabBar on the fallback/implicit window to avoid unused dock-node overhead/noise
20013 return true;
20014 return false;
20015}
20016
20017static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window)
20018{
20019 ImGuiContext& g = *ctx;
20020 ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId);
20021 IM_ASSERT(window->DockNode == NULL);
20022
20023 // We should not be docking into a split node (SetWindowDock should avoid this)
20024 if (node && node->IsSplitNode())
20025 {
20026 DockContextProcessUndockWindow(ctx, window);
20027 return NULL;
20028 }
20029
20030 // Create node
20031 if (node == NULL)
20032 {
20033 node = DockContextAddNode(ctx, window->DockId);
20034 node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window;
20035 node->LastFrameAlive = g.FrameCount;
20036 }
20037
20038 // If the node just turned visible and is part of a hierarchy, it doesn't have a Size assigned by DockNodeTreeUpdatePosSize() yet,
20039 // so we're forcing a Pos/Size update from the first ancestor that is already visible (often it will be the root node).
20040 // If we don't do this, the window will be assigned a zero-size on its first frame, which won't ideally warm up the layout.
20041 // This is a little wonky because we don't normally update the Pos/Size of visible node mid-frame.
20042 if (!node->IsVisible)
20043 {
20044 ImGuiDockNode* ancestor_node = node;
20045 while (!ancestor_node->IsVisible && ancestor_node->ParentNode)
20046 ancestor_node = ancestor_node->ParentNode;
20047 IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f);
20048 DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(ancestor_node));
20049 DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, node);
20050 }
20051
20052 // Add window to node
20053 bool node_was_visible = node->IsVisible;
20054 DockNodeAddWindow(node, window, true);
20055 node->IsVisible = node_was_visible; // Don't mark visible right away (so DockContextEndFrame() doesn't render it, maybe other side effects? will see)
20056 IM_ASSERT(node == window->DockNode);
20057 return node;
20058}
20059
20060static void StoreDockStyleForWindow(ImGuiWindow* window)
20061{
20062 ImGuiContext& g = *GImGui;
20063 for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
20064 window->DockStyle.Colors[color_n] = ImGui::ColorConvertFloat4ToU32(g.Style.Colors[GWindowDockStyleColors[color_n]]);
20065}
20066
20067void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open)
20068{
20069 ImGuiContext& g = *GImGui;
20070
20071 // Clear fields ahead so most early-out paths don't have to do it
20072 window->DockIsActive = window->DockNodeIsVisible = window->DockTabIsVisible = false;
20073
20074 const bool auto_dock_node = GetWindowAlwaysWantOwnTabBar(window);
20075 if (auto_dock_node)
20076 {
20077 if (window->DockId == 0)
20078 {
20079 IM_ASSERT(window->DockNode == NULL);
20080 window->DockId = DockContextGenNodeID(&g);
20081 }
20082 }
20083 else
20084 {
20085 // Calling SetNextWindowPos() undock windows by default (by setting PosUndock)
20086 bool want_undock = false;
20087 want_undock |= (window->Flags & ImGuiWindowFlags_NoDocking) != 0;
20088 want_undock |= (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock;
20089 if (want_undock)
20090 {
20091 DockContextProcessUndockWindow(&g, window);
20092 return;
20093 }
20094 }
20095
20096 // Bind to our dock node
20097 ImGuiDockNode* node = window->DockNode;
20098 if (node != NULL)
20099 IM_ASSERT(window->DockId == node->ID);
20100 if (window->DockId != 0 && node == NULL)
20101 {
20102 node = DockContextBindNodeToWindow(&g, window);
20103 if (node == NULL)
20104 return;
20105 }
20106
20107#if 0
20108 // Undock if the ImGuiDockNodeFlags_NoDockingInCentralNode got set
20109 if (node->IsCentralNode && (node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode))
20110 {
20111 DockContextProcessUndockWindow(ctx, window);
20112 return;
20113 }
20114#endif
20115
20116 // Undock if our dockspace node disappeared
20117 // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly.
20118 if (node->LastFrameAlive < g.FrameCount)
20119 {
20120 // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextNewFrameUpdateDocking()
20121 ImGuiDockNode* root_node = DockNodeGetRootNode(node);
20122 if (root_node->LastFrameAlive < g.FrameCount)
20123 DockContextProcessUndockWindow(&g, window);
20124 else
20125 window->DockIsActive = true;
20126 return;
20127 }
20128
20129 // Store style overrides
20130 StoreDockStyleForWindow(window);
20131
20132 // Fast path return. It is common for windows to hold on a persistent DockId but be the only visible window,
20133 // and never create neither a host window neither a tab bar.
20134 // FIXME-DOCK: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test)
20135 if (node->HostWindow == NULL)
20136 {
20137 if (node->State == ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing)
20138 window->DockIsActive = true;
20139 if (node->Windows.Size > 1 && window->Appearing) // Only hide appearing window
20140 DockNodeHideWindowDuringHostWindowCreation(window);
20141 return;
20142 }
20143
20144 // We can have zero-sized nodes (e.g. children of a small-size dockspace)
20145 IM_ASSERT(node->HostWindow);
20146 IM_ASSERT(node->IsLeafNode());
20147 IM_ASSERT(node->Size.x >= 0.0f && node->Size.y >= 0.0f);
20148 node->State = ImGuiDockNodeState_HostWindowVisible;
20149
20150 // Undock if we are submitted earlier than the host window
20151 if (!(node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly) && window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext)
20152 {
20153 DockContextProcessUndockWindow(&g, window);
20154 return;
20155 }
20156
20157 // Position/Size window
20158 SetNextWindowPos(node->Pos);
20159 SetNextWindowSize(node->Size);
20160 g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos()
20161 window->DockIsActive = true;
20162 window->DockNodeIsVisible = true;
20163 window->DockTabIsVisible = false;
20164 if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly)
20165 return;
20166
20167 // When the window is selected we mark it as visible.
20168 if (node->VisibleWindow == window)
20169 window->DockTabIsVisible = true;
20170
20171 // Update window flag
20172 IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0);
20173 window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize;
20174 window->ChildFlags |= ImGuiChildFlags_AlwaysUseWindowPadding;
20175 if (node->IsHiddenTabBar() || node->IsNoTabBar())
20176 window->Flags |= ImGuiWindowFlags_NoTitleBar;
20177 else
20178 window->Flags &= ~ImGuiWindowFlags_NoTitleBar; // Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed!
20179
20180 // Save new dock order only if the window has been visible once already
20181 // This allows multiple windows to be created in the same frame and have their respective dock orders preserved.
20182 if (node->TabBar && window->WasActive)
20183 window->DockOrder = (short)DockNodeGetTabOrder(window);
20184
20185 if ((node->WantCloseAll || node->WantCloseTabId == window->TabId) && p_open != NULL)
20186 *p_open = false;
20187
20188 // Update ChildId to allow returning from Child to Parent with Escape
20189 ImGuiWindow* parent_window = window->DockNode->HostWindow;
20190 window->ChildId = parent_window->GetID(window->Name);
20191}
20192
20193void ImGui::BeginDockableDragDropSource(ImGuiWindow* window)
20194{
20195 ImGuiContext& g = *GImGui;
20196 IM_ASSERT(g.ActiveId == window->MoveId);
20197 IM_ASSERT(g.MovingWindow == window);
20198 IM_ASSERT(g.CurrentWindow == window);
20199
20200 // 0: Hold SHIFT to disable docking, 1: Hold SHIFT to enable docking.
20201 if (g.IO.ConfigDockingWithShift != g.IO.KeyShift)
20202 {
20203 // When ConfigDockingWithShift is set, display a tooltip to increase UI affordance.
20204 // We cannot set for HoveredWindowUnderMovingWindow != NULL here, as it is only valid/useful when drag and drop is already active
20205 // (because of the 'is_mouse_dragging_with_an_expected_destination' logic in UpdateViewportsNewFrame() function)
20206 IM_ASSERT(g.NextWindowData.Flags == 0);
20207 if (g.IO.ConfigDockingWithShift && g.MouseStationaryTimer >= 1.0f && g.ActiveId >= 1.0f)
20208 SetTooltip("%s", LocalizeGetMsg(ImGuiLocKey_DockingHoldShiftToDock));
20209 return;
20210 }
20211
20212 g.LastItemData.ID = window->MoveId;
20213 window = window->RootWindowDockTree;
20214 IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0);
20215 bool is_drag_docking = (g.IO.ConfigDockingWithShift) || ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); // FIXME-DOCKING: Need to make this stateful and explicit
20216 ImGuiDragDropFlags drag_drop_flags = ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_PayloadAutoExpire | ImGuiDragDropFlags_PayloadNoCrossContext | ImGuiDragDropFlags_PayloadNoCrossProcess;
20217 if (is_drag_docking && BeginDragDropSource(drag_drop_flags))
20218 {
20219 SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window));
20220 EndDragDropSource();
20221 StoreDockStyleForWindow(window); // Store style overrides while dragging (even when not docked) because docking preview may need it.
20222 }
20223}
20224
20225void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window)
20226{
20227 ImGuiContext& g = *GImGui;
20228
20229 //IM_ASSERT(window->RootWindowDockTree == window); // May also be a DockSpace
20230 IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0);
20231 if (!g.DragDropActive)
20232 return;
20233 //GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
20234 if (!BeginDragDropTargetCustom(window->Rect(), window->ID))
20235 return;
20236
20237 // Peek into the payload before calling AcceptDragDropPayload() so we can handle overlapping dock nodes with filtering
20238 // (this is a little unusual pattern, normally most code would call AcceptDragDropPayload directly)
20239 const ImGuiPayload* payload = &g.DragDropPayload;
20240 if (!payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) || !DockNodeIsDropAllowed(window, *(ImGuiWindow**)payload->Data))
20241 {
20242 EndDragDropTarget();
20243 return;
20244 }
20245
20246 ImGuiWindow* payload_window = *(ImGuiWindow**)payload->Data;
20247 if (AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect))
20248 {
20249 // Select target node
20250 // (Important: we cannot use g.HoveredDockNode here! Because each of our target node have filters based on payload, each candidate drop target will do its own evaluation)
20251 bool dock_into_floating_window = false;
20252 ImGuiDockNode* node = NULL;
20253 if (window->DockNodeAsHost)
20254 {
20255 // Cannot assume that node will != NULL even though we passed the rectangle test: it depends on padding/spacing handled by DockNodeTreeFindVisibleNodeByPos().
20256 node = DockNodeTreeFindVisibleNodeByPos(window->DockNodeAsHost, g.IO.MousePos);
20257
20258 // There is an edge case when docking into a dockspace which only has _inactive_ nodes (because none of the windows are active)
20259 // In this case we need to fallback into any leaf mode, possibly the central node.
20260 // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first.
20261 if (node && node->IsDockSpace() && node->IsRootNode())
20262 node = (node->CentralNode && node->IsLeafNode()) ? node->CentralNode : DockNodeTreeFindFallbackLeafNode(node);
20263 }
20264 else
20265 {
20266 if (window->DockNode)
20267 node = window->DockNode;
20268 else
20269 dock_into_floating_window = true; // Dock into a regular window
20270 }
20271
20272 const ImRect explicit_target_rect = (node && node->TabBar && !node->IsHiddenTabBar() && !node->IsNoTabBar()) ? node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight()));
20273 const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max);
20274
20275 // Preview docking request and find out split direction/ratio
20276 //const bool do_preview = true; // Ignore testing for payload->IsPreview() which removes one frame of delay, but breaks overlapping drop targets within the same window.
20277 const bool do_preview = payload->IsPreview() || payload->IsDelivery();
20278 if (do_preview && (node != NULL || dock_into_floating_window))
20279 {
20280 // If we have a non-leaf node it means we are hovering the border of a parent node, in which case only outer markers will appear.
20281 ImGuiDockPreviewData split_inner;
20282 ImGuiDockPreviewData split_outer;
20283 ImGuiDockPreviewData* split_data = &split_inner;
20284 if (node && (node->ParentNode || node->IsCentralNode() || !node->IsLeafNode()))
20285 if (ImGuiDockNode* root_node = DockNodeGetRootNode(node))
20286 {
20287 DockNodePreviewDockSetup(window, root_node, payload_window, NULL, &split_outer, is_explicit_target, true);
20288 if (split_outer.IsSplitDirExplicit)
20289 split_data = &split_outer;
20290 }
20291 if (!node || node->IsLeafNode())
20292 DockNodePreviewDockSetup(window, node, payload_window, NULL, &split_inner, is_explicit_target, false);
20293 if (split_data == &split_outer)
20294 split_inner.IsDropAllowed = false;
20295
20296 // Draw inner then outer, so that previewed tab (in inner data) will be behind the outer drop boxes
20297 DockNodePreviewDockRender(window, node, payload_window, &split_inner);
20298 DockNodePreviewDockRender(window, node, payload_window, &split_outer);
20299
20300 // Queue docking request
20301 if (split_data->IsDropAllowed && payload->IsDelivery())
20302 DockContextQueueDock(&g, window, split_data->SplitNode, payload_window, split_data->SplitDir, split_data->SplitRatio, split_data == &split_outer);
20303 }
20304 }
20305 EndDragDropTarget();
20306}
20307
20308//-----------------------------------------------------------------------------
20309// Docking: Settings
20310//-----------------------------------------------------------------------------
20311// - DockSettingsRenameNodeReferences()
20312// - DockSettingsRemoveNodeReferences()
20313// - DockSettingsFindNodeSettings()
20314// - DockSettingsHandler_ApplyAll()
20315// - DockSettingsHandler_ReadOpen()
20316// - DockSettingsHandler_ReadLine()
20317// - DockSettingsHandler_DockNodeToSettings()
20318// - DockSettingsHandler_WriteAll()
20319//-----------------------------------------------------------------------------
20320
20321static void ImGui::DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id)
20322{
20323 ImGuiContext& g = *GImGui;
20324 IMGUI_DEBUG_LOG_DOCKING("[docking] DockSettingsRenameNodeReferences: from 0x%08X -> to 0x%08X\n", old_node_id, new_node_id);
20325 for (int window_n = 0; window_n < g.Windows.Size; window_n++)
20326 {
20327 ImGuiWindow* window = g.Windows[window_n];
20328 if (window->DockId == old_node_id && window->DockNode == NULL)
20329 window->DockId = new_node_id;
20330 }
20332 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
20333 if (settings->DockId == old_node_id)
20334 settings->DockId = new_node_id;
20335}
20336
20337// Remove references stored in ImGuiWindowSettings to the given ImGuiDockNodeSettings
20338static void ImGui::DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count)
20339{
20340 ImGuiContext& g = *GImGui;
20341 int found = 0;
20343 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
20344 for (int node_n = 0; node_n < node_ids_count; node_n++)
20345 if (settings->DockId == node_ids[node_n])
20346 {
20347 settings->DockId = 0;
20348 settings->DockOrder = -1;
20349 if (++found < node_ids_count)
20350 break;
20351 return;
20352 }
20353}
20354
20355static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID id)
20356{
20357 // FIXME-OPT
20358 ImGuiDockContext* dc = &ctx->DockContext;
20359 for (int n = 0; n < dc->NodesSettings.Size; n++)
20360 if (dc->NodesSettings[n].ID == id)
20361 return &dc->NodesSettings[n];
20362 return NULL;
20363}
20364
20365// Clear settings data
20366static void ImGui::DockSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
20367{
20368 ImGuiDockContext* dc = &ctx->DockContext;
20369 dc->NodesSettings.clear();
20370 DockContextClearNodes(ctx, 0, true);
20371}
20372
20373// Recreate nodes based on settings data
20374static void ImGui::DockSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
20375{
20376 // Prune settings at boot time only
20377 ImGuiDockContext* dc = &ctx->DockContext;
20378 if (ctx->Windows.Size == 0)
20379 DockContextPruneUnusedSettingsNodes(ctx);
20380 DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size);
20381 DockContextBuildAddWindowsToNodes(ctx, 0);
20382}
20383
20384static void* ImGui::DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
20385{
20386 if (strcmp(name, "Data") != 0)
20387 return NULL;
20388 return (void*)1;
20389}
20390
20391static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler*, void*, const char* line)
20392{
20393 char c = 0;
20394 int x = 0, y = 0;
20395 int r = 0;
20396
20397 // Parsing, e.g.
20398 // " DockNode ID=0x00000001 Pos=383,193 Size=201,322 Split=Y,0.506 "
20399 // " DockNode ID=0x00000002 Parent=0x00000001 "
20400 // Important: this code expect currently fields in a fixed order.
20402 line = ImStrSkipBlank(line);
20403 if (strncmp(line, "DockNode", 8) == 0) { line = ImStrSkipBlank(line + strlen("DockNode")); }
20404 else if (strncmp(line, "DockSpace", 9) == 0) { line = ImStrSkipBlank(line + strlen("DockSpace")); node.Flags |= ImGuiDockNodeFlags_DockSpace; }
20405 else return;
20406 if (sscanf(line, "ID=0x%08X%n", &node.ID, &r) == 1) { line += r; } else return;
20407 if (sscanf(line, " Parent=0x%08X%n", &node.ParentNodeId, &r) == 1) { line += r; if (node.ParentNodeId == 0) return; }
20408 if (sscanf(line, " Window=0x%08X%n", &node.ParentWindowId, &r) ==1) { line += r; if (node.ParentWindowId == 0) return; }
20409 if (node.ParentNodeId == 0)
20410 {
20411 if (sscanf(line, " Pos=%i,%i%n", &x, &y, &r) == 2) { line += r; node.Pos = ImVec2ih((short)x, (short)y); } else return;
20412 if (sscanf(line, " Size=%i,%i%n", &x, &y, &r) == 2) { line += r; node.Size = ImVec2ih((short)x, (short)y); } else return;
20413 }
20414 else
20415 {
20416 if (sscanf(line, " SizeRef=%i,%i%n", &x, &y, &r) == 2) { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); }
20417 }
20418 if (sscanf(line, " Split=%c%n", &c, &r) == 1) { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; }
20419 if (sscanf(line, " NoResize=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoResize; }
20420 if (sscanf(line, " CentralNode=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_CentralNode; }
20421 if (sscanf(line, " NoTabBar=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoTabBar; }
20422 if (sscanf(line, " HiddenTabBar=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_HiddenTabBar; }
20423 if (sscanf(line, " NoWindowMenuButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoWindowMenuButton; }
20424 if (sscanf(line, " NoCloseButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoCloseButton; }
20425 if (sscanf(line, " Selected=0x%08X%n", &node.SelectedTabId,&r) == 1) { line += r; }
20426 if (node.ParentNodeId != 0)
20427 if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentNodeId))
20428 node.Depth = parent_settings->Depth + 1;
20429 ctx->DockContext.NodesSettings.push_back(node);
20430}
20431
20432static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDockNode* node, int depth)
20433{
20434 ImGuiDockNodeSettings node_settings;
20435 IM_ASSERT(depth < (1 << (sizeof(node_settings.Depth) << 3)));
20436 node_settings.ID = node->ID;
20437 node_settings.ParentNodeId = node->ParentNode ? node->ParentNode->ID : 0;
20438 node_settings.ParentWindowId = (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) ? node->HostWindow->ParentWindow->ID : 0;
20439 node_settings.SelectedTabId = node->SelectedTabId;
20440 node_settings.SplitAxis = (signed char)(node->IsSplitNode() ? node->SplitAxis : ImGuiAxis_None);
20441 node_settings.Depth = (char)depth;
20442 node_settings.Flags = (node->LocalFlags & ImGuiDockNodeFlags_SavedFlagsMask_);
20443 node_settings.Pos = ImVec2ih(node->Pos);
20444 node_settings.Size = ImVec2ih(node->Size);
20445 node_settings.SizeRef = ImVec2ih(node->SizeRef);
20446 dc->NodesSettings.push_back(node_settings);
20447 if (node->ChildNodes[0])
20448 DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[0], depth + 1);
20449 if (node->ChildNodes[1])
20450 DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[1], depth + 1);
20451}
20452
20453static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
20454{
20455 ImGuiContext& g = *ctx;
20456 ImGuiDockContext* dc = &ctx->DockContext;
20457 if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
20458 return;
20459
20460 // Gather settings data
20461 // (unlike our windows settings, because nodes are always built we can do a full rewrite of the SettingsNode buffer)
20462 dc->NodesSettings.resize(0);
20463 dc->NodesSettings.reserve(dc->Nodes.Data.Size);
20464 for (int n = 0; n < dc->Nodes.Data.Size; n++)
20465 if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
20466 if (node->IsRootNode())
20467 DockSettingsHandler_DockNodeToSettings(dc, node, 0);
20468
20469 int max_depth = 0;
20470 for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++)
20471 max_depth = ImMax((int)dc->NodesSettings[node_n].Depth, max_depth);
20472
20473 // Write to text buffer
20474 buf->appendf("[%s][Data]\n", handler->TypeName);
20475 for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++)
20476 {
20477 const int line_start_pos = buf->size(); (void)line_start_pos;
20478 const ImGuiDockNodeSettings* node_settings = &dc->NodesSettings[node_n];
20479 buf->appendf("%*s%s%*s", node_settings->Depth * 2, "", (node_settings->Flags & ImGuiDockNodeFlags_DockSpace) ? "DockSpace" : "DockNode ", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file
20480 buf->appendf(" ID=0x%08X", node_settings->ID);
20481 if (node_settings->ParentNodeId)
20482 {
20483 buf->appendf(" Parent=0x%08X SizeRef=%d,%d", node_settings->ParentNodeId, node_settings->SizeRef.x, node_settings->SizeRef.y);
20484 }
20485 else
20486 {
20487 if (node_settings->ParentWindowId)
20488 buf->appendf(" Window=0x%08X", node_settings->ParentWindowId);
20489 buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y);
20490 }
20491 if (node_settings->SplitAxis != ImGuiAxis_None)
20492 buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y');
20493 if (node_settings->Flags & ImGuiDockNodeFlags_NoResize)
20494 buf->appendf(" NoResize=1");
20495 if (node_settings->Flags & ImGuiDockNodeFlags_CentralNode)
20496 buf->appendf(" CentralNode=1");
20497 if (node_settings->Flags & ImGuiDockNodeFlags_NoTabBar)
20498 buf->appendf(" NoTabBar=1");
20499 if (node_settings->Flags & ImGuiDockNodeFlags_HiddenTabBar)
20500 buf->appendf(" HiddenTabBar=1");
20501 if (node_settings->Flags & ImGuiDockNodeFlags_NoWindowMenuButton)
20502 buf->appendf(" NoWindowMenuButton=1");
20503 if (node_settings->Flags & ImGuiDockNodeFlags_NoCloseButton)
20504 buf->appendf(" NoCloseButton=1");
20505 if (node_settings->SelectedTabId)
20506 buf->appendf(" Selected=0x%08X", node_settings->SelectedTabId);
20507
20508 // [DEBUG] Include comments in the .ini file to ease debugging (this makes saving slower!)
20509 if (g.IO.ConfigDebugIniSettings)
20510 if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID))
20511 {
20512 buf->appendf("%*s", ImMax(2, (line_start_pos + 92) - buf->size()), ""); // Align everything
20513 if (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow)
20514 buf->appendf(" ; in '%s'", node->HostWindow->ParentWindow->Name);
20515 // Iterate settings so we can give info about windows that didn't exist during the session.
20516 int contains_window = 0;
20517 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
20518 if (settings->DockId == node_settings->ID)
20519 {
20520 if (contains_window++ == 0)
20521 buf->appendf(" ; contains ");
20522 buf->appendf("'%s' ", settings->GetName());
20523 }
20524 }
20525
20526 buf->appendf("\n");
20527 }
20528 buf->appendf("\n");
20529}
20530
20531
20532//-----------------------------------------------------------------------------
20533// [SECTION] PLATFORM DEPENDENT HELPERS
20534//-----------------------------------------------------------------------------
20535// - Default clipboard handlers
20536// - Default shell function handlers
20537// - Default IME handlers
20538//-----------------------------------------------------------------------------
20539
20540#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
20541
20542#ifdef _MSC_VER
20543#pragma comment(lib, "user32")
20544#pragma comment(lib, "kernel32")
20545#endif
20546
20547// Win32 clipboard implementation
20548// We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()
20549static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)
20550{
20551 ImGuiContext& g = *ctx;
20552 g.ClipboardHandlerData.clear();
20553 if (!::OpenClipboard(NULL))
20554 return NULL;
20555 HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
20556 if (wbuf_handle == NULL)
20557 {
20558 ::CloseClipboard();
20559 return NULL;
20560 }
20561 if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
20562 {
20563 int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
20564 g.ClipboardHandlerData.resize(buf_len);
20565 ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
20566 }
20567 ::GlobalUnlock(wbuf_handle);
20568 ::CloseClipboard();
20569 return g.ClipboardHandlerData.Data;
20570}
20571
20572static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text)
20573{
20574 if (!::OpenClipboard(NULL))
20575 return;
20576 const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
20577 HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
20578 if (wbuf_handle == NULL)
20579 {
20580 ::CloseClipboard();
20581 return;
20582 }
20583 WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
20584 ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
20585 ::GlobalUnlock(wbuf_handle);
20586 ::EmptyClipboard();
20587 if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
20588 ::GlobalFree(wbuf_handle);
20589 ::CloseClipboard();
20590}
20591
20592#elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
20593
20594#include <Carbon/Carbon.h> // Use old API to avoid need for separate .mm file
20595static PasteboardRef main_clipboard = 0;
20596
20597// OSX clipboard implementation
20598// If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
20599static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text)
20600{
20601 if (!main_clipboard)
20602 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
20603 PasteboardClear(main_clipboard);
20604 CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
20605 if (cf_data)
20606 {
20607 PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
20608 CFRelease(cf_data);
20609 }
20610}
20611
20612static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)
20613{
20614 ImGuiContext& g = *ctx;
20615 if (!main_clipboard)
20616 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
20617 PasteboardSynchronize(main_clipboard);
20618
20619 ItemCount item_count = 0;
20620 PasteboardGetItemCount(main_clipboard, &item_count);
20621 for (ItemCount i = 0; i < item_count; i++)
20622 {
20623 PasteboardItemID item_id = 0;
20624 PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
20625 CFArrayRef flavor_type_array = 0;
20626 PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
20627 for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
20628 {
20629 CFDataRef cf_data;
20630 if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
20631 {
20632 g.ClipboardHandlerData.clear();
20633 int length = (int)CFDataGetLength(cf_data);
20634 g.ClipboardHandlerData.resize(length + 1);
20635 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);
20636 g.ClipboardHandlerData[length] = 0;
20637 CFRelease(cf_data);
20638 return g.ClipboardHandlerData.Data;
20639 }
20640 }
20641 }
20642 return NULL;
20643}
20644
20645#else
20646
20647// Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
20648static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)
20649{
20650 ImGuiContext& g = *ctx;
20651 return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();
20652}
20653
20654static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext* ctx, const char* text)
20655{
20656 ImGuiContext& g = *ctx;
20657 g.ClipboardHandlerData.clear();
20658 const char* text_end = text + strlen(text);
20659 g.ClipboardHandlerData.resize((int)(text_end - text) + 1);
20660 memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));
20661 g.ClipboardHandlerData[(int)(text_end - text)] = 0;
20662}
20663
20664#endif // Default clipboard handlers
20665
20666//-----------------------------------------------------------------------------
20667
20668#ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
20669#if defined(__APPLE__) && TARGET_OS_IPHONE
20670#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
20671#endif
20672
20673#if defined(_WIN32) && defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
20674#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
20675#endif
20676#endif
20677
20678#ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
20679#ifdef _WIN32
20680#include <shellapi.h> // ShellExecuteA()
20681#ifdef _MSC_VER
20682#pragma comment(lib, "shell32")
20683#endif
20684static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char* path)
20685{
20686 return (INT_PTR)::ShellExecuteA(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT) > 32;
20687}
20688#else
20689#include <sys/wait.h>
20690#include <unistd.h>
20691static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char* path)
20692{
20693#if defined(__APPLE__)
20694 const char* args[] { "open", "--", path, NULL };
20695#else
20696 const char* args[] { "xdg-open", path, NULL };
20697#endif
20698 pid_t pid = fork();
20699 if (pid < 0)
20700 return false;
20701 if (!pid)
20702 {
20703 execvp(args[0], const_cast<char **>(args));
20704 exit(-1);
20705 }
20706 else
20707 {
20708 int status;
20709 waitpid(pid, &status, 0);
20710 return WEXITSTATUS(status) == 0;
20711 }
20712}
20713#endif
20714#else
20715static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char*) { return false; }
20716#endif // Default shell handlers
20717
20718//-----------------------------------------------------------------------------
20719
20720// Win32 API IME support (for Asian languages, etc.)
20721#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
20722
20723#include <imm.h>
20724#ifdef _MSC_VER
20725#pragma comment(lib, "imm32")
20726#endif
20727
20728static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data)
20729{
20730 // Notify OS Input Method Editor of text input position
20731 HWND hwnd = (HWND)viewport->PlatformHandleRaw;
20732 if (hwnd == 0)
20733 return;
20734
20735 //::ImmAssociateContextEx(hwnd, NULL, data->WantVisible ? IACE_DEFAULT : 0);
20736 if (HIMC himc = ::ImmGetContext(hwnd))
20737 {
20738 COMPOSITIONFORM composition_form = {};
20739 composition_form.ptCurrentPos.x = (LONG)(data->InputPos.x - viewport->Pos.x);
20740 composition_form.ptCurrentPos.y = (LONG)(data->InputPos.y - viewport->Pos.y);
20741 composition_form.dwStyle = CFS_FORCE_POSITION;
20742 ::ImmSetCompositionWindow(himc, &composition_form);
20743 CANDIDATEFORM candidate_form = {};
20744 candidate_form.dwStyle = CFS_CANDIDATEPOS;
20745 candidate_form.ptCurrentPos.x = (LONG)(data->InputPos.x - viewport->Pos.x);
20746 candidate_form.ptCurrentPos.y = (LONG)(data->InputPos.y - viewport->Pos.y);
20747 ::ImmSetCandidateWindow(himc, &candidate_form);
20748 ::ImmReleaseContext(hwnd, himc);
20749 }
20750}
20751
20752#else
20753
20754static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport*, ImGuiPlatformImeData*) {}
20755
20756#endif // Default IME handlers
20757
20758//-----------------------------------------------------------------------------
20759// [SECTION] METRICS/DEBUGGER WINDOW
20760//-----------------------------------------------------------------------------
20761// - DebugRenderViewportThumbnail() [Internal]
20762// - RenderViewportsThumbnails() [Internal]
20763// - DebugTextEncoding()
20764// - MetricsHelpMarker() [Internal]
20765// - ShowFontAtlas() [Internal]
20766// - ShowMetricsWindow()
20767// - DebugNodeColumns() [Internal]
20768// - DebugNodeDockNode() [Internal]
20769// - DebugNodeDrawList() [Internal]
20770// - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]
20771// - DebugNodeFont() [Internal]
20772// - DebugNodeFontGlyph() [Internal]
20773// - DebugNodeStorage() [Internal]
20774// - DebugNodeTabBar() [Internal]
20775// - DebugNodeViewport() [Internal]
20776// - DebugNodeWindow() [Internal]
20777// - DebugNodeWindowSettings() [Internal]
20778// - DebugNodeWindowsList() [Internal]
20779// - DebugNodeWindowsListByBeginStackParent() [Internal]
20780//-----------------------------------------------------------------------------
20781
20782#ifndef IMGUI_DISABLE_DEBUG_TOOLS
20783
20784void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb)
20785{
20786 ImGuiContext& g = *GImGui;
20787 ImGuiWindow* window = g.CurrentWindow;
20788
20789 ImVec2 scale = bb.GetSize() / viewport->Size;
20790 ImVec2 off = bb.Min - viewport->Pos * scale;
20791 float alpha_mul = (viewport->Flags & ImGuiViewportFlags_IsMinimized) ? 0.30f : 1.00f;
20792 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));
20793 for (ImGuiWindow* thumb_window : g.Windows)
20794 {
20795 if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow))
20796 continue;
20797 if (thumb_window->Viewport != viewport)
20798 continue;
20799
20800 ImRect thumb_r = thumb_window->Rect();
20801 ImRect title_r = thumb_window->TitleBarRect();
20802 thumb_r = ImRect(ImTrunc(off + thumb_r.Min * scale), ImTrunc(off + thumb_r.Max * scale));
20803 title_r = ImRect(ImTrunc(off + title_r.Min * scale), ImTrunc(off + ImVec2(title_r.Max.x, title_r.Min.y + title_r.GetHeight() * 3.0f) * scale)); // Exaggerate title bar height
20804 thumb_r.ClipWithFull(bb);
20805 title_r.ClipWithFull(bb);
20806 const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
20807 window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul));
20808 window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul));
20809 window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
20810 window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name));
20811 }
20812 draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
20813 if (viewport->ID == g.DebugMetricsConfig.HighlightViewportID)
20814 window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
20815}
20816
20817static void RenderViewportsThumbnails()
20818{
20819 ImGuiContext& g = *GImGui;
20820 ImGuiWindow* window = g.CurrentWindow;
20821
20822 // Draw monitor and calculate their boundaries
20823 float SCALE = 1.0f / 8.0f;
20824 ImRect bb_full(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
20825 for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors)
20826 bb_full.Add(ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize));
20827 ImVec2 p = window->DC.CursorPos;
20828 ImVec2 off = p - bb_full.Min * SCALE;
20829 for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors)
20830 {
20831 ImRect monitor_draw_bb(off + (monitor.MainPos) * SCALE, off + (monitor.MainPos + monitor.MainSize) * SCALE);
20832 window->DrawList->AddRect(monitor_draw_bb.Min, monitor_draw_bb.Max, (g.DebugMetricsConfig.HighlightMonitorIdx == g.PlatformIO.Monitors.index_from_ptr(&monitor)) ? IM_COL32(255, 255, 0, 255) : ImGui::GetColorU32(ImGuiCol_Border), 4.0f);
20833 window->DrawList->AddRectFilled(monitor_draw_bb.Min, monitor_draw_bb.Max, ImGui::GetColorU32(ImGuiCol_Border, 0.10f), 4.0f);
20834 }
20835
20836 // Draw viewports
20837 for (ImGuiViewportP* viewport : g.Viewports)
20838 {
20839 ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE);
20840 ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb);
20841 }
20842 ImGui::Dummy(bb_full.GetSize() * SCALE);
20843}
20844
20845static int IMGUI_CDECL ViewportComparerByLastFocusedStampCount(const void* lhs, const void* rhs)
20846{
20847 const ImGuiViewportP* a = *(const ImGuiViewportP* const*)lhs;
20848 const ImGuiViewportP* b = *(const ImGuiViewportP* const*)rhs;
20849 return b->LastFocusedStampCount - a->LastFocusedStampCount;
20850}
20851
20852// Draw an arbitrary US keyboard layout to visualize translated keys
20853void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list)
20854{
20855 const float scale = ImGui::GetFontSize() / 13.0f;
20856 const ImVec2 key_size = ImVec2(35.0f, 35.0f) * scale;
20857 const float key_rounding = 3.0f * scale;
20858 const ImVec2 key_face_size = ImVec2(25.0f, 25.0f) * scale;
20859 const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f) * scale;
20860 const float key_face_rounding = 2.0f * scale;
20861 const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f) * scale;
20862 const ImVec2 key_step = ImVec2(key_size.x - 1.0f, key_size.y - 1.0f);
20863 const float key_row_offset = 9.0f * scale;
20864
20865 ImVec2 board_min = GetCursorScreenPos();
20866 ImVec2 board_max = ImVec2(board_min.x + 3 * key_step.x + 2 * key_row_offset + 10.0f, board_min.y + 3 * key_step.y + 10.0f);
20867 ImVec2 start_pos = ImVec2(board_min.x + 5.0f - key_step.x, board_min.y);
20868
20869 struct KeyLayoutData { int Row, Col; const char* Label; ImGuiKey Key; };
20870 const KeyLayoutData keys_to_display[] =
20871 {
20872 { 0, 0, "", ImGuiKey_Tab }, { 0, 1, "Q", ImGuiKey_Q }, { 0, 2, "W", ImGuiKey_W }, { 0, 3, "E", ImGuiKey_E }, { 0, 4, "R", ImGuiKey_R },
20873 { 1, 0, "", ImGuiKey_CapsLock }, { 1, 1, "A", ImGuiKey_A }, { 1, 2, "S", ImGuiKey_S }, { 1, 3, "D", ImGuiKey_D }, { 1, 4, "F", ImGuiKey_F },
20874 { 2, 0, "", ImGuiKey_LeftShift },{ 2, 1, "Z", ImGuiKey_Z }, { 2, 2, "X", ImGuiKey_X }, { 2, 3, "C", ImGuiKey_C }, { 2, 4, "V", ImGuiKey_V }
20875 };
20876
20877 // Elements rendered manually via ImDrawList API are not clipped automatically.
20878 // While not strictly necessary, here IsItemVisible() is used to avoid rendering these shapes when they are out of view.
20879 Dummy(board_max - board_min);
20880 if (!IsItemVisible())
20881 return;
20882 draw_list->PushClipRect(board_min, board_max, true);
20883 for (int n = 0; n < IM_ARRAYSIZE(keys_to_display); n++)
20884 {
20885 const KeyLayoutData* key_data = &keys_to_display[n];
20886 ImVec2 key_min = ImVec2(start_pos.x + key_data->Col * key_step.x + key_data->Row * key_row_offset, start_pos.y + key_data->Row * key_step.y);
20887 ImVec2 key_max = key_min + key_size;
20888 draw_list->AddRectFilled(key_min, key_max, IM_COL32(204, 204, 204, 255), key_rounding);
20889 draw_list->AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding);
20890 ImVec2 face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y);
20891 ImVec2 face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y);
20892 draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, ImDrawFlags_None, 2.0f);
20893 draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding);
20894 ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y);
20895 draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label);
20896 if (IsKeyDown(key_data->Key))
20897 draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding);
20898 }
20899 draw_list->PopClipRect();
20900}
20901
20902// Helper tool to diagnose between text encoding issues and font loading issues. Pass your UTF-8 string and verify that there are correct.
20903void ImGui::DebugTextEncoding(const char* str)
20904{
20905 Text("Text: \"%s\"", str);
20906 if (!BeginTable("##DebugTextEncoding", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable))
20907 return;
20908 TableSetupColumn("Offset");
20909 TableSetupColumn("UTF-8");
20910 TableSetupColumn("Glyph");
20911 TableSetupColumn("Codepoint");
20912 TableHeadersRow();
20913 for (const char* p = str; *p != 0; )
20914 {
20915 unsigned int c;
20916 const int c_utf8_len = ImTextCharFromUtf8(&c, p, NULL);
20917 TableNextColumn();
20918 Text("%d", (int)(p - str));
20919 TableNextColumn();
20920 for (int byte_index = 0; byte_index < c_utf8_len; byte_index++)
20921 {
20922 if (byte_index > 0)
20923 SameLine();
20924 Text("0x%02X", (int)(unsigned char)p[byte_index]);
20925 }
20926 TableNextColumn();
20927 if (GetFont()->FindGlyphNoFallback((ImWchar)c))
20928 TextUnformatted(p, p + c_utf8_len);
20929 else
20930 TextUnformatted((c == IM_UNICODE_CODEPOINT_INVALID) ? "[invalid]" : "[missing]");
20931 TableNextColumn();
20932 Text("U+%04X", (int)c);
20933 p += c_utf8_len;
20934 }
20935 EndTable();
20936}
20937
20938static void DebugFlashStyleColorStop()
20939{
20940 ImGuiContext& g = *GImGui;
20941 if (g.DebugFlashStyleColorIdx != ImGuiCol_COUNT)
20942 g.Style.Colors[g.DebugFlashStyleColorIdx] = g.DebugFlashStyleColorBackup;
20943 g.DebugFlashStyleColorIdx = ImGuiCol_COUNT;
20944}
20945
20946// Flash a given style color for some + inhibit modifications of this color via PushStyleColor() calls.
20947void ImGui::DebugFlashStyleColor(ImGuiCol idx)
20948{
20949 ImGuiContext& g = *GImGui;
20950 DebugFlashStyleColorStop();
20951 g.DebugFlashStyleColorTime = 0.5f;
20952 g.DebugFlashStyleColorIdx = idx;
20953 g.DebugFlashStyleColorBackup = g.Style.Colors[idx];
20954}
20955
20956void ImGui::UpdateDebugToolFlashStyleColor()
20957{
20958 ImGuiContext& g = *GImGui;
20959 if (g.DebugFlashStyleColorTime <= 0.0f)
20960 return;
20961 ColorConvertHSVtoRGB(ImCos(g.DebugFlashStyleColorTime * 6.0f) * 0.5f + 0.5f, 0.5f, 0.5f, g.Style.Colors[g.DebugFlashStyleColorIdx].x, g.Style.Colors[g.DebugFlashStyleColorIdx].y, g.Style.Colors[g.DebugFlashStyleColorIdx].z);
20962 g.Style.Colors[g.DebugFlashStyleColorIdx].w = 1.0f;
20963 if ((g.DebugFlashStyleColorTime -= g.IO.DeltaTime) <= 0.0f)
20964 DebugFlashStyleColorStop();
20965}
20966
20967// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
20968static void MetricsHelpMarker(const char* desc)
20969{
20970 ImGui::TextDisabled("(?)");
20971 if (ImGui::BeginItemTooltip())
20972 {
20973 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
20975 ImGui::PopTextWrapPos();
20976 ImGui::EndTooltip();
20977 }
20978}
20979
20980// [DEBUG] List fonts in a font atlas and display its texture
20981void ImGui::ShowFontAtlas(ImFontAtlas* atlas)
20982{
20983 for (ImFont* font : atlas->Fonts)
20984 {
20985 PushID(font);
20986 DebugNodeFont(font);
20987 PopID();
20988 }
20989 if (TreeNode("Font Atlas", "Font Atlas (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight))
20990 {
20991 ImGuiContext& g = *GImGui;
20992 ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
20993 Checkbox("Tint with Text Color", &cfg->ShowAtlasTintedWithTextColor); // Using text color ensure visibility of core atlas data, but will alter custom colored icons
20994 ImVec4 tint_col = cfg->ShowAtlasTintedWithTextColor ? GetStyleColorVec4(ImGuiCol_Text) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
20995 ImVec4 border_col = GetStyleColorVec4(ImGuiCol_Border);
20996 Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), tint_col, border_col);
20997 TreePop();
20998 }
20999}
21000
21001void ImGui::ShowMetricsWindow(bool* p_open)
21002{
21003 ImGuiContext& g = *GImGui;
21004 ImGuiIO& io = g.IO;
21005 ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
21006 if (cfg->ShowDebugLog)
21007 ShowDebugLogWindow(&cfg->ShowDebugLog);
21008 if (cfg->ShowIDStackTool)
21009 ShowIDStackToolWindow(&cfg->ShowIDStackTool);
21010
21011 if (!Begin("Dear ImGui Metrics/Debugger", p_open) || GetCurrentWindow()->BeginCount > 1)
21012 {
21013 End();
21014 return;
21015 }
21016
21017 // [DEBUG] Clear debug breaks hooks after exactly one cycle.
21018 DebugBreakClearData();
21019
21020 // Basic info
21021 Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
21022 if (g.ContextName[0] != 0)
21023 {
21024 SameLine();
21025 Text("(Context Name: \"%s\")", g.ContextName);
21026 }
21027 Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
21028 Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
21029 Text("%d visible windows, %d current allocations", io.MetricsRenderWindows, g.DebugAllocInfo.TotalAllocCount - g.DebugAllocInfo.TotalFreeCount);
21030 //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; }
21031
21032 Separator();
21033
21034 // Debugging enums
21035 enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
21036 const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" };
21037 enum { TRT_OuterRect, TRT_InnerRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsWorkRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentFrozen, TRT_ColumnsContentUnfrozen, TRT_Count }; // Tables Rect Type
21038 const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" };
21039 if (cfg->ShowWindowsRectsType < 0)
21040 cfg->ShowWindowsRectsType = WRT_WorkRect;
21041 if (cfg->ShowTablesRectsType < 0)
21042 cfg->ShowTablesRectsType = TRT_WorkRect;
21043
21044 struct Funcs
21045 {
21046 static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n)
21047 {
21048 ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); // Always using last submitted instance
21049 if (rect_type == TRT_OuterRect) { return table->OuterRect; }
21050 else if (rect_type == TRT_InnerRect) { return table->InnerRect; }
21051 else if (rect_type == TRT_WorkRect) { return table->WorkRect; }
21052 else if (rect_type == TRT_HostClipRect) { return table->HostClipRect; }
21053 else if (rect_type == TRT_InnerClipRect) { return table->InnerClipRect; }
21054 else if (rect_type == TRT_BackgroundClipRect) { return table->BgClipRect; }
21055 else if (rect_type == TRT_ColumnsRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table_instance->LastOuterHeight); }
21056 else if (rect_type == TRT_ColumnsWorkRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->WorkRect.Min.y, c->WorkMaxX, table->WorkRect.Max.y); }
21057 else if (rect_type == TRT_ColumnsClipRect) { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }
21058 else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); } // Note: y1/y2 not always accurate
21059 else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); }
21060 else if (rect_type == TRT_ColumnsContentFrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight); }
21061 else if (rect_type == TRT_ColumnsContentUnfrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); }
21062 IM_ASSERT(0);
21063 return ImRect();
21064 }
21065
21066 static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
21067 {
21068 if (rect_type == WRT_OuterRect) { return window->Rect(); }
21069 else if (rect_type == WRT_OuterRectClipped) { return window->OuterRectClipped; }
21070 else if (rect_type == WRT_InnerRect) { return window->InnerRect; }
21071 else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; }
21072 else if (rect_type == WRT_WorkRect) { return window->WorkRect; }
21073 else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
21074 else if (rect_type == WRT_ContentIdeal) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }
21075 else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; }
21076 IM_ASSERT(0);
21077 return ImRect();
21078 }
21079 };
21080
21081 // Tools
21082 if (TreeNode("Tools"))
21083 {
21084 // Debug Break features
21085 // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
21086 SeparatorTextEx(0, "Debug breaks", NULL, CalcTextSize("(?)").x + g.Style.SeparatorTextPadding.x);
21087 SameLine();
21088 MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.");
21089 if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive)
21090 DebugStartItemPicker();
21091 Checkbox("Show \"Debug Break\" buttons in other sections (io.ConfigDebugIsDebuggerPresent)", &g.IO.ConfigDebugIsDebuggerPresent);
21092
21093 SeparatorText("Visualize");
21094
21095 Checkbox("Show Debug Log", &cfg->ShowDebugLog);
21096 SameLine();
21097 MetricsHelpMarker("You can also call ImGui::ShowDebugLogWindow() from your code.");
21098
21099 Checkbox("Show ID Stack Tool", &cfg->ShowIDStackTool);
21100 SameLine();
21101 MetricsHelpMarker("You can also call ImGui::ShowIDStackToolWindow() from your code.");
21102
21103 Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder);
21104 Checkbox("Show windows rectangles", &cfg->ShowWindowsRects);
21105 SameLine();
21106 SetNextItemWidth(GetFontSize() * 12);
21107 cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count);
21108 if (cfg->ShowWindowsRects && g.NavWindow != NULL)
21109 {
21110 BulletText("'%s':", g.NavWindow->Name);
21111 Indent();
21112 for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
21113 {
21114 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
21115 Text("(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), wrt_rects_names[rect_n]);
21116 }
21117 Unindent();
21118 }
21119
21120 Checkbox("Show tables rectangles", &cfg->ShowTablesRects);
21121 SameLine();
21122 SetNextItemWidth(GetFontSize() * 12);
21123 cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count);
21124 if (cfg->ShowTablesRects && g.NavWindow != NULL)
21125 {
21126 for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
21127 {
21128 ImGuiTable* table = g.Tables.TryGetMapData(table_n);
21129 if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow))
21130 continue;
21131
21132 BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name);
21133 if (IsItemHovered())
21134 GetForegroundDrawList()->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
21135 Indent();
21136 char buf[128];
21137 for (int rect_n = 0; rect_n < TRT_Count; rect_n++)
21138 {
21139 if (rect_n >= TRT_ColumnsRect)
21140 {
21141 if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect)
21142 continue;
21143 for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
21144 {
21145 ImRect r = Funcs::GetTableRect(table, rect_n, column_n);
21146 ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]);
21147 Selectable(buf);
21148 if (IsItemHovered())
21149 GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
21150 }
21151 }
21152 else
21153 {
21154 ImRect r = Funcs::GetTableRect(table, rect_n, -1);
21155 ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]);
21156 Selectable(buf);
21157 if (IsItemHovered())
21158 GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
21159 }
21160 }
21161 Unindent();
21162 }
21163 }
21164 Checkbox("Show groups rectangles", &g.DebugShowGroupRects); // Storing in context as this is used by group code and prefers to be in hot-data
21165
21166 SeparatorText("Validate");
21167
21168 Checkbox("Debug Begin/BeginChild return value", &io.ConfigDebugBeginReturnValueLoop);
21169 SameLine();
21170 MetricsHelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running.");
21171
21172 Checkbox("UTF-8 Encoding viewer", &cfg->ShowTextEncodingViewer);
21173 SameLine();
21174 MetricsHelpMarker("You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct.");
21175 if (cfg->ShowTextEncodingViewer)
21176 {
21177 static char buf[64] = "";
21178 SetNextItemWidth(-FLT_MIN);
21179 InputText("##DebugTextEncodingBuf", buf, IM_ARRAYSIZE(buf));
21180 if (buf[0] != 0)
21181 DebugTextEncoding(buf);
21182 }
21183
21184 TreePop();
21185 }
21186
21187 // Windows
21188 if (TreeNode("Windows", "Windows (%d)", g.Windows.Size))
21189 {
21190 //SetNextItemOpen(true, ImGuiCond_Once);
21191 DebugNodeWindowsList(&g.Windows, "By display order");
21192 DebugNodeWindowsList(&g.WindowsFocusOrder, "By focus order (root windows)");
21193 if (TreeNode("By submission order (begin stack)"))
21194 {
21195 // Here we display windows in their submitted order/hierarchy, however note that the Begin stack doesn't constitute a Parent<>Child relationship!
21196 ImVector<ImGuiWindow*>& temp_buffer = g.WindowsTempSortBuffer;
21197 temp_buffer.resize(0);
21198 for (ImGuiWindow* window : g.Windows)
21199 if (window->LastFrameActive + 1 >= g.FrameCount)
21200 temp_buffer.push_back(window);
21201 struct Func { static int IMGUI_CDECL WindowComparerByBeginOrder(const void* lhs, const void* rhs) { return ((int)(*(const ImGuiWindow* const *)lhs)->BeginOrderWithinContext - (*(const ImGuiWindow* const*)rhs)->BeginOrderWithinContext); } };
21202 ImQsort(temp_buffer.Data, (size_t)temp_buffer.Size, sizeof(ImGuiWindow*), Func::WindowComparerByBeginOrder);
21203 DebugNodeWindowsListByBeginStackParent(temp_buffer.Data, temp_buffer.Size, NULL);
21204 TreePop();
21205 }
21206
21207 TreePop();
21208 }
21209
21210 // DrawLists
21211 int drawlist_count = 0;
21212 for (ImGuiViewportP* viewport : g.Viewports)
21213 drawlist_count += viewport->DrawDataP.CmdLists.Size;
21214 if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count))
21215 {
21216 Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
21217 Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
21218 for (ImGuiViewportP* viewport : g.Viewports)
21219 {
21220 bool viewport_has_drawlist = false;
21221 for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)
21222 {
21223 if (!viewport_has_drawlist)
21224 Text("Active DrawLists in Viewport #%d, ID: 0x%08X", viewport->Idx, viewport->ID);
21225 viewport_has_drawlist = true;
21226 DebugNodeDrawList(NULL, viewport, draw_list, "DrawList");
21227 }
21228 }
21229 TreePop();
21230 }
21231
21232 // Viewports
21233 if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size))
21234 {
21235 cfg->HighlightMonitorIdx = -1;
21236 bool open = TreeNode("Monitors", "Monitors (%d)", g.PlatformIO.Monitors.Size);
21237 SameLine();
21238 MetricsHelpMarker("Dear ImGui uses monitor data:\n- to query DPI settings on a per monitor basis\n- to position popup/tooltips so they don't straddle monitors.");
21239 if (open)
21240 {
21241 for (int i = 0; i < g.PlatformIO.Monitors.Size; i++)
21242 {
21243 DebugNodePlatformMonitor(&g.PlatformIO.Monitors[i], "Monitor", i);
21244 if (IsItemHovered())
21245 cfg->HighlightMonitorIdx = i;
21246 }
21247 DebugNodePlatformMonitor(&g.FallbackMonitor, "Fallback", 0);
21248 TreePop();
21249 }
21250
21251 SetNextItemOpen(true, ImGuiCond_Once);
21252 if (TreeNode("Windows Minimap"))
21253 {
21254 RenderViewportsThumbnails();
21255 TreePop();
21256 }
21257 cfg->HighlightViewportID = 0;
21258
21259 BulletText("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport ? g.MouseViewport->ID : 0, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0);
21260 if (TreeNode("Inferred Z order (front-to-back)"))
21261 {
21262 static ImVector<ImGuiViewportP*> viewports;
21263 viewports.resize(g.Viewports.Size);
21264 memcpy(viewports.Data, g.Viewports.Data, g.Viewports.size_in_bytes());
21265 if (viewports.Size > 1)
21266 ImQsort(viewports.Data, viewports.Size, sizeof(ImGuiViewport*), ViewportComparerByLastFocusedStampCount);
21267 for (ImGuiViewportP* viewport : viewports)
21268 {
21269 BulletText("Viewport #%d, ID: 0x%08X, LastFocused = %08d, PlatformFocused = %s, Window: \"%s\"",
21270 viewport->Idx, viewport->ID, viewport->LastFocusedStampCount,
21271 (g.PlatformIO.Platform_GetWindowFocus && viewport->PlatformWindowCreated) ? (g.PlatformIO.Platform_GetWindowFocus(viewport) ? "1" : "0") : "N/A",
21272 viewport->Window ? viewport->Window->Name : "N/A");
21273 if (IsItemHovered())
21274 cfg->HighlightViewportID = viewport->ID;
21275 }
21276 TreePop();
21277 }
21278
21279 for (ImGuiViewportP* viewport : g.Viewports)
21280 DebugNodeViewport(viewport);
21281 TreePop();
21282 }
21283
21284 // Details for Popups
21285 if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
21286 {
21287 for (const ImGuiPopupData& popup_data : g.OpenPopupStack)
21288 {
21289 // As it's difficult to interact with tree nodes while popups are open, we display everything inline.
21290 ImGuiWindow* window = popup_data.Window;
21291 BulletText("PopupID: %08x, Window: '%s' (%s%s), RestoreNavWindow '%s', ParentWindow '%s'",
21292 popup_data.PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? "Child;" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? "Menu;" : "",
21293 popup_data.RestoreNavWindow ? popup_data.RestoreNavWindow->Name : "NULL", window && window->ParentWindow ? window->ParentWindow->Name : "NULL");
21294 }
21295 TreePop();
21296 }
21297
21298 // Details for TabBars
21299 if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetAliveCount()))
21300 {
21301 for (int n = 0; n < g.TabBars.GetMapSize(); n++)
21302 if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n))
21303 {
21304 PushID(tab_bar);
21305 DebugNodeTabBar(tab_bar, "TabBar");
21306 PopID();
21307 }
21308 TreePop();
21309 }
21310
21311 // Details for Tables
21312 if (TreeNode("Tables", "Tables (%d)", g.Tables.GetAliveCount()))
21313 {
21314 for (int n = 0; n < g.Tables.GetMapSize(); n++)
21315 if (ImGuiTable* table = g.Tables.TryGetMapData(n))
21316 DebugNodeTable(table);
21317 TreePop();
21318 }
21319
21320 // Details for Fonts
21321 ImFontAtlas* atlas = g.IO.Fonts;
21322 if (TreeNode("Fonts", "Fonts (%d)", atlas->Fonts.Size))
21323 {
21324 ShowFontAtlas(atlas);
21325 TreePop();
21326 }
21327
21328 // Details for InputText
21329 if (TreeNode("InputText"))
21330 {
21331 DebugNodeInputTextState(&g.InputTextState);
21332 TreePop();
21333 }
21334
21335 // Details for TypingSelect
21336 if (TreeNode("TypingSelect", "TypingSelect (%d)", g.TypingSelectState.SearchBuffer[0] != 0 ? 1 : 0))
21337 {
21338 DebugNodeTypingSelectState(&g.TypingSelectState);
21339 TreePop();
21340 }
21341
21342 // Details for MultiSelect
21343 if (TreeNode("MultiSelect", "MultiSelect (%d)", g.MultiSelectStorage.GetAliveCount()))
21344 {
21345 ImGuiBoxSelectState* bs = &g.BoxSelectState;
21346 BulletText("BoxSelect ID=0x%08X, Starting = %d, Active %d", bs->ID, bs->IsStarting, bs->IsActive);
21347 for (int n = 0; n < g.MultiSelectStorage.GetMapSize(); n++)
21348 if (ImGuiMultiSelectState* state = g.MultiSelectStorage.TryGetMapData(n))
21349 DebugNodeMultiSelectState(state);
21350 TreePop();
21351 }
21352
21353 // Details for Docking
21354#ifdef IMGUI_HAS_DOCK
21355 if (TreeNode("Docking"))
21356 {
21357 static bool root_nodes_only = true;
21358 ImGuiDockContext* dc = &g.DockContext;
21359 Checkbox("List root nodes", &root_nodes_only);
21360 Checkbox("Ctrl shows window dock info", &cfg->ShowDockingNodes);
21361 if (SmallButton("Clear nodes")) { DockContextClearNodes(&g, 0, true); }
21362 SameLine();
21363 if (SmallButton("Rebuild all")) { dc->WantFullRebuild = true; }
21364 for (int n = 0; n < dc->Nodes.Data.Size; n++)
21365 if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
21366 if (!root_nodes_only || node->IsRootNode())
21367 DebugNodeDockNode(node, "Node");
21368 TreePop();
21369 }
21370#endif // #ifdef IMGUI_HAS_DOCK
21371
21372 // Settings
21373 if (TreeNode("Settings"))
21374 {
21375 if (SmallButton("Clear"))
21376 ClearIniSettings();
21377 SameLine();
21378 if (SmallButton("Save to memory"))
21379 SaveIniSettingsToMemory();
21380 SameLine();
21381 if (SmallButton("Save to disk"))
21382 SaveIniSettingsToDisk(g.IO.IniFilename);
21383 SameLine();
21384 if (g.IO.IniFilename)
21385 Text("\"%s\"", g.IO.IniFilename);
21386 else
21387 TextUnformatted("<NULL>");
21388 Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings);
21389 Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
21390 if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
21391 {
21392 for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
21393 BulletText("\"%s\"", handler.TypeName);
21394 TreePop();
21395 }
21396 if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
21397 {
21398 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
21399 DebugNodeWindowSettings(settings);
21400 TreePop();
21401 }
21402
21403 if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
21404 {
21405 for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
21406 DebugNodeTableSettings(settings);
21407 TreePop();
21408 }
21409
21410#ifdef IMGUI_HAS_DOCK
21411 if (TreeNode("SettingsDocking", "Settings packed data: Docking"))
21412 {
21413 ImGuiDockContext* dc = &g.DockContext;
21414 Text("In SettingsWindows:");
21415 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
21416 if (settings->DockId != 0)
21417 BulletText("Window '%s' -> DockId %08X DockOrder=%d", settings->GetName(), settings->DockId, settings->DockOrder);
21418 Text("In SettingsNodes:");
21419 for (int n = 0; n < dc->NodesSettings.Size; n++)
21420 {
21421 ImGuiDockNodeSettings* settings = &dc->NodesSettings[n];
21422 const char* selected_tab_name = NULL;
21423 if (settings->SelectedTabId)
21424 {
21425 if (ImGuiWindow* window = FindWindowByID(settings->SelectedTabId))
21426 selected_tab_name = window->Name;
21427 else if (ImGuiWindowSettings* window_settings = FindWindowSettingsByID(settings->SelectedTabId))
21428 selected_tab_name = window_settings->GetName();
21429 }
21430 BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeId, settings->SelectedTabId, selected_tab_name ? selected_tab_name : settings->SelectedTabId ? "N/A" : "");
21431 }
21432 TreePop();
21433 }
21434#endif // #ifdef IMGUI_HAS_DOCK
21435
21436 if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
21437 {
21438 InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);
21439 TreePop();
21440 }
21441 TreePop();
21442 }
21443
21444 // Settings
21445 if (TreeNode("Memory allocations"))
21446 {
21447 ImGuiDebugAllocInfo* info = &g.DebugAllocInfo;
21448 Text("%d current allocations", info->TotalAllocCount - info->TotalFreeCount);
21449 if (SmallButton("GC now")) { g.GcCompactAll = true; }
21450 Text("Recent frames with allocations:");
21451 int buf_size = IM_ARRAYSIZE(info->LastEntriesBuf);
21452 for (int n = buf_size - 1; n >= 0; n--)
21453 {
21454 ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[(info->LastEntriesIdx - n + buf_size) % buf_size];
21455 BulletText("Frame %06d: %+3d ( %2d alloc, %2d free )", entry->FrameCount, entry->AllocCount - entry->FreeCount, entry->AllocCount, entry->FreeCount);
21456 if (n == 0)
21457 {
21458 SameLine();
21459 Text("<- %d frames ago", g.FrameCount - entry->FrameCount);
21460 }
21461 }
21462 TreePop();
21463 }
21464
21465 if (TreeNode("Inputs"))
21466 {
21467 Text("KEYBOARD/GAMEPAD/MOUSE KEYS");
21468 {
21469 // We iterate both legacy native range and named ImGuiKey ranges, which is a little odd but this allows displaying the data for old/new backends.
21470 // User code should never have to go through such hoops! You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.
21471 Indent();
21472#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
21473 struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };
21474#else
21475 struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key >= 0 && key < 512 && GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array
21476 //Text("Legacy raw:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key++) { if (io.KeysDown[key]) { SameLine(); Text("\"%s\" %d", GetKeyName(key), key); } }
21477#endif
21478 Text("Keys down:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyDown(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); SameLine(); Text("(%.02f)", GetKeyData(key)->DownDuration); }
21479 Text("Keys pressed:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyPressed(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
21480 Text("Keys released:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyReleased(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
21481 Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
21482 Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; SameLine(); Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.
21483 DebugRenderKeyboardPreview(GetWindowDrawList());
21484 Unindent();
21485 }
21486
21487 Text("MOUSE STATE");
21488 {
21489 Indent();
21490 if (IsMousePosValid())
21491 Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y);
21492 else
21493 Text("Mouse pos: <INVALID>");
21494 Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y);
21495 int count = IM_ARRAYSIZE(io.MouseDown);
21496 Text("Mouse down:"); for (int i = 0; i < count; i++) if (IsMouseDown(i)) { SameLine(); Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); }
21497 Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text("b%d (%d)", i, io.MouseClickedCount[i]); }
21498 Text("Mouse released:"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text("b%d", i); }
21499 Text("Mouse wheel: %.1f", io.MouseWheel);
21500 Text("MouseStationaryTimer: %.2f", g.MouseStationaryTimer);
21501 Text("Mouse source: %s", GetMouseSourceName(io.MouseSource));
21502 Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused
21503 Unindent();
21504 }
21505
21506 Text("MOUSE WHEELING");
21507 {
21508 Indent();
21509 Text("WheelingWindow: '%s'", g.WheelingWindow ? g.WheelingWindow->Name : "NULL");
21510 Text("WheelingWindowReleaseTimer: %.2f", g.WheelingWindowReleaseTimer);
21511 Text("WheelingAxisAvg[] = { %.3f, %.3f }, Main Axis: %s", g.WheelingAxisAvg.x, g.WheelingAxisAvg.y, (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? "X" : (g.WheelingAxisAvg.x < g.WheelingAxisAvg.y) ? "Y" : "<none>");
21512 Unindent();
21513 }
21514
21515 Text("KEY OWNERS");
21516 {
21517 Indent();
21518 if (BeginChild("##owners", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))
21519 for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
21520 {
21521 ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
21522 if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)
21523 continue;
21524 Text("%s: 0x%08X%s", GetKeyName(key), owner_data->OwnerCurr,
21525 owner_data->LockUntilRelease ? " LockUntilRelease" : owner_data->LockThisFrame ? " LockThisFrame" : "");
21526 DebugLocateItemOnHover(owner_data->OwnerCurr);
21527 }
21528 EndChild();
21529 Unindent();
21530 }
21531 Text("SHORTCUT ROUTING");
21532 SameLine();
21533 MetricsHelpMarker("Declared shortcut routes automatically set key owner when mods matches.");
21534 {
21535 Indent();
21536 if (BeginChild("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))
21537 for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
21538 {
21539 ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
21540 for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; )
21541 {
21542 ImGuiKeyRoutingData* routing_data = &rt->Entries[idx];
21543 ImGuiKeyChord key_chord = key | routing_data->Mods;
21544 Text("%s: 0x%08X (scored %d)", GetKeyChordName(key_chord), routing_data->RoutingCurr, routing_data->RoutingCurrScore);
21545 DebugLocateItemOnHover(routing_data->RoutingCurr);
21546 if (g.IO.ConfigDebugIsDebuggerPresent)
21547 {
21548 SameLine();
21549 if (DebugBreakButton("**DebugBreak**", "in SetShortcutRouting() for this KeyChord"))
21550 g.DebugBreakInShortcutRouting = key_chord;
21551 }
21552 idx = routing_data->NextEntryIndex;
21553 }
21554 }
21555 EndChild();
21556 Text("(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
21557 Unindent();
21558 }
21559 TreePop();
21560 }
21561
21562 if (TreeNode("Internal state"))
21563 {
21564 Text("WINDOWING");
21565 Indent();
21566 Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
21567 Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindowDockTree->Name : "NULL");
21568 Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL");
21569 Text("HoveredDockNode: 0x%08X", g.DebugHoveredDockNode ? g.DebugHoveredDockNode->ID : 0);
21570 Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
21571 Text("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport->ID, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0);
21572 Unindent();
21573
21574 Text("ITEMS");
21575 Indent();
21576 Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, GetInputSourceName(g.ActiveIdSource));
21577 DebugLocateItemOnHover(g.ActiveId);
21578 Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
21579 Text("ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: %X", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
21580 Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame
21581 Text("HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverItemDelayId, g.HoverItemDelayTimer, g.HoverItemDelayClearTimer);
21582 Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
21583 DebugLocateItemOnHover(g.DragDropPayload.SourceId);
21584 Unindent();
21585
21586 Text("NAV,FOCUS");
21587 Indent();
21588 Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
21589 Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
21590 DebugLocateItemOnHover(g.NavId);
21591 Text("NavInputSource: %s", GetInputSourceName(g.NavInputSource));
21592 Text("NavLastValidSelectionUserData = %" IM_PRId64 " (0x%" IM_PRIX64 ")", g.NavLastValidSelectionUserData, g.NavLastValidSelectionUserData);
21593 Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
21594 Text("NavActivateId/DownId/PressedId: %08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId);
21595 Text("NavActivateFlags: %04X", g.NavActivateFlags);
21596 Text("NavCursorVisible: %d, NavHighlightItemUnderNav: %d", g.NavCursorVisible, g.NavHighlightItemUnderNav);
21597 Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
21598 Text("NavFocusRoute[] = ");
21599 for (int path_n = g.NavFocusRoute.Size - 1; path_n >= 0; path_n--)
21600 {
21601 const ImGuiFocusScopeData& focus_scope = g.NavFocusRoute[path_n];
21602 SameLine(0.0f, 0.0f);
21603 Text("0x%08X/", focus_scope.ID);
21604 SetItemTooltip("In window \"%s\"", FindWindowByID(focus_scope.WindowID)->Name);
21605 }
21606 Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
21607 Unindent();
21608
21609 TreePop();
21610 }
21611
21612 // Overlay: Display windows Rectangles and Begin Order
21613 if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder)
21614 {
21615 for (ImGuiWindow* window : g.Windows)
21616 {
21617 if (!window->WasActive)
21618 continue;
21619 ImDrawList* draw_list = GetForegroundDrawList(window);
21620 if (cfg->ShowWindowsRects)
21621 {
21622 ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType);
21623 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
21624 }
21625 if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow))
21626 {
21627 char buf[32];
21628 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
21629 float font_size = GetFontSize();
21630 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
21631 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
21632 }
21633 }
21634 }
21635
21636 // Overlay: Display Tables Rectangles
21637 if (cfg->ShowTablesRects)
21638 {
21639 for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
21640 {
21641 ImGuiTable* table = g.Tables.TryGetMapData(table_n);
21642 if (table == NULL || table->LastFrameActive < g.FrameCount - 1)
21643 continue;
21644 ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow);
21645 if (cfg->ShowTablesRectsType >= TRT_ColumnsRect)
21646 {
21647 for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
21648 {
21649 ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n);
21650 ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255);
21651 float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f;
21652 draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness);
21653 }
21654 }
21655 else
21656 {
21657 ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1);
21658 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
21659 }
21660 }
21661 }
21662
21663#ifdef IMGUI_HAS_DOCK
21664 // Overlay: Display Docking info
21665 if (cfg->ShowDockingNodes && g.IO.KeyCtrl && g.DebugHoveredDockNode)
21666 {
21667 char buf[64] = "";
21668 char* p = buf;
21669 ImGuiDockNode* node = g.DebugHoveredDockNode;
21670 ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport());
21671 p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode() ? " *CentralNode*" : "");
21672 p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "WindowClass: %08X\n", node->WindowClass.ClassId);
21673 p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y);
21674 p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.y);
21675 int depth = DockNodeGetDepth(node);
21676 overlay_draw_list->AddRect(node->Pos + ImVec2(3, 3) * (float)depth, node->Pos + node->Size - ImVec2(3, 3) * (float)depth, IM_COL32(200, 100, 100, 255));
21677 ImVec2 pos = node->Pos + ImVec2(3, 3) * (float)depth;
21678 overlay_draw_list->AddRectFilled(pos - ImVec2(1, 1), pos + CalcTextSize(buf) + ImVec2(1, 1), IM_COL32(200, 100, 100, 255));
21679 overlay_draw_list->AddText(NULL, 0.0f, pos, IM_COL32(255, 255, 255, 255), buf);
21680 }
21681#endif // #ifdef IMGUI_HAS_DOCK
21682
21683 End();
21684}
21685
21686void ImGui::DebugBreakClearData()
21687{
21688 // Those fields are scattered in their respective subsystem to stay in hot-data locations
21689 ImGuiContext& g = *GImGui;
21690 g.DebugBreakInWindow = 0;
21691 g.DebugBreakInTable = 0;
21692 g.DebugBreakInShortcutRouting = ImGuiKey_None;
21693}
21694
21695void ImGui::DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location)
21696{
21697 if (!BeginItemTooltip())
21698 return;
21699 Text("To call IM_DEBUG_BREAK() %s:", description_of_location);
21700 Separator();
21701 TextUnformatted(keyboard_only ? "- Press 'Pause/Break' on keyboard." : "- Press 'Pause/Break' on keyboard.\n- or Click (may alter focus/active id).\n- or navigate using keyboard and press space.");
21702 Separator();
21703 TextUnformatted("Choose one way that doesn't interfere with what you are trying to debug!\nYou need a debugger attached or this will crash!");
21704 EndTooltip();
21705}
21706
21707// Special button that doesn't take focus, doesn't take input owner, and can be activated without a click etc.
21708// In order to reduce interferences with the contents we are trying to debug into.
21709bool ImGui::DebugBreakButton(const char* label, const char* description_of_location)
21710{
21711 ImGuiWindow* window = GetCurrentWindow();
21712 if (window->SkipItems)
21713 return false;
21714
21715 ImGuiContext& g = *GImGui;
21716 const ImGuiID id = window->GetID(label);
21717 const ImVec2 label_size = CalcTextSize(label, NULL, true);
21718 ImVec2 pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrLineTextBaseOffset);
21719 ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x * 2.0f, label_size.y);
21720
21721 const ImRect bb(pos, pos + size);
21722 ItemSize(size, 0.0f);
21723 if (!ItemAdd(bb, id))
21724 return false;
21725
21726 // WE DO NOT USE ButtonEx() or ButtonBehavior() in order to reduce our side-effects.
21727 bool hovered = ItemHoverable(bb, id, g.CurrentItemFlags);
21728 bool pressed = hovered && (IsKeyChordPressed(g.DebugBreakKeyChord) || IsMouseClicked(0) || g.NavActivateId == id);
21729 DebugBreakButtonTooltip(false, description_of_location);
21730
21731 ImVec4 col4f = GetStyleColorVec4(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
21732 ImVec4 hsv;
21733 ColorConvertRGBtoHSV(col4f.x, col4f.y, col4f.z, hsv.x, hsv.y, hsv.z);
21734 ColorConvertHSVtoRGB(hsv.x + 0.20f, hsv.y, hsv.z, col4f.x, col4f.y, col4f.z);
21735
21736 RenderNavCursor(bb, id);
21737 RenderFrame(bb.Min, bb.Max, GetColorU32(col4f), true, g.Style.FrameRounding);
21738 RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, g.Style.ButtonTextAlign, &bb);
21739
21740 IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
21741 return pressed;
21742}
21743
21744// [DEBUG] Display contents of Columns
21745void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
21746{
21747 if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
21748 return;
21749 BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
21750 for (ImGuiOldColumnData& column : columns->Columns)
21751 BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", (int)columns->Columns.index_from_ptr(&column), column.OffsetNorm, GetColumnOffsetFromNorm(columns, column.OffsetNorm));
21752 TreePop();
21753}
21754
21755static void DebugNodeDockNodeFlags(ImGuiDockNodeFlags* p_flags, const char* label, bool enabled)
21756{
21757 using namespace ImGui;
21758 PushID(label);
21759 PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
21760 Text("%s:", label);
21761 if (!enabled)
21762 BeginDisabled();
21763 CheckboxFlags("NoResize", p_flags, ImGuiDockNodeFlags_NoResize);
21764 CheckboxFlags("NoResizeX", p_flags, ImGuiDockNodeFlags_NoResizeX);
21765 CheckboxFlags("NoResizeY",p_flags, ImGuiDockNodeFlags_NoResizeY);
21766 CheckboxFlags("NoTabBar", p_flags, ImGuiDockNodeFlags_NoTabBar);
21767 CheckboxFlags("HiddenTabBar", p_flags, ImGuiDockNodeFlags_HiddenTabBar);
21768 CheckboxFlags("NoWindowMenuButton", p_flags, ImGuiDockNodeFlags_NoWindowMenuButton);
21769 CheckboxFlags("NoCloseButton", p_flags, ImGuiDockNodeFlags_NoCloseButton);
21770 CheckboxFlags("DockedWindowsInFocusRoute", p_flags, ImGuiDockNodeFlags_DockedWindowsInFocusRoute);
21771 CheckboxFlags("NoDocking", p_flags, ImGuiDockNodeFlags_NoDocking); // Multiple flags
21772 CheckboxFlags("NoDockingSplit", p_flags, ImGuiDockNodeFlags_NoDockingSplit);
21773 CheckboxFlags("NoDockingSplitOther", p_flags, ImGuiDockNodeFlags_NoDockingSplitOther);
21774 CheckboxFlags("NoDockingOver", p_flags, ImGuiDockNodeFlags_NoDockingOverMe);
21775 CheckboxFlags("NoDockingOverOther", p_flags, ImGuiDockNodeFlags_NoDockingOverOther);
21776 CheckboxFlags("NoDockingOverEmpty", p_flags, ImGuiDockNodeFlags_NoDockingOverEmpty);
21777 CheckboxFlags("NoUndocking", p_flags, ImGuiDockNodeFlags_NoUndocking);
21778 if (!enabled)
21779 EndDisabled();
21780 PopStyleVar();
21781 PopID();
21782}
21783
21784// [DEBUG] Display contents of ImDockNode
21785void ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label)
21786{
21787 ImGuiContext& g = *GImGui;
21788 const bool is_alive = (g.FrameCount - node->LastFrameAlive < 2); // Submitted with ImGuiDockNodeFlags_KeepAliveOnly
21789 const bool is_active = (g.FrameCount - node->LastFrameActive < 2); // Submitted
21790 if (!is_alive) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
21791 bool open;
21792 ImGuiTreeNodeFlags tree_node_flags = node->IsFocused ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
21793 if (node->Windows.Size > 0)
21794 open = TreeNodeEx((void*)(intptr_t)node->ID, tree_node_flags, "%s 0x%04X%s: %d windows (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", node->Windows.Size, node->VisibleWindow ? node->VisibleWindow->Name : "NULL");
21795 else
21796 open = TreeNodeEx((void*)(intptr_t)node->ID, tree_node_flags, "%s 0x%04X%s: %s (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", (node->SplitAxis == ImGuiAxis_X) ? "horizontal split" : (node->SplitAxis == ImGuiAxis_Y) ? "vertical split" : "empty", node->VisibleWindow ? node->VisibleWindow->Name : "NULL");
21797 if (!is_alive) { PopStyleColor(); }
21798 if (is_active && IsItemHovered())
21799 if (ImGuiWindow* window = node->HostWindow ? node->HostWindow : node->VisibleWindow)
21800 GetForegroundDrawList(window)->AddRect(node->Pos, node->Pos + node->Size, IM_COL32(255, 255, 0, 255));
21801 if (open)
21802 {
21803 IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node);
21804 IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node);
21805 BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)",
21806 node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y);
21807 DebugNodeWindow(node->HostWindow, "HostWindow");
21808 DebugNodeWindow(node->VisibleWindow, "VisibleWindow");
21809 BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabId, node->LastFocusedNodeId);
21810 BulletText("Misc:%s%s%s%s%s%s%s",
21811 node->IsDockSpace() ? " IsDockSpace" : "",
21812 node->IsCentralNode() ? " IsCentralNode" : "",
21813 is_alive ? " IsAlive" : "", is_active ? " IsActive" : "", node->IsFocused ? " IsFocused" : "",
21814 node->WantLockSizeOnce ? " WantLockSizeOnce" : "",
21815 node->HasCentralNodeChild ? " HasCentralNodeChild" : "");
21816 if (TreeNode("flags", "Flags Merged: 0x%04X, Local: 0x%04X, InWindows: 0x%04X, Shared: 0x%04X", node->MergedFlags, node->LocalFlags, node->LocalFlagsInWindows, node->SharedFlags))
21817 {
21818 if (BeginTable("flags", 4))
21819 {
21820 TableNextColumn(); DebugNodeDockNodeFlags(&node->MergedFlags, "MergedFlags", false);
21821 TableNextColumn(); DebugNodeDockNodeFlags(&node->LocalFlags, "LocalFlags", true);
21822 TableNextColumn(); DebugNodeDockNodeFlags(&node->LocalFlagsInWindows, "LocalFlagsInWindows", false);
21823 TableNextColumn(); DebugNodeDockNodeFlags(&node->SharedFlags, "SharedFlags", true);
21824 EndTable();
21825 }
21826 TreePop();
21827 }
21828 if (node->ParentNode)
21829 DebugNodeDockNode(node->ParentNode, "ParentNode");
21830 if (node->ChildNodes[0])
21831 DebugNodeDockNode(node->ChildNodes[0], "Child[0]");
21832 if (node->ChildNodes[1])
21833 DebugNodeDockNode(node->ChildNodes[1], "Child[1]");
21834 if (node->TabBar)
21835 DebugNodeTabBar(node->TabBar, "TabBar");
21836 DebugNodeWindowsList(&node->Windows, "Windows");
21837
21838 TreePop();
21839 }
21840}
21841
21842static void FormatTextureIDForDebugDisplay(char* buf, int buf_size, ImTextureID tex_id)
21843{
21844 union { void* ptr; int integer; } tex_id_opaque;
21845 memcpy(&tex_id_opaque, &tex_id, ImMin(sizeof(void*), sizeof(tex_id)));
21846 if (sizeof(tex_id) >= sizeof(void*))
21847 ImFormatString(buf, buf_size, "0x%p", tex_id_opaque.ptr);
21848 else
21849 ImFormatString(buf, buf_size, "0x%04X", tex_id_opaque.integer);
21850}
21851
21852// [DEBUG] Display contents of ImDrawList
21853// Note that both 'window' and 'viewport' may be NULL here. Viewport is generally null of destroyed popups which previously owned a viewport.
21854void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label)
21855{
21856 ImGuiContext& g = *GImGui;
21857 ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
21858 int cmd_count = draw_list->CmdBuffer.Size;
21859 if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL)
21860 cmd_count--;
21861 bool node_open = TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, cmd_count);
21862 if (draw_list == GetWindowDrawList())
21863 {
21864 SameLine();
21865 TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
21866 if (node_open)
21867 TreePop();
21868 return;
21869 }
21870
21871 ImDrawList* fg_draw_list = viewport ? GetForegroundDrawList(viewport) : NULL; // Render additional visuals into the top-most draw list
21872 if (window && IsItemHovered() && fg_draw_list)
21873 fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
21874 if (!node_open)
21875 return;
21876
21877 if (window && !window->WasActive)
21878 TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
21879
21880 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++)
21881 {
21882 if (pcmd->UserCallback)
21883 {
21884 BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
21885 continue;
21886 }
21887
21888 char texid_desc[20];
21889 FormatTextureIDForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), pcmd->TextureId);
21890 char buf[300];
21891 ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
21892 pcmd->ElemCount / 3, texid_desc, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
21893 bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
21894 if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)
21895 DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);
21896 if (!pcmd_node_open)
21897 continue;
21898
21899 // Calculate approximate coverage area (touched pixel count)
21900 // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
21901 const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
21902 const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset;
21903 float total_area = 0.0f;
21904 for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; )
21905 {
21906 ImVec2 triangle[3];
21907 for (int n = 0; n < 3; n++, idx_n++)
21908 triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos;
21909 total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
21910 }
21911
21912 // Display vertex information summary. Hover to get all triangles drawn in wire-frame
21913 ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
21914 Selectable(buf);
21915 if (IsItemHovered() && fg_draw_list)
21916 DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false);
21917
21918 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
21919 ImGuiListClipper clipper;
21920 clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
21921 while (clipper.Step())
21922 for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
21923 {
21924 char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf);
21925 ImVec2 triangle[3];
21926 for (int n = 0; n < 3; n++, idx_i++)
21927 {
21928 const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
21929 triangle[n] = v.pos;
21930 buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
21931 (n == 0) ? "Vert:" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
21932 }
21933
21934 Selectable(buf, false);
21935 if (fg_draw_list && IsItemHovered())
21936 {
21937 ImDrawListFlags backup_flags = fg_draw_list->Flags;
21938 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
21939 fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f);
21940 fg_draw_list->Flags = backup_flags;
21941 }
21942 }
21943 TreePop();
21944 }
21945 TreePop();
21946}
21947
21948// [DEBUG] Display mesh/aabb of a ImDrawCmd
21949void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)
21950{
21951 IM_ASSERT(show_mesh || show_aabb);
21952
21953 // Draw wire-frame version of all triangles
21954 ImRect clip_rect = draw_cmd->ClipRect;
21955 ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
21956 ImDrawListFlags backup_flags = out_draw_list->Flags;
21957 out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
21958 for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; )
21959 {
21960 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; // We don't hold on those pointers past iterations as ->AddPolyline() may invalidate them if out_draw_list==draw_list
21961 ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;
21962
21963 ImVec2 triangle[3];
21964 for (int n = 0; n < 3; n++, idx_n++)
21965 vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));
21966 if (show_mesh)
21967 out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles
21968 }
21969 // Draw bounding boxes
21970 if (show_aabb)
21971 {
21972 out_draw_list->AddRect(ImTrunc(clip_rect.Min), ImTrunc(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU
21973 out_draw_list->AddRect(ImTrunc(vtxs_rect.Min), ImTrunc(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
21974 }
21975 out_draw_list->Flags = backup_flags;
21976}
21977
21978// [DEBUG] Display details for a single font, called by ShowStyleEditor().
21979void ImGui::DebugNodeFont(ImFont* font)
21980{
21981 bool opened = TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)",
21982 font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount);
21983 SameLine();
21984 if (SmallButton("Set as default"))
21985 GetIO().FontDefault = font;
21986 if (!opened)
21987 return;
21988
21989 // Display preview text
21990 PushFont(font);
21991 Text("The quick brown fox jumps over the lazy dog");
21992 PopFont();
21993
21994 // Display details
21995 SetNextItemWidth(GetFontSize() * 8);
21996 DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f");
21997 SameLine(); MetricsHelpMarker(
21998 "Note that the default embedded font is NOT meant to be scaled.\n\n"
21999 "Font are currently rendered into bitmaps at a given size at the time of building the atlas. "
22000 "You may oversample them to get some flexibility with scaling. "
22001 "You can also render at multiple sizes and select which one to use at runtime.\n\n"
22002 "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)");
22003 Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent);
22004 char c_str[5];
22005 Text("Fallback character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->FallbackChar), font->FallbackChar);
22006 Text("Ellipsis character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->EllipsisChar), font->EllipsisChar);
22007 const int surface_sqrt = (int)ImSqrt((float)font->MetricsTotalSurface);
22008 Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt);
22009 for (int config_i = 0; config_i < font->ConfigDataCount; config_i++)
22010 if (font->ConfigData)
22011 if (const ImFontConfig* cfg = &font->ConfigData[config_i])
22012 BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)",
22013 config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y);
22014
22015 // Display all glyphs of the fonts in separate pages of 256 characters
22016 if (TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size))
22017 {
22018 ImDrawList* draw_list = GetWindowDrawList();
22019 const ImU32 glyph_col = GetColorU32(ImGuiCol_Text);
22020 const float cell_size = font->FontSize * 1;
22021 const float cell_spacing = GetStyle().ItemSpacing.y;
22022 for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)
22023 {
22024 // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k)
22025 // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT
22026 // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here)
22027 if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095))
22028 {
22029 base += 4096 - 256;
22030 continue;
22031 }
22032
22033 int count = 0;
22034 for (unsigned int n = 0; n < 256; n++)
22035 if (font->FindGlyphNoFallback((ImWchar)(base + n)))
22036 count++;
22037 if (count <= 0)
22038 continue;
22039 if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph"))
22040 continue;
22041
22042 // Draw a 16x16 grid of glyphs
22043 ImVec2 base_pos = GetCursorScreenPos();
22044 for (unsigned int n = 0; n < 256; n++)
22045 {
22046 // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions
22047 // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string.
22048 ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing));
22049 ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size);
22050 const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n));
22051 draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50));
22052 if (!glyph)
22053 continue;
22054 font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n));
22055 if (IsMouseHoveringRect(cell_p1, cell_p2) && BeginTooltip())
22056 {
22057 DebugNodeFontGlyph(font, glyph);
22058 EndTooltip();
22059 }
22060 }
22061 Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16));
22062 TreePop();
22063 }
22064 TreePop();
22065 }
22066 TreePop();
22067}
22068
22069void ImGui::DebugNodeFontGlyph(ImFont*, const ImFontGlyph* glyph)
22070{
22071 Text("Codepoint: U+%04X", glyph->Codepoint);
22072 Separator();
22073 Text("Visible: %d", glyph->Visible);
22074 Text("AdvanceX: %.1f", glyph->AdvanceX);
22075 Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1);
22076 Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1);
22077}
22078
22079// [DEBUG] Display contents of ImGuiStorage
22080void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)
22081{
22082 if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
22083 return;
22084 for (const ImGuiStoragePair& p : storage->Data)
22085 {
22086 BulletText("Key 0x%08X Value { i: %d }", p.key, p.val_i); // Important: we currently don't store a type, real value may not be integer.
22087 DebugLocateItemOnHover(p.key);
22088 }
22089 TreePop();
22090}
22091
22092// [DEBUG] Display contents of ImGuiTabBar
22093void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
22094{
22095 // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
22096 char buf[256];
22097 char* p = buf;
22098 const char* buf_end = buf + IM_ARRAYSIZE(buf);
22099 const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2);
22100 p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s {", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*");
22101 for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++)
22102 {
22103 ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
22104 p += ImFormatString(p, buf_end - p, "%s'%s'", tab_n > 0 ? ", " : "", TabBarGetTabName(tab_bar, tab));
22105 }
22106 p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } ");
22107 if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
22108 bool open = TreeNode(label, "%s", buf);
22109 if (!is_active) { PopStyleColor(); }
22110 if (is_active && IsItemHovered())
22111 {
22112 ImDrawList* draw_list = GetForegroundDrawList();
22113 draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));
22114 draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
22115 draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
22116 }
22117 if (open)
22118 {
22119 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
22120 {
22121 ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
22122 PushID(tab);
22123 if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2);
22124 if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine();
22125 Text("%02d%c Tab 0x%08X '%s' Offset: %.2f, Width: %.2f/%.2f",
22126 tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, TabBarGetTabName(tab_bar, tab), tab->Offset, tab->Width, tab->ContentWidth);
22127 PopID();
22128 }
22129 TreePop();
22130 }
22131}
22132
22133void ImGui::DebugNodeViewport(ImGuiViewportP* viewport)
22134{
22135 ImGuiContext& g = *GImGui;
22136 SetNextItemOpen(true, ImGuiCond_Once);
22137 bool open = TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, Parent: 0x%08X, Window: \"%s\"", viewport->Idx, viewport->ID, viewport->ParentViewportId, viewport->Window ? viewport->Window->Name : "N/A");
22138 if (IsItemHovered())
22139 g.DebugMetricsConfig.HighlightViewportID = viewport->ID;
22140 if (open)
22141 {
22142 ImGuiWindowFlags flags = viewport->Flags;
22143 BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Inset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f\nMonitor: %d, DpiScale: %.0f%%",
22144 viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y,
22145 viewport->WorkInsetMin.x, viewport->WorkInsetMin.y, viewport->WorkInsetMax.x, viewport->WorkInsetMax.y,
22146 viewport->PlatformMonitor, viewport->DpiScale * 100.0f);
22147 if (viewport->Idx > 0) { SameLine(); if (SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200, 200); viewport->UpdateWorkRect(); if (viewport->Window) viewport->Window->Pos = viewport->Pos; } }
22148 BulletText("Flags: 0x%04X =%s%s%s%s%s%s%s%s%s%s%s%s%s", viewport->Flags,
22149 //(flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "", // Omitting because it is the standard
22150 (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "",
22151 (flags & ImGuiViewportFlags_IsMinimized) ? " IsMinimized" : "",
22152 (flags & ImGuiViewportFlags_IsFocused) ? " IsFocused" : "",
22153 (flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : "",
22154 (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "",
22155 (flags & ImGuiViewportFlags_NoTaskBarIcon) ? " NoTaskBarIcon" : "",
22156 (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "",
22157 (flags & ImGuiViewportFlags_NoFocusOnClick) ? " NoFocusOnClick" : "",
22158 (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "",
22159 (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : "",
22160 (flags & ImGuiViewportFlags_NoAutoMerge) ? " NoAutoMerge" : "",
22161 (flags & ImGuiViewportFlags_TopMost) ? " TopMost" : "",
22162 (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "");
22163 for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)
22164 DebugNodeDrawList(NULL, viewport, draw_list, "DrawList");
22165 TreePop();
22166 }
22167}
22168
22169void ImGui::DebugNodePlatformMonitor(ImGuiPlatformMonitor* monitor, const char* label, int idx)
22170{
22171 BulletText("%s %d: DPI %.0f%%\n MainMin (%.0f,%.0f), MainMax (%.0f,%.0f), MainSize (%.0f,%.0f)\n WorkMin (%.0f,%.0f), WorkMax (%.0f,%.0f), WorkSize (%.0f,%.0f)",
22172 label, idx, monitor->DpiScale * 100.0f,
22173 monitor->MainPos.x, monitor->MainPos.y, monitor->MainPos.x + monitor->MainSize.x, monitor->MainPos.y + monitor->MainSize.y, monitor->MainSize.x, monitor->MainSize.y,
22174 monitor->WorkPos.x, monitor->WorkPos.y, monitor->WorkPos.x + monitor->WorkSize.x, monitor->WorkPos.y + monitor->WorkSize.y, monitor->WorkSize.x, monitor->WorkSize.y);
22175}
22176
22177void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
22178{
22179 if (window == NULL)
22180 {
22181 BulletText("%s: NULL", label);
22182 return;
22183 }
22184
22185 ImGuiContext& g = *GImGui;
22186 const bool is_active = window->WasActive;
22187 ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
22188 if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
22189 const bool open = TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*");
22190 if (!is_active) { PopStyleColor(); }
22191 if (IsItemHovered() && is_active)
22192 GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
22193 if (!open)
22194 return;
22195
22196 if (window->MemoryCompacted)
22197 TextDisabled("Note: some memory buffers have been compacted/freed.");
22198
22199 if (g.IO.ConfigDebugIsDebuggerPresent && DebugBreakButton("**DebugBreak**", "in Begin()"))
22200 g.DebugBreakInWindow = window->ID;
22201
22202 ImGuiWindowFlags flags = window->Flags;
22203 DebugNodeDrawList(window, window->Viewport, window->DrawList, "DrawList");
22204 BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y);
22205 BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
22206 (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
22207 (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
22208 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
22209 if (flags & ImGuiWindowFlags_ChildWindow)
22210 BulletText("ChildFlags: 0x%08X (%s%s%s%s..)", window->ChildFlags,
22211 (window->ChildFlags & ImGuiChildFlags_Borders) ? "Borders " : "",
22212 (window->ChildFlags & ImGuiChildFlags_ResizeX) ? "ResizeX " : "",
22213 (window->ChildFlags & ImGuiChildFlags_ResizeY) ? "ResizeY " : "",
22214 (window->ChildFlags & ImGuiChildFlags_NavFlattened) ? "NavFlattened " : "");
22215 BulletText("WindowClassId: 0x%08X", window->WindowClass.ClassId);
22216 BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : "");
22217 BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
22218 BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
22219 for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
22220 {
22221 ImRect r = window->NavRectRel[layer];
22222 if (r.Min.x >= r.Max.x && r.Min.y >= r.Max.y)
22223 BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]);
22224 else
22225 BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y);
22226 DebugLocateItemOnHover(window->NavLastIds[layer]);
22227 }
22228 const ImVec2* pr = window->NavPreferredScoringPosRel;
22229 for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
22230 BulletText("NavPreferredScoringPosRel[%d] = {%.1f,%.1f)", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater.
22231 BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
22232
22233 BulletText("Viewport: %d%s, ViewportId: 0x%08X, ViewportPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportOwned ? " (Owned)" : "", window->ViewportId, window->ViewportPos.x, window->ViewportPos.y);
22234 BulletText("ViewportMonitor: %d", window->Viewport ? window->Viewport->PlatformMonitor : -1);
22235 BulletText("DockId: 0x%04X, DockOrder: %d, Act: %d, Vis: %d", window->DockId, window->DockOrder, window->DockIsActive, window->DockTabIsVisible);
22236 if (window->DockNode || window->DockNodeAsHost)
22237 DebugNodeDockNode(window->DockNodeAsHost ? window->DockNodeAsHost : window->DockNode, window->DockNodeAsHost ? "DockNodeAsHost" : "DockNode");
22238
22239 if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); }
22240 if (window->RootWindowDockTree != window->RootWindow) { DebugNodeWindow(window->RootWindowDockTree, "RootWindowDockTree"); }
22241 if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
22242 if (window->ParentWindowForFocusRoute != NULL) { DebugNodeWindow(window->ParentWindowForFocusRoute, "ParentWindowForFocusRoute"); }
22243 if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); }
22244 if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
22245 {
22246 for (ImGuiOldColumns& columns : window->ColumnsStorage)
22247 DebugNodeColumns(&columns);
22248 TreePop();
22249 }
22250 DebugNodeStorage(&window->StateStorage, "Storage");
22251 TreePop();
22252}
22253
22254void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)
22255{
22256 if (settings->WantDelete)
22257 BeginDisabled();
22258 Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
22259 settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
22260 if (settings->WantDelete)
22261 EndDisabled();
22262}
22263
22264void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label)
22265{
22266 if (!TreeNode(label, "%s (%d)", label, windows->Size))
22267 return;
22268 for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back
22269 {
22270 PushID((*windows)[i]);
22271 DebugNodeWindow((*windows)[i], "Window");
22272 PopID();
22273 }
22274 TreePop();
22275}
22276
22277// FIXME-OPT: This is technically suboptimal, but it is simpler this way.
22278void ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack)
22279{
22280 for (int i = 0; i < windows_size; i++)
22281 {
22282 ImGuiWindow* window = windows[i];
22283 if (window->ParentWindowInBeginStack != parent_in_begin_stack)
22284 continue;
22285 char buf[20];
22286 ImFormatString(buf, IM_ARRAYSIZE(buf), "[%04d] Window", window->BeginOrderWithinContext);
22287 //BulletText("[%04d] Window '%s'", window->BeginOrderWithinContext, window->Name);
22288 DebugNodeWindow(window, buf);
22289 Indent();
22290 DebugNodeWindowsListByBeginStackParent(windows + i + 1, windows_size - i - 1, window);
22291 Unindent();
22292 }
22293}
22294
22295//-----------------------------------------------------------------------------
22296// [SECTION] DEBUG LOG WINDOW
22297//-----------------------------------------------------------------------------
22298
22299void ImGui::DebugLog(const char* fmt, ...)
22300{
22301 va_list args;
22302 va_start(args, fmt);
22303 DebugLogV(fmt, args);
22304 va_end(args);
22305}
22306
22307void ImGui::DebugLogV(const char* fmt, va_list args)
22308{
22309 ImGuiContext& g = *GImGui;
22310 const int old_size = g.DebugLogBuf.size();
22311 if (g.ContextName[0] != 0)
22312 g.DebugLogBuf.appendf("[%s] [%05d] ", g.ContextName, g.FrameCount);
22313 else
22314 g.DebugLogBuf.appendf("[%05d] ", g.FrameCount);
22315 g.DebugLogBuf.appendfv(fmt, args);
22316 g.DebugLogIndex.append(g.DebugLogBuf.c_str(), old_size, g.DebugLogBuf.size());
22317 if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY)
22318 IMGUI_DEBUG_PRINTF("%s", g.DebugLogBuf.begin() + old_size);
22319#ifdef IMGUI_ENABLE_TEST_ENGINE
22320 // IMGUI_TEST_ENGINE_LOG() adds a trailing \n automatically
22321 const int new_size = g.DebugLogBuf.size();
22322 const bool trailing_carriage_return = (g.DebugLogBuf[new_size - 1] == '\n');
22323 if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTestEngine)
22324 IMGUI_TEST_ENGINE_LOG("%.*s", new_size - old_size - (trailing_carriage_return ? 1 : 0), g.DebugLogBuf.begin() + old_size);
22325#endif
22326}
22327
22328// FIXME-LAYOUT: To be done automatically via layout mode once we rework ItemSize/ItemAdd into ItemLayout.
22329static void SameLineOrWrap(const ImVec2& size)
22330{
22331 ImGuiContext& g = *GImGui;
22332 ImGuiWindow* window = g.CurrentWindow;
22333 ImVec2 pos(window->DC.CursorPosPrevLine.x + g.Style.ItemSpacing.x, window->DC.CursorPosPrevLine.y);
22334 if (window->WorkRect.Contains(ImRect(pos, pos + size)))
22335 ImGui::SameLine();
22336}
22337
22338static void ShowDebugLogFlag(const char* name, ImGuiDebugLogFlags flags)
22339{
22340 ImGuiContext& g = *GImGui;
22341 ImVec2 size(ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x + ImGui::CalcTextSize(name).x, ImGui::GetFrameHeight());
22342 SameLineOrWrap(size); // FIXME-LAYOUT: To be done automatically once we rework ItemSize/ItemAdd into ItemLayout.
22343
22344 bool highlight_errors = (flags == ImGuiDebugLogFlags_EventError && g.DebugLogSkippedErrors > 0);
22345 if (highlight_errors)
22346 ImGui::PushStyleColor(ImGuiCol_Text, ImLerp(g.Style.Colors[ImGuiCol_Text], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.30f));
22347 if (ImGui::CheckboxFlags(name, &g.DebugLogFlags, flags) && g.IO.KeyShift && (g.DebugLogFlags & flags) != 0)
22348 {
22349 g.DebugLogAutoDisableFrames = 2;
22350 g.DebugLogAutoDisableFlags |= flags;
22351 }
22352 if (highlight_errors)
22353 {
22354 ImGui::PopStyleColor();
22355 ImGui::SetItemTooltip("%d past errors skipped.", g.DebugLogSkippedErrors);
22356 }
22357 else
22358 {
22359 ImGui::SetItemTooltip("Hold SHIFT when clicking to enable for 2 frames only (useful for spammy log entries)");
22360 }
22361}
22362
22363void ImGui::ShowDebugLogWindow(bool* p_open)
22364{
22365 ImGuiContext& g = *GImGui;
22366 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) == 0)
22367 SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 12.0f), ImGuiCond_FirstUseEver);
22368 if (!Begin("Dear ImGui Debug Log", p_open) || GetCurrentWindow()->BeginCount > 1)
22369 {
22370 End();
22371 return;
22372 }
22373
22374 ImGuiDebugLogFlags all_enable_flags = ImGuiDebugLogFlags_EventMask_ & ~ImGuiDebugLogFlags_EventInputRouting;
22375 CheckboxFlags("All", &g.DebugLogFlags, all_enable_flags);
22376 SetItemTooltip("(except InputRouting which is spammy)");
22377
22378 ShowDebugLogFlag("Errors", ImGuiDebugLogFlags_EventError);
22379 ShowDebugLogFlag("ActiveId", ImGuiDebugLogFlags_EventActiveId);
22380 ShowDebugLogFlag("Clipper", ImGuiDebugLogFlags_EventClipper);
22381 ShowDebugLogFlag("Docking", ImGuiDebugLogFlags_EventDocking);
22382 ShowDebugLogFlag("Focus", ImGuiDebugLogFlags_EventFocus);
22383 ShowDebugLogFlag("IO", ImGuiDebugLogFlags_EventIO);
22384 ShowDebugLogFlag("Nav", ImGuiDebugLogFlags_EventNav);
22385 ShowDebugLogFlag("Popup", ImGuiDebugLogFlags_EventPopup);
22386 ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection);
22387 ShowDebugLogFlag("Viewport", ImGuiDebugLogFlags_EventViewport);
22388 ShowDebugLogFlag("InputRouting", ImGuiDebugLogFlags_EventInputRouting);
22389
22390 if (SmallButton("Clear"))
22391 {
22392 g.DebugLogBuf.clear();
22393 g.DebugLogIndex.clear();
22394 g.DebugLogSkippedErrors = 0;
22395 }
22396 SameLine();
22397 if (SmallButton("Copy"))
22398 SetClipboardText(g.DebugLogBuf.c_str());
22399 SameLine();
22400 if (SmallButton("Configure Outputs.."))
22401 OpenPopup("Outputs");
22402 if (BeginPopup("Outputs"))
22403 {
22404 CheckboxFlags("OutputToTTY", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTTY);
22405#ifndef IMGUI_ENABLE_TEST_ENGINE
22406 BeginDisabled();
22407#endif
22408 CheckboxFlags("OutputToTestEngine", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTestEngine);
22409#ifndef IMGUI_ENABLE_TEST_ENGINE
22410 EndDisabled();
22411#endif
22412 EndPopup();
22413 }
22414
22415 BeginChild("##log", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
22416
22417 const ImGuiDebugLogFlags backup_log_flags = g.DebugLogFlags;
22418 g.DebugLogFlags &= ~ImGuiDebugLogFlags_EventClipper;
22419
22420 ImGuiListClipper clipper;
22421 clipper.Begin(g.DebugLogIndex.size());
22422 while (clipper.Step())
22423 for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
22424 DebugTextUnformattedWithLocateItem(g.DebugLogIndex.get_line_begin(g.DebugLogBuf.c_str(), line_no), g.DebugLogIndex.get_line_end(g.DebugLogBuf.c_str(), line_no));
22425 g.DebugLogFlags = backup_log_flags;
22426 if (GetScrollY() >= GetScrollMaxY())
22427 SetScrollHereY(1.0f);
22428 EndChild();
22429
22430 End();
22431}
22432
22433// Display line, search for 0xXXXXXXXX identifiers and call DebugLocateItemOnHover() when hovered.
22434void ImGui::DebugTextUnformattedWithLocateItem(const char* line_begin, const char* line_end)
22435{
22436 TextUnformatted(line_begin, line_end);
22437 if (!IsItemHovered())
22438 return;
22439 ImGuiContext& g = *GImGui;
22440 ImRect text_rect = g.LastItemData.Rect;
22441 for (const char* p = line_begin; p <= line_end - 10; p++)
22442 {
22443 ImGuiID id = 0;
22444 if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, "%X", &id) != 1 || ImCharIsXdigitA(p[10]))
22445 continue;
22446 ImVec2 p0 = CalcTextSize(line_begin, p);
22447 ImVec2 p1 = CalcTextSize(p, p + 10);
22448 g.LastItemData.Rect = ImRect(text_rect.Min + ImVec2(p0.x, 0.0f), text_rect.Min + ImVec2(p0.x + p1.x, p1.y));
22449 if (IsMouseHoveringRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, true))
22450 DebugLocateItemOnHover(id);
22451 p += 10;
22452 }
22453}
22454
22455//-----------------------------------------------------------------------------
22456// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)
22457//-----------------------------------------------------------------------------
22458
22459// Draw a small cross at current CursorPos in current window's DrawList
22460void ImGui::DebugDrawCursorPos(ImU32 col)
22461{
22462 ImGuiContext& g = *GImGui;
22463 ImGuiWindow* window = g.CurrentWindow;
22464 ImVec2 pos = window->DC.CursorPos;
22465 window->DrawList->AddLine(ImVec2(pos.x, pos.y - 3.0f), ImVec2(pos.x, pos.y + 4.0f), col, 1.0f);
22466 window->DrawList->AddLine(ImVec2(pos.x - 3.0f, pos.y), ImVec2(pos.x + 4.0f, pos.y), col, 1.0f);
22467}
22468
22469// Draw a 10px wide rectangle around CurposPos.x using Line Y1/Y2 in current window's DrawList
22470void ImGui::DebugDrawLineExtents(ImU32 col)
22471{
22472 ImGuiContext& g = *GImGui;
22473 ImGuiWindow* window = g.CurrentWindow;
22474 float curr_x = window->DC.CursorPos.x;
22475 float line_y1 = (window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y);
22476 float line_y2 = line_y1 + (window->DC.IsSameLine ? window->DC.PrevLineSize.y : window->DC.CurrLineSize.y);
22477 window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y1), ImVec2(curr_x + 5.0f, line_y1), col, 1.0f);
22478 window->DrawList->AddLine(ImVec2(curr_x - 0.5f, line_y1), ImVec2(curr_x - 0.5f, line_y2), col, 1.0f);
22479 window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y2), ImVec2(curr_x + 5.0f, line_y2), col, 1.0f);
22480}
22481
22482// Draw last item rect in ForegroundDrawList (so it is always visible)
22483void ImGui::DebugDrawItemRect(ImU32 col)
22484{
22485 ImGuiContext& g = *GImGui;
22486 ImGuiWindow* window = g.CurrentWindow;
22487 GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col);
22488}
22489
22490// [DEBUG] Locate item position/rectangle given an ID.
22491static const ImU32 DEBUG_LOCATE_ITEM_COLOR = IM_COL32(0, 255, 0, 255); // Green
22492
22493void ImGui::DebugLocateItem(ImGuiID target_id)
22494{
22495 ImGuiContext& g = *GImGui;
22496 g.DebugLocateId = target_id;
22497 g.DebugLocateFrames = 2;
22498 g.DebugBreakInLocateId = false;
22499}
22500
22501// FIXME: Doesn't work over through a modal window, because they clear HoveredWindow.
22502void ImGui::DebugLocateItemOnHover(ImGuiID target_id)
22503{
22504 if (target_id == 0 || !IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenBlockedByPopup))
22505 return;
22506 ImGuiContext& g = *GImGui;
22507 DebugLocateItem(target_id);
22508 GetForegroundDrawList(g.CurrentWindow)->AddRect(g.LastItemData.Rect.Min - ImVec2(3.0f, 3.0f), g.LastItemData.Rect.Max + ImVec2(3.0f, 3.0f), DEBUG_LOCATE_ITEM_COLOR);
22509
22510 // Can't easily use a context menu here because it will mess with focus, active id etc.
22511 if (g.IO.ConfigDebugIsDebuggerPresent && g.MouseStationaryTimer > 1.0f)
22512 {
22513 DebugBreakButtonTooltip(false, "in ItemAdd()");
22514 if (IsKeyChordPressed(g.DebugBreakKeyChord))
22515 g.DebugBreakInLocateId = true;
22516 }
22517}
22518
22519void ImGui::DebugLocateItemResolveWithLastItem()
22520{
22521 ImGuiContext& g = *GImGui;
22522
22523 // [DEBUG] Debug break requested by user
22524 if (g.DebugBreakInLocateId)
22525 IM_DEBUG_BREAK();
22526
22527 ImGuiLastItemData item_data = g.LastItemData;
22528 g.DebugLocateId = 0;
22529 ImDrawList* draw_list = GetForegroundDrawList(g.CurrentWindow);
22530 ImRect r = item_data.Rect;
22531 r.Expand(3.0f);
22532 ImVec2 p1 = g.IO.MousePos;
22533 ImVec2 p2 = ImVec2((p1.x < r.Min.x) ? r.Min.x : (p1.x > r.Max.x) ? r.Max.x : p1.x, (p1.y < r.Min.y) ? r.Min.y : (p1.y > r.Max.y) ? r.Max.y : p1.y);
22534 draw_list->AddRect(r.Min, r.Max, DEBUG_LOCATE_ITEM_COLOR);
22535 draw_list->AddLine(p1, p2, DEBUG_LOCATE_ITEM_COLOR);
22536}
22537
22538void ImGui::DebugStartItemPicker()
22539{
22540 ImGuiContext& g = *GImGui;
22541 g.DebugItemPickerActive = true;
22542}
22543
22544// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
22545void ImGui::UpdateDebugToolItemPicker()
22546{
22547 ImGuiContext& g = *GImGui;
22548 g.DebugItemPickerBreakId = 0;
22549 if (!g.DebugItemPickerActive)
22550 return;
22551
22552 const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
22553 SetMouseCursor(ImGuiMouseCursor_Hand);
22554 if (IsKeyPressed(ImGuiKey_Escape))
22555 g.DebugItemPickerActive = false;
22556 const bool change_mapping = g.IO.KeyMods == (ImGuiMod_Ctrl | ImGuiMod_Shift);
22557 if (!change_mapping && IsMouseClicked(g.DebugItemPickerMouseButton) && hovered_id)
22558 {
22559 g.DebugItemPickerBreakId = hovered_id;
22560 g.DebugItemPickerActive = false;
22561 }
22562 for (int mouse_button = 0; mouse_button < 3; mouse_button++)
22563 if (change_mapping && IsMouseClicked(mouse_button))
22564 g.DebugItemPickerMouseButton = (ImU8)mouse_button;
22565 SetNextWindowBgAlpha(0.70f);
22566 if (!BeginTooltip())
22567 return;
22568 Text("HoveredId: 0x%08X", hovered_id);
22569 Text("Press ESC to abort picking.");
22570 const char* mouse_button_names[] = { "Left", "Right", "Middle" };
22571 if (change_mapping)
22572 Text("Remap w/ Ctrl+Shift: click anywhere to select new mouse button.");
22573 else
22574 TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click %s Button to break in debugger! (remap w/ Ctrl+Shift)", mouse_button_names[g.DebugItemPickerMouseButton]);
22575 EndTooltip();
22576}
22577
22578// [DEBUG] ID Stack Tool: update queries. Called by NewFrame()
22579void ImGui::UpdateDebugToolStackQueries()
22580{
22581 ImGuiContext& g = *GImGui;
22582 ImGuiIDStackTool* tool = &g.DebugIDStackTool;
22583
22584 // Clear hook when id stack tool is not visible
22585 g.DebugHookIdInfo = 0;
22586 if (g.FrameCount != tool->LastActiveFrame + 1)
22587 return;
22588
22589 // Update queries. The steps are: -1: query Stack, >= 0: query each stack item
22590 // We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time
22591 const ImGuiID query_id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId;
22592 if (tool->QueryId != query_id)
22593 {
22594 tool->QueryId = query_id;
22595 tool->StackLevel = -1;
22596 tool->Results.resize(0);
22597 }
22598 if (query_id == 0)
22599 return;
22600
22601 // Advance to next stack level when we got our result, or after 2 frames (in case we never get a result)
22602 int stack_level = tool->StackLevel;
22603 if (stack_level >= 0 && stack_level < tool->Results.Size)
22604 if (tool->Results[stack_level].QuerySuccess || tool->Results[stack_level].QueryFrameCount > 2)
22605 tool->StackLevel++;
22606
22607 // Update hook
22608 stack_level = tool->StackLevel;
22609 if (stack_level == -1)
22610 g.DebugHookIdInfo = query_id;
22611 if (stack_level >= 0 && stack_level < tool->Results.Size)
22612 {
22613 g.DebugHookIdInfo = tool->Results[stack_level].ID;
22614 tool->Results[stack_level].QueryFrameCount++;
22615 }
22616}
22617
22618// [DEBUG] ID Stack tool: hooks called by GetID() family functions
22619void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end)
22620{
22621 ImGuiContext& g = *GImGui;
22622 ImGuiWindow* window = g.CurrentWindow;
22623 ImGuiIDStackTool* tool = &g.DebugIDStackTool;
22624
22625 // Step 0: stack query
22626 // This assumes that the ID was computed with the current ID stack, which tends to be the case for our widget.
22627 if (tool->StackLevel == -1)
22628 {
22629 tool->StackLevel++;
22630 tool->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo());
22631 for (int n = 0; n < window->IDStack.Size + 1; n++)
22632 tool->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id;
22633 return;
22634 }
22635
22636 // Step 1+: query for individual level
22637 IM_ASSERT(tool->StackLevel >= 0);
22638 if (tool->StackLevel != window->IDStack.Size)
22639 return;
22640 ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel];
22641 IM_ASSERT(info->ID == id && info->QueryFrameCount > 0);
22642
22643 switch (data_type)
22644 {
22645 case ImGuiDataType_S32:
22646 ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%d", (int)(intptr_t)data_id);
22647 break;
22648 case ImGuiDataType_String:
22649 ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%.*s", data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)strlen((const char*)data_id), (const char*)data_id);
22650 break;
22651 case ImGuiDataType_Pointer:
22652 ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "(void*)0x%p", data_id);
22653 break;
22654 case ImGuiDataType_ID:
22655 if (info->Desc[0] != 0) // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one.
22656 return;
22657 ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "0x%08X [override]", id);
22658 break;
22659 default:
22660 IM_ASSERT(0);
22661 }
22662 info->QuerySuccess = true;
22663 info->DataType = data_type;
22664}
22665
22666static int StackToolFormatLevelInfo(ImGuiIDStackTool* tool, int n, bool format_for_ui, char* buf, size_t buf_size)
22667{
22668 ImGuiStackLevelInfo* info = &tool->Results[n];
22669 ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL;
22670 if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked)
22671 return ImFormatString(buf, buf_size, format_for_ui ? "\"%s\" [window]" : "%s", window->Name);
22672 if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id)
22673 return ImFormatString(buf, buf_size, (format_for_ui && info->DataType == ImGuiDataType_String) ? "\"%s\"" : "%s", info->Desc);
22674 if (tool->StackLevel < tool->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers.
22675 return (*buf = 0);
22676#ifdef IMGUI_ENABLE_TEST_ENGINE
22677 if (const char* label = ImGuiTestEngine_FindItemDebugLabel(GImGui, info->ID)) // Source: ImGuiTestEngine's ItemInfo()
22678 return ImFormatString(buf, buf_size, format_for_ui ? "??? \"%s\"" : "%s", label);
22679#endif
22680 return ImFormatString(buf, buf_size, "???");
22681}
22682
22683// ID Stack Tool: Display UI
22684void ImGui::ShowIDStackToolWindow(bool* p_open)
22685{
22686 ImGuiContext& g = *GImGui;
22687 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) == 0)
22688 SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 8.0f), ImGuiCond_FirstUseEver);
22689 if (!Begin("Dear ImGui ID Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1)
22690 {
22691 End();
22692 return;
22693 }
22694
22695 // Display hovered/active status
22696 ImGuiIDStackTool* tool = &g.DebugIDStackTool;
22697 const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
22698 const ImGuiID active_id = g.ActiveId;
22699#ifdef IMGUI_ENABLE_TEST_ENGINE
22700 Text("HoveredId: 0x%08X (\"%s\"), ActiveId: 0x%08X (\"%s\")", hovered_id, hovered_id ? ImGuiTestEngine_FindItemDebugLabel(&g, hovered_id) : "", active_id, active_id ? ImGuiTestEngine_FindItemDebugLabel(&g, active_id) : "");
22701#else
22702 Text("HoveredId: 0x%08X, ActiveId: 0x%08X", hovered_id, active_id);
22703#endif
22704 SameLine();
22705 MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details.");
22706
22707 // CTRL+C to copy path
22708 const float time_since_copy = (float)g.Time - tool->CopyToClipboardLastTime;
22709 Checkbox("Ctrl+C: copy path to clipboard", &tool->CopyToClipboardOnCtrlC);
22710 SameLine();
22711 TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.f, 1.f, 0.3f, 1.f) : ImVec4(), "*COPIED*");
22712 if (tool->CopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused))
22713 {
22714 tool->CopyToClipboardLastTime = (float)g.Time;
22715 char* p = g.TempBuffer.Data;
22716 char* p_end = p + g.TempBuffer.Size;
22717 for (int stack_n = 0; stack_n < tool->Results.Size && p + 3 < p_end; stack_n++)
22718 {
22719 *p++ = '/';
22720 char level_desc[256];
22721 StackToolFormatLevelInfo(tool, stack_n, false, level_desc, IM_ARRAYSIZE(level_desc));
22722 for (int n = 0; level_desc[n] && p + 2 < p_end; n++)
22723 {
22724 if (level_desc[n] == '/')
22725 *p++ = '\\';
22726 *p++ = level_desc[n];
22727 }
22728 }
22729 *p = '\0';
22730 SetClipboardText(g.TempBuffer.Data);
22731 }
22732
22733 // Display decorated stack
22734 tool->LastActiveFrame = g.FrameCount;
22735 if (tool->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders))
22736 {
22737 const float id_width = CalcTextSize("0xDDDDDDDD").x;
22738 TableSetupColumn("Seed", ImGuiTableColumnFlags_WidthFixed, id_width);
22739 TableSetupColumn("PushID", ImGuiTableColumnFlags_WidthStretch);
22740 TableSetupColumn("Result", ImGuiTableColumnFlags_WidthFixed, id_width);
22741 TableHeadersRow();
22742 for (int n = 0; n < tool->Results.Size; n++)
22743 {
22744 ImGuiStackLevelInfo* info = &tool->Results[n];
22745 TableNextColumn();
22746 Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0);
22747 TableNextColumn();
22748 StackToolFormatLevelInfo(tool, n, true, g.TempBuffer.Data, g.TempBuffer.Size);
22749 TextUnformatted(g.TempBuffer.Data);
22750 TableNextColumn();
22751 Text("0x%08X", info->ID);
22752 if (n == tool->Results.Size - 1)
22753 TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header));
22754 }
22755 EndTable();
22756 }
22757 End();
22758}
22759
22760#else
22761
22762void ImGui::ShowMetricsWindow(bool*) {}
22763void ImGui::ShowFontAtlas(ImFontAtlas*) {}
22764void ImGui::DebugNodeColumns(ImGuiOldColumns*) {}
22765void ImGui::DebugNodeDrawList(ImGuiWindow*, ImGuiViewportP*, const ImDrawList*, const char*) {}
22766void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
22767void ImGui::DebugNodeFont(ImFont*) {}
22768void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}
22769void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {}
22770void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {}
22771void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}
22772void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}
22773void ImGui::DebugNodeViewport(ImGuiViewportP*) {}
22774
22775void ImGui::ShowDebugLogWindow(bool*) {}
22776void ImGui::ShowIDStackToolWindow(bool*) {}
22777void ImGui::DebugStartItemPicker() {}
22778void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {}
22779
22780#endif // #ifndef IMGUI_DISABLE_DEBUG_TOOLS
22781
22782//-----------------------------------------------------------------------------
22783
22784// Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
22785// Prefer just including imgui_internal.h from your code rather than using this define. If a declaration is missing from imgui_internal.h add it or request it on the github.
22786#ifdef IMGUI_INCLUDE_IMGUI_USER_INL
22787#include "imgui_user.inl"
22788#endif
22789
22790//-----------------------------------------------------------------------------
22791
22792#endif // #ifndef IMGUI_DISABLE
uintptr_t id
int g
TclObject t
mat4 p4(vec4(1, 2, 3, 4), vec4(3, 4, 5, 6), vec4(5, 0, 7, 8), vec4(7, 8, 9, 0))
mat3 p3(vec3(1, 2, 3), vec3(4, 5, 6), vec3(7, 0, 9))
const char * ImStreolRange(const char *str, const char *str_end)
Definition imgui.cc:2066
char * ImStrdupcpy(char *dst, size_t *p_dst_size, const char *src)
Definition imgui.cc:2037
const char * ImStrbol(const char *buf_mid_line, const char *buf_begin)
Definition imgui.cc:2072
#define va_copy(dest, src)
Definition imgui.cc:2912
char * ImStrdup(const char *str)
Definition imgui.cc:2030
ImGuiContext * GImGui
Definition imgui.cc:1289
int ImFormatStringV(char *buf, size_t buf_size, const char *fmt, va_list args)
Definition imgui.cc:2166
ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2 &p1, const ImVec2 &p2, const ImVec2 &p3, const ImVec2 &p4, const ImVec2 &p, float tess_tol)
Definition imgui.cc:1944
ImGuiStoragePair * ImLowerBound(ImGuiStoragePair *in_begin, ImGuiStoragePair *in_end, ImGuiID key)
Definition imgui.cc:2671
void ImTriangleBarycentricCoords(const ImVec2 &a, const ImVec2 &b, const ImVec2 &c, const ImVec2 &p, float &out_u, float &out_v, float &out_w)
Definition imgui.cc:1975
void ImFormatStringToTempBuffer(const char **out_buf, const char **out_buf_end, const char *fmt,...)
Definition imgui.cc:2182
int ImTextStrToUtf8(char *out_buf, int out_buf_size, const ImWchar *in_text, const ImWchar *in_text_end)
Definition imgui.cc:2523
int ImStrnicmp(const char *str1, const char *str2, size_t count)
Definition imgui.cc:2014
int ImStrlenW(const ImWchar *str)
Definition imgui.cc:2057
const char * ImStrchrRange(const char *str, const char *str_end, char c)
Definition imgui.cc:2051
int ImFormatString(char *buf, size_t buf_size, const char *fmt,...)
Definition imgui.cc:2148
ImVec2 ImLineClosestPoint(const ImVec2 &a, const ImVec2 &b, const ImVec2 &p)
Definition imgui.cc:1954
void ImStrTrimBlanks(char *buf)
Definition imgui.cc:2102
void ImFormatStringToTempBufferV(const char **out_buf, const char **out_buf_end, const char *fmt, va_list args)
Definition imgui.cc:2194
IM_MSVC_RUNTIME_CHECKS_RESTORE IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
Definition imgui.cc:2585
const char * ImStrSkipBlank(const char *str)
Definition imgui.cc:2117
const char * ImTextCharToUtf8(char out_buf[5], unsigned int c)
Definition imgui.cc:2500
bool ImTriangleContainsPoint(const ImVec2 &a, const ImVec2 &b, const ImVec2 &c, const ImVec2 &p)
Definition imgui.cc:1967
ImU64 ImFileWrite(const void *data, ImU64 sz, ImU64 count, ImFileHandle f)
Definition imgui.cc:2330
#define IMGUI_DEBUG_NAV_SCORING
Definition imgui.cc:1152
int ImTextCountUtf8BytesFromStr(const ImWchar *in_text, const ImWchar *in_text_end)
Definition imgui.cc:2539
ImU64 ImFileRead(void *data, ImU64 sz, ImU64 count, ImFileHandle f)
Definition imgui.cc:2329
ImGuiID ImHashStr(const char *data_p, size_t data_size, ImGuiID seed)
Definition imgui.cc:2267
const char * ImTextFindPreviousUtf8Codepoint(const char *in_text_start, const char *in_text_curr)
Definition imgui.cc:2553
IM_MSVC_RUNTIME_CHECKS_OFF int ImTextCharFromUtf8(unsigned int *out_char, const char *in_text, const char *in_text_end)
Definition imgui.cc:2384
int ImTextCountCharsFromUtf8(const char *in_text, const char *in_text_end)
Definition imgui.cc:2452
ImGuiDockRequestType
Definition imgui.cc:16658
@ ImGuiDockRequestType_Split
Definition imgui.cc:16662
@ ImGuiDockRequestType_Dock
Definition imgui.cc:16660
@ ImGuiDockRequestType_None
Definition imgui.cc:16659
@ ImGuiDockRequestType_Undock
Definition imgui.cc:16661
bool ImFileClose(ImFileHandle f)
Definition imgui.cc:2327
void * ImFileLoadToMemory(const char *filename, const char *mode, size_t *out_file_size, int padding_bytes)
Definition imgui.cc:2336
const char * ImStristr(const char *haystack, const char *haystack_end, const char *needle, const char *needle_end)
Definition imgui.cc:2079
ImVec2 ImBezierCubicClosestPoint(const ImVec2 &p1, const ImVec2 &p2, const ImVec2 &p3, const ImVec2 &p4, const ImVec2 &p, int num_segments)
Definition imgui.cc:1886
ImGuiID ImHashData(const void *data_p, size_t data_size, ImGuiID seed)
Definition imgui.cc:2251
ImU64 ImFileGetSize(ImFileHandle f)
Definition imgui.cc:2328
int ImStricmp(const char *str1, const char *str2)
Definition imgui.cc:2007
int ImTextCountUtf8BytesFromChar(const char *in_text, const char *in_text_end)
Definition imgui.cc:2508
ImFileHandle ImFileOpen(const char *filename, const char *mode)
Definition imgui.cc:2302
IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT==IM_ARRAYSIZE(GKeyNames))
int ImTextStrFromUtf8(ImWchar *buf, int buf_size, const char *in_text, const char *in_text_end, const char **in_text_remaining)
Definition imgui.cc:2436
void ImStrncpy(char *dst, const char *src, size_t count)
Definition imgui.cc:2021
ImVec2 ImTriangleClosestPoint(const ImVec2 &a, const ImVec2 &b, const ImVec2 &c, const ImVec2 &p)
Definition imgui.cc:1986
int ImTextCountLines(const char *in_text, const char *in_text_end)
Definition imgui.cc:2564
#define IM_NEWLINE
#define IMGUI_CDECL
ImVec2 ImBezierCubicCalc(const ImVec2 &p1, const ImVec2 &p2, const ImVec2 &p3, const ImVec2 &p4, float t)
IMGUI_API void ShowFontAtlas(ImFontAtlas *atlas)
Definition imgui.cc:20981
auto CalcTextSize(std::string_view str)
Definition ImGuiUtils.hh:39
const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID
Definition imgui.cc:1248
void TextUnformatted(const std::string &str)
Definition ImGuiUtils.hh:26
constexpr double e
Definition Math.hh:21
T length(const vecN< N, T > &x)
Definition gl_vec.hh:505
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition gl_vec.hh:431
constexpr mat4 scale(const vec3 &xyz)
void Window(const char *name, bool *p_open, ImGuiWindowFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:63
void ID(const char *str_id, std::invocable<> auto next)
Definition ImGuiCpp.hh:244
void TabBar(const char *str_id, ImGuiTabBarFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:476
bool TreeNode(const char *label, ImGuiTreeNodeFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:302
void Combo(const char *label, const char *preview_value, ImGuiComboFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:289
void Font(ImFont *font, std::invocable<> auto next)
Definition ImGuiCpp.hh:131
void Indent(float indent_w, std::invocable<> auto next)
Definition ImGuiCpp.hh:224
bool Checkbox(const HotKey &hotKey, BooleanSetting &setting)
Definition ImGuiUtils.cc:58
bool SliderInt(IntegerSetting &setting, ImGuiSliderFlags flags)
Definition ImGuiUtils.cc:83
bool InputText(Setting &setting)
auto remove(ForwardRange &&range, const T &value)
Definition ranges.hh:291
auto count(InputRange &&range, const T &value)
Definition ranges.hh:349
size_t size(std::string_view utf8)
auto distance(octet_iterator first, octet_iterator last)
signed char SplitAxis
Definition imgui.cc:16710
ImGuiDockNodeFlags Flags
Definition imgui.cc:16712
ImGuiDockNode * CentralNode
Definition imgui.cc:17716
ImGuiDockNode * FirstNodeWithWindows
Definition imgui.cc:17717
ImGuiDockNode * SplitNode
Definition imgui.cc:16695
ImRect DropRectsDraw[ImGuiDir_COUNT+1]
Definition imgui.cc:16698
ImGuiDockNode FutureNode
Definition imgui.cc:16690
ImGuiDockNode * UndockTargetNode
Definition imgui.cc:16675
ImGuiWindow * DockPayload
Definition imgui.cc:16670
ImGuiDir DockSplitDir
Definition imgui.cc:16671
ImGuiDockNode * DockTargetNode
Definition imgui.cc:16669
ImGuiWindow * UndockTargetWindow
Definition imgui.cc:16674
ImGuiDockRequestType Type
Definition imgui.cc:16667
ImGuiWindow * DockTargetWindow
Definition imgui.cc:16668
float DockSplitRatio
Definition imgui.cc:16672
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.
Definition xrange.hh:147
constexpr auto begin(const zstring_view &x)